Codename: chainlink · Scope: objective-critical core · Local, single-user Date: 2026-06-14 · Sources:
PRD.md,PRFAQ.md
Build Dossier as a local, single-user memory layer for long-running agent work. A Dossier is a durable topic with a curated Markdown Distilled State, a source-retaining Archive of captured artifacts, and an append-only audit log. v1 supports Claude Code through MCP, CLI/TUI, context files, and lifecycle hooks.
The product must optimize for agent-initiated use: when a supported agent session starts, the agent should see the user's Dossier library, understand capability limitations for that harness/session, and help the user continue or create a Dossier without forcing a separate CLI workflow.
- Flat topics: no graph, tree, hierarchy, or persistent cross-Dossier links. Related Dossiers are merged.
- Two layers: Distilled State and Archive are separate. Distilled State carries critical state; Archive carries captured source.
- No database: files are the source of truth. Indexes, if ever added, are derived caches only and not v1.
- No human gate for ordinary saves: distillation writes commit without confirmation, governed by the Distillation Guide and hook cadence.
- Human disambiguation for ambiguous/destructive actions: ambiguous link targets and merge conflicts must ask the user.
- Per-session active Dossier: each agent session has zero or one active Dossier; there is no global active Dossier.
- No native deletion: Dossier supports archive, not delete. Deletion is manual folder removal by the user.
- Text-first artifacts: Markdown, JSON, and TXT are supported. Native binary attachment storage is out of scope.
- 100k-token Distilled State target: over-target recall is allowed with explicit warning; never silently truncate critical state.
- Visible degradation: if a harness lacks hooks or transcript capture, say so at install and session start.
Ship one self-contained executable named dossier.
The binary provides:
- CLI commands.
- TUI views for list/select/merge where useful.
- MCP server over stdio.
- Hook installer/config generator.
- Context-file generator.
- File store read/write/search.
- Token estimation.
- Distillation Guide asset delivery.
Implementation language recommendation: Go or Rust. Choose based on the fastest path to reliable cross-platform single-binary distribution and MCP stdio support.
Default root:
~/.dossier/
The root can be overridden by:
DOSSIER_HOME=/path/to/store
Store layout:
~/.dossier/
config.yaml
context/
library.md
guide.md
sessions/
<session-binding-id>.json
<slug>/
dossier.md
artifacts/
<artifact-id>.md
<artifact-id>.json
<artifact-id>.txt
conflicts/
<conflict-id>.md
audit.log
config.yaml records install settings, detected harness capabilities, default token target, and optional custom priority weights.
context/library.md is the generated open-work context file for harnesses without deterministic hooks.
context/guide.md is the installed Distillation Guide.
sessions/<session-binding-id>.json records per-session active Dossier bindings where the harness can provide or persist a session identifier.
Each dossier.md begins with YAML frontmatter:
id: dos_01jz8example000000000000000
name: Pricing model refresh
slug: pricing-model-refresh
created_at: 2026-06-14T15:40:00-07:00
updated_at: 2026-06-14T16:10:00-07:00
last_touched_at: 2026-06-14T16:10:00-07:00
status: active
lead: "Alice"
next_action: "Compare revised pricing scenarios with sales feedback."
open_questions:
- "Does Sales prefer account-tier or usage-tier packaging?"
importance: high
urgency: low
due_date: 2026-06-21
token_target: 100000
base_revision: rev_01jz8example000000000000000Required fields:
idnameslugcreated_atupdated_atlast_touched_atstatusnext_actionopen_questionsimportanceurgency
Optional fields:
leaddue_datetoken_targetbase_revision
Valid enums:
status:active,waiting,blocked,resolved,archivedimportance:high,lowurgency:high,low
Derived, not stored:
staleness- priority score
- token estimate
Required section order:
# <Dossier Name>
## Situation
## Decisions
## Findings
## Current State
## Next StepsRules:
- Preserve critical substance over brevity.
- Strip greetings, small talk, dead-end reasoning, tool-call mechanics, and redundant restatement.
- Every material claim must include provenance.
- Decisions must include what was decided, rationale, attribution, date if known, and provenance.
- Superseded claims should be replaced in the Distilled State; source remains in Archive/audit.
Recommended provenance syntax:
- Sales prefers annual commits for enterprise renewals. [src:art_01jz8salesfeedback#L42-L68]For artifacts without line support:
- The original launch date was moved to July. [src:art_01jz8launchthread]Artifacts are Markdown, JSON, or TXT files under artifacts/.
Markdown artifact frontmatter:
id: art_01jz8example000000000000000
dossier_id: dos_01jz8example000000000000000
type: source_snapshot
title: "Slack thread: pricing approval"
captured_at: 2026-06-14T16:00:00-07:00
refreshed_at: 2026-06-14T16:00:00-07:00
frozen: false
provenance:
origin: "Slack"
url: "https://example.slack.com/archives/..."
captured_by: "Claude Code"
harness: "claude-code"
content_format: markdown
source_size_bytes: 19420Valid artifact types:
transcriptsource_snapshotfile_snapshotlinkquerydecision_evidence
Rules:
- No max artifact count per Dossier.
- Reject any single artifact over 1 GB.
- Native binary storage is unsupported. For binary-only or too-large files, store metadata, path/link, provenance, and any agent-provided textual extraction or summary.
- Markdown is preferred for documents and snapshots.
- JSON is acceptable for structured data.
- TXT is acceptable for transcripts and plain text.
audit.log is append-only JSON Lines.
Example:
{"ts":"2026-06-14T16:10:00-07:00","event":"save","dossier_id":"dos_...","actor":"agent:claude-code","session_id":"sess_...","before_revision":"rev_a","after_revision":"rev_b","artifacts_added":["art_..."],"token_estimate":8420}Required event types:
createsavepromotelinkmerge_startedmerge_completedmerge_conflictstatus_changedarchivedsnapshot_refreshedsnapshot_frozenambiguity_confirmedconflict_createdconflict_resolvedtranscript_capture_unavailableinstall_warning
Audit writes must be append-only and last-write-wins is acceptable for audit entries.
An active Dossier is bound to one agent session.
Binding fields:
{
"session_binding_id": "sess_01jz8example000000000000000",
"harness": "claude-code",
"dossier_id": "dos_01jz8example000000000000000",
"bound_at": "2026-06-14T16:00:00-07:00",
"last_seen_revision": "rev_01jz8example000000000000000",
"capabilities": {
"mcp": true,
"session_start_hook": true,
"session_end_hook": false,
"pre_compaction_hook": false,
"transcript_capture": false
}
}If a harness cannot expose a durable session id, the agent may maintain binding in context only. In that case, Dossier must still support dossier_session(id?) during the session, but deterministic recovery of the binding after restart is not guaranteed.
Switching active Dossier must:
- Save the current active Dossier first where possible.
- Clear the session binding.
- Bind the new Dossier.
- Return the new Distilled State, token estimate, and capability warnings.
- Audit
status_changedonly if lifecycle status changes; switching alone is not a Dossier lifecycle change.
/clear removes the Dossier from session context but does not alter the Dossier on disk.
v1 supports Claude Code only. Claude Code provides the full capability set Dossier relies on:
- Session-start surfacing is deterministic.
- Session-end save is deterministic.
- Pre-compaction save is deterministic.
- MCP tools work.
- Raw transcript capture works.
Other harnesses (e.g. Codex, Antigravity) reach only degraded capability levels — missing transcript capture and/or deterministic session-start/session-end hooks — which are insufficient for Dossier's guarantees, so they are out of scope for v1.
Even within Claude Code, an expected capability may be unavailable in a given session (e.g. transcript access). When that happens, Dossier must degrade visibly, never silently: install and session-start notices must warn about the missing capability rather than skip it quietly.
Before implementation, verify for Claude Code:
- MCP server registration path.
- Slash command support.
- SessionStart hook support.
- session-end hook support, including
/clearand/exitif available. - pre-compaction hook support.
- raw transcript access.
- session identifier access.
- ability to inject context at session start.
- ability to show install/session-start notices.
The first implementation milestone should produce a capability matrix in docs/harness-capabilities.md.
dossier init
dossier ls [--status active|waiting|blocked|resolved|archived|all] [--json]
dossier show <slug-or-id> [--json]
dossier promote [--name <name>] [--from-file <path>] [--json]
dossier link [<slug-or-id>] [--from-file <path>] [--json]
dossier merge <source> <target> [--json]
dossier recall <slug-or-id> [--json]
dossier search <query> [--dossier <slug-or-id>] [--json]
dossier status <slug-or-id> <active|waiting|blocked|resolved|archived>
dossier lead <slug-or-id> "<lead-name>"
dossier next <slug-or-id> "<next action>"
dossier questions <slug-or-id> add|set|clear [...]
dossier priority <slug-or-id> --importance <h|l> --urgency <h|l> [--due <date>]
dossier active [--session <session-id>] [--json]
dossier switch <slug-or-id> [--session <session-id>] [--json]
dossier path [<slug-or-id>] [--json]
dossier archive <slug-or-id> [--json]
dossier context refresh
dossier doctor
dossier init
- Detects when running from a volatile/temporary path and offers to self-install to a stable PATH location (default
~/.local/bin/dossier) first. - Creates store directories.
- Writes default config.
- Installs Distillation Guide.
- Generates
context/library.md. - Detects configured harnesses where possible.
- Prompts for confirmation before updating user/global configurations to register the Dossier MCP server and lifecycle hooks, preserving existing third-party servers and hooks (never-clobber behavior). Writes each to the file Claude Code actually reads (hooks →
~/.claude/settings.json, MCP →~/.claude.json) and idempotently migrates stale entries an older build wrote to the wrong file. - Prints detected capabilities and warnings for Claude Code.
- Does not fail if a harness config cannot be updated or hooks are unsupported; warns visibly.
dossier ls
- Reads frontmatter across
*/dossier.md. - Default status filter:
active,waiting,blocked. - Sorts by priority score.
- Includes capability warning column if invoked inside a known harness/session.
dossier promote
- Creates a new Dossier from agent-provided content or
--from-file. - If likely existing matches are found, returns top candidates and requires disambiguation unless
--nameand explicit create intent are provided. - Captures transcript artifact if available.
- Warns if transcript capture is unavailable.
dossier link
- With id: attaches current session content to the target Dossier.
- Without id: returns top 3 candidates with confidence and asks for selection when ambiguous.
- Must not silently attach to a low-confidence match.
dossier merge
- Asks/records which Dossier is surviving target.
- Creates unified Archive.
- Produces converged Distilled State.
- Surfaces conflicts for human resolution.
- Does not silently reconcile contradictions.
dossier recall
- Returns full Distilled State.
- Estimates tokens.
- Warns if above
token_target. - Does not load Archive artifacts by default.
dossier archive
- Sets status to
archived. - Hides Dossier from default open-work view.
- Does not delete files.
dossier doctor
- Validates store integrity.
- Checks YAML frontmatter.
- Checks missing artifact links.
- Checks provenance references.
- Checks harness installation/capability status.
- Prints warnings and suggested fixes.
All MCP tools are prefixed with dossier_.
Required tools:
dossier_listdossier_recalldossier_searchdossier_savedossier_promotedossier_linkdossier_mergedossier_sessiondossier_update
Note on
dossier_update: it acceptsname,status,lead,next_action,open_questions, and priority fields, and routes them all through the singleSavewrite path (so CLI/MCP/TUI behave identically and edits get optimistic-concurrency handling). Changingnameupdates the display name only — theslug(and the on-disk directory) is the durable identifier and never changes on rename.
All tools return:
{
"ok": true,
"data": {},
"warnings": [],
"next_actions": []
}Errors return:
{
"ok": false,
"error": {
"code": "ambiguous_target",
"message": "Multiple likely Dossiers match this session.",
"details": {}
},
"warnings": [],
"next_actions": []
}Required error codes:
not_foundambiguous_targetconflict_detectedinvalid_frontmatterartifact_too_largebinary_artifact_unsupportedtranscript_unavailableover_token_targetconcurrent_editharness_capability_unavailable
Input:
{
"status": ["active", "waiting", "blocked"],
"limit": 50,
"include_warnings": true
}Output Dossier item:
{
"id": "dos_...",
"name": "Pricing model refresh",
"slug": "pricing-model-refresh",
"status": "active",
"lead": "Alice",
"next_action": "Compare revised pricing scenarios with sales feedback.",
"open_questions": ["Does Sales prefer account-tier or usage-tier packaging?"],
"importance": "high",
"urgency": "low",
"due_date": "2026-06-21",
"staleness_days": 2,
"priority_score": 2,
"path": "/Users/me/.dossier/pricing-model-refresh",
"warnings": ["Transcript archive unavailable in this session."]
}Input:
{
"id": "dos_...",
"base_revision": "rev_...",
"distilled_state_markdown": "...",
"frontmatter_updates": {
"status": "active",
"next_action": "...",
"open_questions": []
},
"artifacts": [
{
"type": "source_snapshot",
"title": "Slack thread: pricing approval",
"content_format": "markdown",
"content": "...",
"provenance": {
"origin": "Slack",
"url": "..."
}
}
]
}Behavior:
- Validate frontmatter updates.
- Validate artifact sizes and formats.
- Check optimistic concurrency using
base_revision. - If concurrent edit is detected, create conflict artifact/draft and return
concurrent_edit. - Estimate tokens.
- Write
dossier.mdatomically. - Append audit event.
- Return new revision and warnings.
Input:
{
"name": "Pricing model refresh",
"distilled_state_markdown": "...",
"session_content": "...",
"transcript": null,
"harness": "claude-code"
}Behavior:
- Run suggestion against existing Dossiers before creating.
- If high ambiguity exists, return
ambiguous_targetwith candidates. - Create Dossier if create intent is clear.
- Store
session_contentas artifact if useful and provided. - Store
transcriptastranscriptartifact if available. - Warn if transcript unavailable.
Input:
{
"id": "dos_...",
"base_revision": "rev_...",
"distilled_state_markdown": "...",
"session_content": "..."
}If id is omitted, return candidates:
{
"candidates": [
{"id": "dos_...", "name": "Pricing model refresh", "confidence": 0.82, "reason": "Shared terms: pricing, sales, packaging"}
],
"requires_selection": true
}Input:
{
"source_id": "dos_a",
"target_id": "dos_b",
"resolved_conflicts": []
}Behavior:
- Target survives.
- Source folder is retained but marked merged/archived or moved under target audit metadata; do not delete source files in v1.
- Unified Archive contains all artifacts.
- Conflict detection covers contradictory decisions, divergent next actions, incompatible lifecycle states, and material claims with incompatible values.
- If conflicts exist and
resolved_conflictsis empty, returnconflict_detected.
Session start injection fires on every session regardless of whether that
session has anything to do with Dossier, so its unbound-session payload is
deliberately a single-line nudge, not a full listing — the heavy payload
(Distillation Guide, full Distilled State) is delivered by the MCP tool
calls themselves (dossier_session's response), the moment an agent
actually enters a Dossier's context, not passively on every session start.
When supported, session start injection includes:
# Dossier Library
Warning: Transcript archive is unavailable in this session.
3 open dossier(s): Alpha, Beta, Gamma. Use dossier_list for details, dossier_session to resume one, or dossier_promote for a new thread (it flags likely duplicates automatically). Guide: ~/.dossier/context/guide.mdIf a session has an active binding, also inject the Distillation Guide and the active Dossier's Distilled State in full.
If no active binding exists, the one-line nudge above is the entire payload — no further instructions block. dossier_promote's own similarity check (returned as next_actions on ambiguous matches) is what actually guides the agent's decision, not prose injected up front.
When supported:
- Trigger
dossier_savefor the active Dossier. - Include latest Distilled State from agent context.
- Capture transcript if available.
- Audit success/failure.
If unsupported:
- Distillation Guide instructs the agent to self-trigger a save at boundaries.
- Session-start notices must warn that deterministic save backstops are unavailable.
Generate:
~/.dossier/context/library.md
This file contains:
- Capability warnings from last
dossier initordossier doctor. - Open-work list.
- Instructions for loading a Dossier.
- Pointer to Distillation Guide.
dossier context refresh regenerates it.
The Distillation Guide is a shipped prompt asset stored at:
~/.dossier/context/guide.md
It must instruct agents to:
- Keep the session's active Dossier current best-effort each turn.
- Save at session end,
/clear,/exit, and before compaction where possible. - Preserve material claims, decisions, rationale, attribution, constraints, current state, open questions, next action, and findings.
- Strip greetings, small talk, dead ends, tool mechanics, and redundant restatement.
- Add provenance to every material claim.
- Ask the user for ambiguous link targets and merge conflict resolution.
- Never silently truncate Distilled State to satisfy the token target.
- Warn when transcript capture or deterministic hooks are unavailable.
The guide should include examples of good and bad distillation.
Default scoring: The importance and urgency dimensions map to a 1-4 Eisenhower matrix scale (where 1 is highest priority):
- 1: High Importance, High Urgency ("1. Do")
- 2: High Importance, Low Urgency ("2. Plan")
- 3: Low Importance, High Urgency ("3. Delegate")
- 4: Low Importance, Low Urgency ("4. Delete")
Priority score is strictly the Eisenhower matrix scale value (1-4).
Sort ascending by score, then oldest last_touched_at, then updated_at.
Weights are configurable in config.yaml.
v1 uses lexical ranking.
Inputs:
- Current session content.
- Candidate Dossier name.
- Frontmatter fields.
- Distilled State.
- Artifact titles.
Scoring:
- Normalize lowercase.
- Remove stop words.
- Tokenize words.
- Weight exact name/slug matches highest.
- Weight
next_actionandopen_questionsabove body text. - Weight recent Dossiers slightly above stale ones, but do not use recency as primary ranking.
Return top 3 candidates with confidence:
high: strong exact or repeated domain match.medium: plausible overlap.low: weak overlap.
Only high confidence can be recommended as likely; never silently link.
Use file scan, not database.
Preferred implementation:
- Native recursive text search in binary, or shell out to
ripgrepif available. - Search
dossier.md, artifact.md,.json,.txt, and titles/frontmatter. - Exclude unsupported binary files.
Return:
- Dossier id/name.
- Artifact id/title if artifact match.
- Path.
- Snippet.
- Line reference where available.
Use one tokenizer benchmarked against Opus 4.8 as reference. Precision per target model is not required.
Behavior:
- Estimate Distilled State tokens on recall/save.
- Default target: 100,000 tokens.
- If estimate exceeds target, return warning with recommended next actions.
- Do not fail recall solely due to target overflow.
- Do not silently truncate.
Each save includes base_revision.
Revision can be a hash of canonical dossier.md content plus artifact manifest.
On save:
- Read current revision.
- If current revision equals
base_revision, write atomically. - If current revision differs:
- If only non-overlapping frontmatter changed, auto-merge and audit.
- Otherwise create
conflicts/<conflict-id>.md. - Return
concurrent_edit. - Preserve proposed write in conflict file.
No last-write-wins for Distilled State.
For dossier.md:
- Read current content.
- Validate concurrency.
- Write temp file in same directory.
- fsync temp file where practical.
- Rename temp file over target.
- Append audit event.
For artifacts:
- Generate id first.
- Write artifact file atomically.
- Append audit event after successful write.
Slug rules:
- Lowercase.
- ASCII preferred.
- Replace spaces and punctuation with
-. - Collapse repeated
-. - Trim leading/trailing
-. - If conflict, append short id suffix.
Use sortable unique ids with prefixes:
dos_art_sess_rev_conf_
ULID-style ids are acceptable.
Must:
- Create
~/.dossier. - Create
context/. - Create
sessions/. - Write
config.yaml. - Write Distillation Guide.
- Generate library context file.
- Detect Claude Code config availability where possible.
- Attempt to install/register MCP and hooks only where supported and safe.
- Print detected capabilities and warnings.
Example output:
Dossier initialized at ~/.dossier
Claude Code integration:
- detected
- MCP: available
- Session-start hook: available
- Session-end hook: available
- Pre-compaction hook: available
- Transcript capture: available
Checks:
- Store exists.
- Config parses.
- Dossier frontmatter parses.
- Required sections exist.
- Artifact references resolve.
- Provenance links resolve.
- Audit log is parseable JSONL.
- Context files are current.
- Harness registrations exist.
- Capability warnings are current.
- Creating a Dossier writes
dossier.md,artifacts/, andaudit.log. dossier.mdis readable as plain Markdown.- Frontmatter scan over 500 Dossiers completes under 2 seconds on a typical laptop.
- No database file is created.
dossier_recallreturns full Distilled State.- Recall includes token estimate.
- Over-target Distilled State returns warning and does not truncate.
- Archive artifacts are not loaded by default.
- Sample generated Dossiers include provenance on every material claim.
dossier doctorreports missing provenance.- Provenance links resolve to artifacts or line ranges where available.
- Promote creates a new Dossier from agent-provided content.
- Promote warns when transcript capture is unavailable.
- Link without id returns candidates when matches exist.
- Ambiguous link requires user/agent selection.
- No low-confidence candidate is silently linked.
- Merge requires surviving target.
- Contradictory decisions produce conflict.
- Conflict is preserved in
conflicts/. - Source Dossier files are not deleted.
- Merge audit events are written.
- Each session can bind zero or one active Dossier.
- Two sessions can bind different Dossiers.
dossier_sessionreports or changes session binding and returns new Distilled State when switching./clearremoves session context but does not alter Dossier files.
dossier initreports detected Claude Code capabilities.- Session-start context reports missing hooks/transcript capture.
- recall/save/search remain available through the MCP/context-file path even when hooks are unavailable.
- Concurrent Distilled State edit creates conflict artifact instead of overwriting.
- Non-overlapping frontmatter update can auto-merge.
- Audit log records conflict creation and resolution.
dossier archivehides Dossier from default open-work view.- Archived Dossier remains searchable.
- No CLI/MCP delete command exists.
- Choose Go or Rust.
- Implement config/store root.
- Produce
docs/harness-capabilities.md. - Stub CLI and MCP server.
- Implement
dossier initanddossier doctorbaseline.
- Implement Dossier create/read/update.
- Implement frontmatter parsing.
- Implement artifact write/read.
- Implement audit JSONL.
- Implement list, show, path, archive.
- Implement recall.
- Implement token estimation.
- Implement over-target warning.
- Implement full-text search.
- Implement generated context library.
- Implement MCP stdio server.
- Implement required MCP tools.
- Validate tool contracts against at least one harness.
- Implement promote.
- Implement lexical suggestion engine.
- Implement link flow and ambiguity handling.
- Implement transcript warning behavior.
- Implement session binding.
- Implement switch/active.
- Install hooks for Claude Code.
- Add session-start library injection.
- Add session-end/pre-compaction save where supported.
- Implement optimistic concurrency.
- Implement conflict artifacts.
- Implement merge target selection and conflict reporting.
- Implement merge audit events.
- Ship initial Distillation Guide with examples.
- Dogfood across real Claude Code sessions.
- Measure success metrics from PRD.
- Tighten guide and harness warnings.
These are not product blockers; they are discovery tasks for build planning.
- Which v1 language, Go or Rust, gives the fastest reliable MCP + TUI + cross-platform binary path?
- For Claude Code, what exact config files and hooks are available?
- Can Claude Code expose raw transcripts safely and deterministically?
- Can Claude Code provide a stable session id?
- What is the safest install behavior for modifying Claude Code's config?
- Should hook installation require explicit confirmation in
dossier init? - Which tokenizer library best approximates Opus 4.8 while staying lightweight?
- Should
ripgrepbe a soft dependency, or should v1 implement native search to preserve single-binary purity? - What exact Markdown provenance syntax is easiest for agents to maintain and for
doctorto validate? - How much TUI is needed in v1 versus plain CLI plus MCP responses?
The tooling will become effective fastest through dogfood loops rather than abstract polish.
Recommended partnership rhythm:
- Harness reality pass: use your actual Claude Code setup to map hooks, transcript access, config files, and session ids.
- Dossier seed set: create 10-20 real Dossiers from your current work, including messy ones, clean ones, blocked ones, and resolved ones.
- Distillation review sessions: compare generated Distilled State against your memory of the work; tune the Distillation Guide rather than adding confirmation gates.
- Resume drills: intentionally resume topics in a different Claude Code session than the one that created them; record what was missing or bloated.
- Ambiguity drills: test promote/link against similar topics to tune suggestion confidence and wording.
- Failure drills: force unavailable transcript, over-target Distilled State, concurrent edits, and merge conflicts; verify warnings feel clear rather than noisy.
- Weekly metric check: track cross-session resume success, provenance misses, over-target warnings, and time-to-find-next-topic.
The important collaboration mode: you provide real topic workflows and judgment about whether the agent resumed with the right state; the implementation should make every hidden limitation explicit and every recovery path easy.