Implementation
How H2A is implemented today — and how it could be implemented tomorrow.
Principle
H2A defines the semantics of interactions (entities, invariants, protocol/observational layers). This document describes the current implementation. A different implementation would be H2A-conformant as long as it respects the semantics defined in h2a.md, friction.md, contribution.md, and exchange.md.
Current implementation
Stack
| Component | Choice | Role |
|---|---|---|
| Artifact format | Markdown + YAML frontmatter | Human-readable and audit-readable, no software dependency |
| Persistence | git | Immutable history, diff, blame — trace per session |
| Space structure | Filesystem directories | One directory = one space. Natural isolation |
| Shared space | shared/ at instance root |
Sole channel between personas |
| Instance marker | sofia.md file at root |
Identifies a SOFIA deployment and its version |
| AI provider | Claude Code | CLAUDE.md as persona instruction, hooks, project memory |
Traceability conventions
Commits (recommended):
{persona}: {short summary} ({date})
One commit per session.
Session summaries:
{space}/sessions/{YYYY-MM-DD}-{HHmm}-{persona}.md
At each closure, the persona creates a new file. The HHmm is the closure time (not the boot time). A long session with multiple closures produces multiple files.
Instance structure
Scaffolding is minimal — only elements necessary for the protocol are created at initialization. The internal organization of shared/ (subdirectories, artifact naming conventions, archiving) is an instance convention, not standard implementation.
instance/ ← scaffolding (create-instance)
├── sofia.md ← instance marker
├── shared/ ← shared space (exchange bus)
│ ├── conventions.md ← instance-specific conventions
│ └── orga/ ← team organization
│ ├── personas/ ← persona files
│ └── contextes/ ← contexts per persona-product
├── {space}/ ← one per persona
│ ├── CLAUDE.md ← persona instructions (provider)
│ └── sessions/ ← session summaries
└── ...
The protocol requires shared/ as the sole channel and artifacts with frontmatter. How the instance organizes its artifacts in shared/ (subdirectories, naming, archiving) is a local decision documented in conventions.md.
Repo ownership (MAY)
When an instance spans multiple repositories, conventions.md MAY declare a ## Repo ownership section mapping external repo paths to persona owners.
## Repo ownership
| Repo | Path | Owner |
|------|------|-------|
| product-repo | doc/architecture/ | @architect |
| product-repo | (rest) | @dev |
This is useful when personas from the same instance work in different parts of the same external repo. The workspace field in persona files covers the instance-internal space; repo ownership covers external repos.
Installing the protocol on a persona
The H2A protocol is installed via the context (shared/orga/contextes/contexte-{persona}-{product}.md), not via the persona file. The persona file defines the role (instance-agnostic). The context installs the instance rules.
Each context MUST contain an ## H2A Protocol section that:
- Points to
shared/conventions.md(read at first boot, reread before each artifact and closure) - Recalls the mandatory session summary sections (Produced, Decisions, Shared notes, Open)
- Recalls the observational sections (Orchestrator friction SHOULD, Flow MAY)
- Recalls the commit convention
- Contains the inline friction template — the exact expected format, directly visible in the context
Without this section, the persona will not trace friction — this is the most common prescription/usage gap.
Inline friction template (to include in each context):
Friction format: {symbol} [{marker}] {description} — [{initiative}] → {resolution}
Markers: ✓ [sound], ~ [contestable], ⚡ [simplification], ◐ [blind_spot], ✗ [refuted]
Initiative: [persona] or [PO]
Resolution (SHOULD): → ratified, → contested, → revised, → rejected
Lineage: if amending a prior friction, add (ref: {source-id}/{index})
The template is a safety net — the persona has the format in view when producing, without rereading an external file.
Frontmatter
Every artifact deposited in the shared space carries a YAML frontmatter. No accents in values.
Artifacts:
---
from: persona-emitter
to: persona-recipient
nature: signal # signal | question | request | response
status: new # new | read | done
date: YYYY-MM-DD
ref: artifact-identifier # SHOULD when nature = response
---
The ref field links a response to its source artifact. The identifier is the filename without extension (e.g., note-aurele-localisation-doc-site-livia). For cross-instance artifacts, the instance-source field indicates the originating instance.
Sessions:
---
persona: persona-name
date: YYYY-MM-DD
session: "HHmm"
---
Archiving
When an artifact moves to status: done, it is moved to archives/ in the parent directory.
Status lifecycle
| Status | Meaning |
|---|---|
new |
Deposited, not yet read by the recipient |
read |
Read by the recipient |
done |
The recipient has acted on it |
Retrocompat: the parser also accepts FR values (
nouveau,lu,traite).
Artifact resolution
When an artifact is processed, each point SHOULD carry a resolution tag in the document body (not in the frontmatter — an artifact often contains multiple points).
Convention: the recipient annotates each point with → ratified, → contested, → revised, or → rejected before archiving.
Example:
## Proposition A
→ ratified
## Proposition B
→ rejected (short justification)
## Proposition C
→ revised (detail on what changes)
Retrocompat: the parser also accepts FR tags (
ratifie,conteste,revise,rejete).
Friction in session summaries
Section ## Orchestrator friction (SHOULD — see exchange.md §Sessions, observational layer).
Each line carries the dimensions defined in friction.md, rendered as:
## Orchestrator friction
- [marker] description — [initiative] → resolution
Markers: rendered as bracketed keywords + visual symbol in instance conventions.
Protocol (friction.md) |
Markdown rendering |
|---|---|
[sound] |
✓ or [sound] |
[contestable] |
~ or [contestable] |
[simplification] |
⚡ or [simplification] |
[blind_spot] |
◐ or [blind_spot] |
[refuted] |
✗ or [refuted] |
Retrocompat: the parser also accepts FR brackets (
[juste],[angle-mort],[faux]).
Visual symbols (✓/~/⚡/◐/✗) are an instance convenience. Bracketed keywords are authoritative for audit.
Initiative: [persona] or [PO] — who initiated the friction subject.
Resolution: epistemic gesture tag after the arrow →. See protocol/friction.md §Resolution.
Protocol (friction.md) |
Markdown rendering |
|---|---|
ratified |
→ ratified |
contested |
→ contested |
revised |
→ revised |
rejected |
→ rejected |
The resolution tag is set per friction point, not per section.
Example:
## Orchestrator friction
- ✓ [sound] the Toulmin mapping illuminates without constraining — [PO] → ratified
- ~ [contestable] the Toulmin mapping is suggestive, not established — [PO] → revised
- ◐ [blind_spot] scaffolding absent from the Böckeler review — [aurele] → ratified
Lineage (antecedent dimension)
When a friction amends a prior friction (see protocol/friction.md §Lineage), the antecedent dimension is materialized by a ref: field at the end of the line:
- ✓ [sound] the protocol/observational distinction covers the case — [aurele] → ratified (ref: 2026-04-10-1430-aurele/3)
Format: ref: {source-id}/{index} where:
{source-id}= filename (without extension) — session summary or artifact{index}= friction position in the source file (1-based, order of appearance)
Implementation rules:
- The parser MUST follow
ref:links and propagate the resolution from the last link to the source friction. - A friction referenced by a
ref:does not appear in the open frictions list if the referencing link carries a resolution. - Counters count the logical friction once, with the original marker and the final resolution.
The exchange and emitter dimensions are implicit: the exchange is the current session, the emitter is the persona authoring the summary.
reportPattern in session summaries
Section ## reportPattern (MAY — observational layer for the observation, protocol layer for the counter).
The persona records the trigger and the orchestrator's choice:
## reportPattern
- Theme: [theme] — N rejected frictions (sessions YYYY-MM-DD, ...)
- Choice: LLM error | conviction | resistance
- Justification: ...
The audit counts triggers and the distribution of choices (protocol counter).
Contribution in session summaries
Section ## Flow (MAY — see exchange.md §Sessions, observational layer).
Each line carries the dimensions defined in contribution.md, rendered as:
## Flow
- {direction}:{type} — description
Direction: H (human brings) or A (assistant brings). Type: substance, structure, contestation, decision.
Counting (optional): a summary line at the end of the section.
Example:
## Flow
- H:substance — Böckeler article, request for opinion
- A:substance — scaffolding lineage absent from Böckeler
- A:structure — three levels of harness/SOFIA complementarity
- H:decision — keep keyword notation
H:2 (substance 1, decision 1) | A:2 (substance 1, structure 1)
The session dimension is implicit: it is the current session.
Operations — filesystem implementation
Mapping of H2A operations (see protocol/h2a.md) to the current implementation.
| Operation | Mode | Concrete gesture |
|---|---|---|
| openSession() | manual | The orchestrator launches a terminal in the persona's workspace (or resumes an existing Claude Code session) |
| closeSession() | manual | The orchestrator gives the signal. The persona rereads shared/conventions.md, then produces the summary, prepares the commit. The orchestrator executes the commit |
| send() | manual | The orchestrator instructs the persona. The persona rereads shared/conventions.md, then produces the artifact (note, review, feature) and deposits in the recipient's shared/. Cross-instance: the artifact goes to the recipient instance's shared/, not the emitter's |
| receive() | manual | The orchestrator opens a session with the recipient and presents the artifact. The recipient reads, processes, and MAY produce a response (with ref: to the source artifact) |
| markRead() | manual | The orchestrator sets status: read in the artifact's frontmatter |
| markDone() | manual | The orchestrator sets status: done in the frontmatter — the artifact is then moved to archives/ |
| qualifyFriction() | automatic | The persona pre-fills the ## Orchestrator friction section at closure. The orchestrator validates or corrects |
| qualifyContribution() | automatic | The persona pre-fills the ## Flow section at closure. The orchestrator validates or corrects |
| reportPattern() | automatic | The persona detects a thematic convergence of rejections during the session. It challenges the orchestrator with the observation + 3 argued hypotheses. The orchestrator responds with their choice + justification. At closure, the persona records in a ## reportPattern section of the summary |
Manual = the orchestrator triggers with an explicit gesture. Automatic = the persona produces at session closure, the orchestrator validates.
The persona MUST NOT close on its own or deposit an artifact without orchestrator instruction.
Tooling
| Tool | Role |
|---|---|
binding/filesystem/audit-instance.py |
Verifies instance protocol conformity |
binding/filesystem/create-instance.py |
Scaffolds a new instance |
sofia.md |
Meta persona — instantiates, audits. Protocol operator |
What is implementation vs protocol
| Element | Protocol (h2a.md) | Implementation (this doc) | |
|---|---|---|---|
| "Each session produces a trace" | ✓ | ||
| "The trace is a .md file committed in git" | ✓ | ||
| "The markers are [sound] [contestable] etc." | ✓ | ||
| "The markers are in a Markdown file" | ✓ | ||
| "The shared space is the sole channel" | ✓ | ||
| "The shared space is a shared/ directory" | ✓ | ||
| "The instance is identifiable" | ✓ | ||
| "The instance is identified by a sofia.md file" | ✓ | ||
| "Friction carries 5 dimensions" | ✓ | ||
| "Friction is a Markdown line with symbol + keyword + initiative" | ✓ | ||
| "Contribution carries direction and type" | ✓ | ||
| "Contribution is a `{H\ | A}:{type} — description` line" | ✓ | |
| "Friction carries a resolution tag" | ✓ | ||
"The resolution tag is rendered → ratified at end of Markdown line" |
✓ | ||
| "A resolution can evolve between sessions with ref:" | ✓ | ||
"The ref: is rendered (ref: session-id/n) at end of Markdown line" |
✓ | ||
| "reportPattern() produces an observation + 3 hypotheses + qualification" | ✓ | ||
"reportPattern is a ## reportPattern section in the summary" |
✓ | ||
| "The reportPattern choice counter is auditable" | ✓ |
Perspectives
HTTP API
H2A could be implemented as a REST API:
- Each entity (Instance, Space, Persona, Exchange, Friction, Contribution) becomes a resource
- YAML frontmatter becomes JSON schemas
- Statuses (
new→read→done) become state transitions - Audit becomes a verification endpoint
Database
Session summaries, artifacts, and friction markers could live in a relational or document database. Git history would be replaced by an event log.
MCP / A2A interoperability
H2A covers the human-assistant layer. MCP and A2A cover the agent-tools and agent-agent layers. A complete system could combine all three:
- H2A for human-assistant coordination
- MCP for resource access by assistants
- A2A for inter-assistant coordination (if the human routing invariant is relaxed)
Wire format
If H2A evolves toward a technical protocol, a wire format (JSON, protobuf, etc.) and an interoperability spec will be needed. This is not in the current scope.
Audit rules — binding/filesystem
Reference for all checks performed by
analysis/cli/probe.py.
Check ID taxonomy
Format: {level}{category}{number}
| Prefix | Level | Category | Conditioned by |
|---|---|---|---|
| PS | Protocol | Structure | always |
| PP | Protocol | Personas | always |
| PA | Protocol | Artifacts | always |
| PF | Protocol | Format | always |
| AN | Artifact | Note | --artifacts notes |
| AR | Artifact | Review | --artifacts reviews |
| AF | Artifact | Feature | --artifacts features |
| IS | Instance | Structure | not --protocol-only |
| IN | Instance | Naming | not --protocol-only |
| IR | Instance | Roadmap | not --protocol-only |
Severity
| Severity | Meaning |
|---|---|
fail |
Blocks conformity — the instance is not valid |
warn |
Should be fixed — degraded quality |
info |
Informational — no action required |
Protocol checks
Protocol Structure (PS)
| ID | Rule | Severity | Source |
|---|---|---|---|
| PS1 | sofia.md present at root |
fail | protocol/exchange.md — instance marker |
| PS2 | shared/ directory present |
fail | protocol/exchange.md — shared space |
| PS3 | shared/conventions.md present |
warn | protocol/exchange.md — exchange rules |
| PS4 | At least 1 workspace with CLAUDE.md |
fail | protocol/exchange.md — persona space |
| PS5 | Each workspace has sessions/ |
warn | protocol/exchange.md — session traces |
Protocol Personas (PP)
| ID | Rule | Severity | Source |
|---|---|---|---|
| PP1 | shared/orga/personas/ has persona-*.md files |
warn | core/model.md — persona entity |
| PP2 | Each workspace maps to a persona file | warn | protocol/exchange.md — persona space |
| PP3 | Persona files have 7 required dimensions (identity, stance, scope, deliverables, prohibitions, challenge, collaboration) | warn | core/model.md §7 dimensions |
| PP4 | Each persona has a context file in shared/orga/contextes/ |
info | Instance convention |
Protocol Artifacts (PA)
Global checks on ALL .md files in shared/ (excluding conventions.md, roadmap-*.md, orga/, audits/).
| ID | Rule | Severity | Source |
|---|---|---|---|
| PA1 | All artifacts have frontmatter | warn | protocol/exchange.md §Artifacts |
| PA2 | All artifacts have required fields (from, to, nature, status, date) |
warn | protocol/exchange.md §Specific dimensions |
| PA3 | All status values are valid (new/read/done or FR equivalents) |
warn | protocol/exchange.md §Lifecycle |
Protocol Format (PF)
| ID | Rule | Severity | Source |
|---|---|---|---|
| PF1 | Sessions have conformant frontmatter (persona, date) |
info | protocol/exchange.md §Sessions |
Artifact checks
Each declared artifact type gets 5 standard checks. Add --artifacts notes,reviews,features to control which types are audited.
Artifact Note (AN)
| ID | Rule | Severity |
|---|---|---|
| AN1 | shared/notes/ present with archives/ |
info |
| AN2 | Notes have frontmatter | warn |
| AN3 | Required fields (from, to, nature, status, date) |
warn |
| AN4 | Naming convention note-{subject}-{author}.md |
info |
| AN5 | Done files archived | warn |
Artifact Review (AR)
| ID | Rule | Severity |
|---|---|---|
| AR1 | shared/review/ present with archives/ |
info |
| AR2 | Reviews have frontmatter | warn |
| AR3 | Required fields (from, to, nature, status, date, subject) |
warn |
| AR4 | Naming convention review-{subject}-{author}.md |
info |
| AR5 | Done files archived | warn |
Artifact Feature (AF)
| ID | Rule | Severity |
|---|---|---|
| AF1 | shared/features/ present |
info |
| AF2 | Features have frontmatter | warn |
| AF3 | Required fields (from, to, nature, status, date) |
warn |
Other artifact types (dynamic)
Any type declared in ## Artifact types of shared/conventions.md gets the same 5 checks (A{X}1-A{X}5) with a dynamic prefix. Common optional types:
| Type | Prefix | Directory | Naming | Notes |
|---|---|---|---|---|
| features | AF | shared/features/ | feature-{name}.md |
Feature specs |
| adr | AA | shared/adr/ | adr-{number}-{title}.md |
Instance-level decision records |
| team-orga | AT | shared/orga/ | team-orga.md |
Team organization (single file) |
These are not protocol — they are instance conventions. An instance that doesn't use ADRs or team-orga simply doesn't declare them.
Instance checks
Instance Structure (IS)
| ID | Rule | Severity |
|---|---|---|
| IS1 | shared/orga/ present |
info |
| IS2 | Roadmaps in shared/ |
info |
| IS3 | No accents in frontmatter values | info |
| IS4 | Files in archives/ have status done |
info |
Instance Naming (IN)
| ID | Rule | Severity |
|---|---|---|
| IN1 | Roadmaps follow roadmap-{product}.md |
info |
Instance Roadmap (IR)
Conventions documented in shared/conventions.md §Roadmaps.
| ID | Rule | Severity |
|---|---|---|
| IR1 | Roadmap has # Roadmap header |
warn |
| IR2 | Roadmap declares an Owner | warn |
| IR3 | Version sections have metadata comment | info |
| IR4 | Items have a status tag | warn |
| IR5 | Items have an @owner |
warn |
| IR6 | Uses ↔ convergence markers |
info |
| IR7 | Uses cible: markers |
info |
| IR8 | Uses source: markers |
info |
Artifact types
By default, the audit scans notes and reviews in shared/. An instance can declare additional artifact types in shared/conventions.md using a ## Artifact types section:
## Artifact types
| Type | Directory | Naming | Extra fields |
|------|-----------|--------|--------------|
| notes | shared/notes/ | `note-{subject}-{author}.md` | |
| reviews | shared/review/ | `review-{subject}-{author}.md` | subject |
| features | shared/features/ | `feature-{name}.md` | |
| adr | shared/adr/ | `adr-{number}-{title}.md` | |
| team-orga | shared/orga/ | `team-orga.md` | |
The --artifacts flag lets the user override which types are audited:
# Audit only protocol checks + notes and reviews (default)
python analysis/cli/probe.py /path/to/instance
# Audit with features included
python analysis/cli/probe.py /path/to/instance --artifacts notes,reviews,features
# Protocol checks only — skip all artifact and instance checks
python analysis/cli/probe.py /path/to/instance --protocol-only
When --protocol-only is set, all artifact checks (AN1-AN5, AR1-AR5, AF1-AF3) and instance checks (IS1-IS4, IN1, IR1-IR8) are skipped.
Phase 2 — Exchanges & friction
Phase 2 scans all .md files in the declared artifact directories and extracts:
- Exchange matrix — who sends to whom (from
from:/to:frontmatter) - Friction matrix — who challenges whom (from friction markers in body)
- Orchestrator friction — frictions from session summaries (
## Orchestrator friction) - reportPattern() — convergence detection across rejected frictions
These are observational — no pass/fail, just data.
Conventions
Instance conventions. Extend as the project evolves.
Artifact types
Declare which artifact types this instance uses. The audit checks only declared types.
| Type | Directory | Naming | Extra fields |
|---|---|---|---|
| notes | shared/notes/ | note-{subject}-{author}.md |
|
| reviews | shared/review/ | review-{subject}-{author}.md |
subject |
<!-- Add rows for additional types used by this instance, e.g.:
| features | shared/features/ | feature-{name}.md |
|
|---|---|---|---|
| adr | shared/adr/ | adr-{number}-{title}.md |
|
| team-orga | shared/orga/ | team-orga.md |
-->
Structure
Maintain as you go. When a new directory appears in
shared/(notes/, review/, etc.), add it here so personas know where to look and where to deposit.
instance/
├── sofia.md
├── shared/
│ ├── conventions.md
│ └── orga/
│ ├── personas/
│ └── contextes/
├── {space}/
│ ├── CLAUDE.md
│ └── sessions/
└── ...
Repo ownership (optional)
<!-- Uncomment and fill if your instance spans external repos.
| Repo | Path | Owner |
|---|---|---|
| product-repo | doc/architecture/ | @architect |
| product-repo | (rest) | @dev |
-->
Sessions
Session summary
At each closure, the persona creates a file in {space}/sessions/:
{YYYY-MM-DD}-{HHmm}-{persona}.md
HHmm is the closure time (not the boot time).
Session frontmatter
---
persona: persona-name
date: YYYY-MM-DD
session: "HHmm"
---
Protocol sections (MUST)
| Section | Content |
|---|---|
## Produced |
Files created or modified |
## Decisions |
Choices made |
## Shared notes |
Artifacts deposited in shared/ |
## Open |
Unresolved questions |
No prose — short lists. 30 lines max.
Observational sections
| Section | Status | Content |
|---|---|---|
## Orchestrator friction |
SHOULD | Qualified frictions |
## Flow |
MAY | Epistemic contributions |
Commits
{persona}: {short summary} ({date})
One commit per session.
Artifacts
Every artifact deposited in shared/ carries a YAML frontmatter.
---
from: emitting-persona
to: recipient-persona
nature: signal # signal | question | request | response
status: new # new | read | done
date: YYYY-MM-DD
ref: source-artifact # SHOULD when nature = response (filename without extension)
---
Lifecycle
| Status | Meaning |
|---|---|
new |
Deposited, not yet read by recipient |
read |
Read by recipient |
done |
Processed by recipient |
Resolution
When an artifact is processed, each point SHOULD carry a resolution tag in the document body:
→ ratified | → contested | → revised | → rejected
Friction
Each line carries: marker + description + initiative + resolution.
- [marker] description — [initiative] → resolution
Markers
5 epistemic positions. Closed set — do not add new ones.
| Symbol | Marker | Meaning |
|---|---|---|
| ✓ | [sound] |
Corroboration — position is correct |
| ~ | [contestable] |
Underdetermination — defensible but not the only reading |
| ⚡ | [simplification] |
Reductionism — reality is more complex |
| ◐ | [blind_spot] |
Incompleteness — missing data |
| ✗ | [refuted] |
Refutation — factually incorrect or incoherent |
Bracketed keywords are authoritative for the audit.
Initiative
[persona] or [PO] — who initiated the friction topic.
Resolution
| Tag | Meaning |
|---|---|
ratified |
Agreement — the position is accepted |
contested |
Disagreement maintained — no change of position |
revised |
Disagreement with change of position |
rejected |
Terminal disagreement — the position is discarded |
One tag per friction point.
Cross-session mutability
A resolution may evolve in a later session. The friction SHOULD carry a ref: field pointing to the original friction:
- ✓ [sound] description — [persona] → ratified (ref: 2026-04-10-1430-persona/3)
Quick reading
- Only ✓ → absent friction — alert signal
- Mix ✓/~/⚡ → healthy friction
- Presence of ◐ or ✗ → tension to address
- No resolution → unresolved frictions, to address or report in Open
Contribution (epistemic flow)
Section ## Flow — optional.
- {direction}:{type} — description
| Direction | Meaning |
|---|---|
H |
The human (orchestrator) contributes |
A |
The assistant (persona) contributes |
| Type | Definition |
|---|---|
substance |
New information |
structure |
Formatting, categorization, synthesis |
contestation |
Challenge, counter-example |
decision |
Arbitration, choice made |
Optional counting at end of section.
Roadmaps
Roadmaps live in shared/ and follow the naming convention roadmap-{product}.md.
Structure
Each roadmap SHOULD have:
- A
# Roadmap {name}header - An owner declaration in the blockquote:
> Owner : @persona - Version sections (
### vX.Y.Z) with metadata comments:<!-- produit: X | cible: YYYY-MM | statut: todo -->
Items
Each item is a markdown list entry (- [status] description @owner).
- Status:
[done],[running],[todo],[blocked],[ready] - Owner:
@persona— who is responsible - Cross-instance:
↔marker for dependencies on other instances
Cross-instance exchanges
When the orchestrator routes an artifact between two instances, the artifact MUST be deposited in shared/ of the recipient's instance — not the emitter's.
The emitting persona does not need to know the recipient's instance. The orchestrator crosses instance boundaries — personas remain isolated.
See protocol/exchange.md §Cross-instance exchanges.