Conversation
…eria from truthkit-kb (E0008.4 Phase 1)
…riteria from truthkit-kb (E0008.4 Phase 1)
…eria from truthkit-kb (E0008.4 Phase 1)
…iteria from truthkit-kb (E0008.4 Phase 1)
…ria from truthkit-kb (E0008.4 Phase 1)
… port truthkit-kb pattern (E0008.4 Phase 1)
…with truthkit-kb)
…im with klappy.dev frontmatter (E0008.4 Phase 1)
Canon Quality —
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Fallback type declaration removed from observation frontmatter
- Restored the
fallback: truefield in the frontmatter ofodd/encoding-types/observation.mdso Observation remains the fallback encoding type as documented inhow-to-write-encoding-types.md.
- Restored the
Preview (7889cd9d3c)
diff --git a/docs/architecture/encode-architecture-problem-and-gaps.md b/docs/architecture/encode-architecture-problem-and-gaps.md
new file mode 100644
--- /dev/null
+++ b/docs/architecture/encode-architecture-problem-and-gaps.md
@@ -1,0 +1,250 @@
+---
+uri: klappy://docs/architecture/encode-architecture-problem-and-gaps
+title: "Encode Architecture: Problem, Gaps, and Alternatives Analysis"
+audience: docs
+exposure: nav
+tier: 2
+voice: neutral
+stability: semi_stable
+tags: ["odd", "oddkit", "encode", "architecture", "vodka-architecture", "prompt-over-code", "alternative-d", "dolche", "dolcheo", "tsv", "governance", "design-brief", "epoch-8.4"]
+epoch: E0008.4
+date: 2026-04-30
+derives_from: "canon/definitions/dolcheo-vocabulary.md, canon/principles/prompt-over-code.md, odd/encoding-types/how-to-write-encoding-types.md, odd/encoding-types/serialization-format.md"
+complements: "odd/encoding-types/decision.md, odd/encoding-types/observation.md, odd/encoding-types/learning.md, odd/encoding-types/constraint.md, odd/encoding-types/handoff.md, odd/encoding-types/open.md, odd/encoding-types/encode.md"
+governs: "Implementation brief for the oddkit_encode vodka refactor (Phase 2 of E0008.4). Names the problem, evaluates alternatives, recommends Alternative D — Governance-defined field schemas with format-agnostic serialization."
+provenance: "Originated in klappy/truthkit-kb at docs/architecture/encode-architecture-problem-and-gaps.md (commit prior to 2026-04-16). Migrated verbatim to klappy.dev as the implementation brief for the oddkit encode refactor, with frontmatter added for canon discoverability. The TruthKit-KB origin is preserved in repo history; this doc is now the oddkit-canonical version."
+status: active
+---
+
+# Encode Architecture: Problem, Gaps, and Alternatives Analysis
+
+> The encode tool's parser and the governance it claims to serve are completely disconnected. The parser recognizes four English keywords via hardcoded regex. The governance defines six extensible dimensions via canon articles. The model does all the categorization work — and the parser throws it away. The fix must make governance the source of truth for encoding behavior: the server searches canon at encode time, dynamically builds extraction patterns from what it finds, teaches the calling model how to structure input, and supports ad-hoc types that any knowledge base can define without server changes.
+
+---
+
+## The Problem in One Sentence
+
+The encode tool's type detection is hardcoded in TypeScript while the governance it should implement lives in markdown — a direct violation of prompt over code.
+
+---
+
+## Current State — What the Code Actually Does
+
+`detectEncodeType()` in `orchestrate.ts` (lines 256–266):
+
+```typescript
+function detectEncodeType(input: string): string {
+ if (/\b(decided|decision|chose|choosing|selected|committed to|going with)\b/i.test(input))
+ return "decision";
+ if (/\b(learned|insight|realized|discovered|found that|turns out)\b/i.test(input))
+ return "insight";
+ if (/\b(boundary|limit|constraint|rule|prohibition|must not|never)\b/i.test(input))
+ return "boundary";
+ if (/\b(override|exception|despite|even though|notwithstanding)\b/i.test(input))
+ return "override";
+ return "decision";
+}
+```
+
+The entire encode handler then produces **one artifact** with: one type, one title, one quality score, one rationale extraction, one set of constraints. Everything the model sends — regardless of how many DOLCHE dimensions it contains — collapses into a single blob typed as "decision," "insight," "boundary," or "override."
+
+---
+
+## Gap 1 — Parser Vocabulary ≠ Governance Vocabulary
+
+The parser knows four types: `decision`, `insight`, `boundary`, `override`.
+
+The DOLCHE vocabulary doc (`docs/oddkit/proactive/dolche-vocabulary.md`) defines six dimensions: **D**ecisions, **O**bservations, **L**earnings, **C**onstraints, **H**andoffs, **E**ncodes. The vocabulary doc explicitly states: "The type field is a string, not an enum. Any knowledge base can extend DOLCHE with custom types by adding a governance document."
+
+These two systems share zero coordination:
+
+| Governance says | Parser recognizes | Alignment |
+|---|---|---|
+| Decision (D) | "decision" | Partial — different trigger words |
+| Observation (O) | — | **Missing entirely** |
+| Learning (L) | "insight" | Renamed — "insight" ≠ "learning" |
+| Constraint (C) | "boundary" | Renamed — "boundary" ≠ "constraint" |
+| Handoff (H) | — | **Missing entirely** |
+| Encode (E) | — | Meta-level, not a content type |
+| Custom (any) | — | **No extension mechanism** |
+| — | "override" | **Not in DOLCHE at all** |
+
+The parser invented "override" (not in DOLCHE) and renamed two types ("insight" for Learning, "boundary" for Constraint). Two DOLCHE types (Observation, Handoff) have no parser representation at all. The governance vocabulary and the server vocabulary diverged silently — and no mechanism detects or prevents the drift.
+
+## Gap 2 — Single Blob Output for Multi-Dimensional Input
+
+When a model calls encode with a full DOLCHE session capture — say, 5 decisions, 3 observations, 2 learnings, 4 constraints, 3 handoffs — the handler produces **one artifact**. One title (extracted from the first sentence). One type (the first regex match). One quality score (computed against the entire input string).
+
+The DOLCHE from the last session (attached to this conversation) contained 20+ categorized items across 6 types. The encode parser would have collapsed all of it into a single "decision" artifact scored against the entire blob. The model did all the extraction work. The server discarded it.
+
+This isn't just wasteful — it's architecturally backwards. The model has the intelligence to categorize. The server has the structure to store per-type artifacts. But the interface between them is a single unstructured string, parsed by regex that doesn't match the vocabulary, producing one output where many are needed.
+
+## Gap 3 — No Discovery Mechanism for Governance
+
+The encode handler never searches canon. It never fetches the DOLCHE vocabulary doc. It never looks for custom type definitions. The governance articles exist and are thoroughly written — but the server code that should implement them doesn't know they exist and has no mechanism to find them.
+
+This is the prompt-over-code violation in its purest form. The canon says the vocabulary is extensible via governance documents. The server says the vocabulary is four hardcoded regex patterns. When the governance changes — as it already did when OLDC+H became DOLCHE — the server doesn't know and can't adapt.
+
+## Gap 4 — The Model Can't Learn From the Governance
+
+The encode tool description currently says: "Standard artifact types: Observations (O), Learnings (L), Decisions (D), Constraints (C), Handoffs (H) — OLDC+H."
+
+This is a static string. It was written by a human, committed to the server codebase, and will drift from the governance vocabulary the moment the governance changes. It already has — it says OLDC+H while the governance says DOLCHE. It doesn't mention custom types. It doesn't teach the model how to structure input for optimal extraction. It doesn't know about extensions a specific knowledge base might define.
+
+The model reads this description once and improvises from there. The result: every encode call is a negotiation between what the model guesses the tool wants and what the regex actually matches. The governance has the answers — type definitions, trigger phrases, structural guidance — but the model never sees them.
+
+## Gap 5 — Quality Scoring Is Monolithic
+
+The quality scorer checks for: word count ≥ 10, rationale present, constraints present, alternatives mentioned, reversibility noted. This produces one score (0–5) for the entire input.
+
+But a DOLCHE capture has per-type quality criteria. A Decision without rationale is weak. An Observation without evidence is speculation. A Handoff without next-actions is incomplete. A Constraint without enforcement is a suggestion. These are different quality dimensions that require different scoring criteria — criteria that the governance docs already define but the scorer doesn't read.
+
+## Gap 6 — Ad-Hoc Types Are Governance Fiction
+
+The DOLCHE vocabulary doc says: "A pastoral knowledge base might add 'P' for Prayer Requests." This is currently a governance aspiration with zero server support. There is no mechanism for:
+
+1. The server to discover that a KB defines custom types
+2. The parser to recognize input that matches custom type patterns
+3. The model to learn about custom types from the KB's governance
+4. Quality scoring to apply custom criteria to custom types
+
+The extensibility promise is real in the governance layer and fictional in the server layer.
+
+---
+
+## Alternatives Analysis
+
+### Alternative A: Expand the Hardcoded Regex
+
+Add regex patterns for Observation, Learning, Constraint, Handoff, and Encode to `detectEncodeType()`. Map them to DOLCHE letters.
+
+**Pros:** Minimal code change. Ships today.
+
+**Cons:** Still violates prompt over code — every vocabulary change requires a server deployment. Still produces single-blob output. Still can't handle custom types. Still doesn't teach the model. The regex grows but the architecture doesn't improve. This is the fix that works for DOLCHE and breaks for every extension.
+
+**Verdict:** Solves today's naming mismatch. Creates tomorrow's maintenance burden. Scales linearly with vocabulary size.
+
+### Alternative B: Let the Model Do All the Work via Tool Description
+
+Make the tool description extremely detailed — include all type definitions, trigger words, structural guidance, quality criteria. Tell the model to produce structured JSON with per-type artifacts. The server becomes a passthrough that validates and returns whatever the model sends.
+
+**Pros:** Zero server changes. Pure prompt-over-code. The model is the intelligence layer.
+
+**Cons:** Tool descriptions have token limits. Stuffing the entire DOLCHE vocabulary + quality criteria + custom type definitions into a tool description is fragile and bloats every MCP handshake. The description becomes stale the moment governance changes. Different MCP platforms truncate descriptions at different lengths. The model still has to guess what custom types exist.
+
+**Verdict:** Right direction, wrong layer. The intelligence should be in the model, but the governance should be surfaced dynamically — not embedded statically.
+
+### Alternative C: Encode Calls Search Internally
+
+At encode time, the server searches its own canon for governance documents tagged with encoding vocabulary. It finds the DOLCHE vocabulary doc, any custom type definitions, and any quality criteria docs. It includes these in the encode response, teaching the calling model how to structure a re-invocation or how to structure future encode calls.
+
+**Pros:** Dynamic — governance changes propagate automatically. Extensible — custom types surface from KB governance, not server code. The server stays thin (search is already a capability). Mirrors how `telemetry_policy` works — governance fetched from canon at runtime.
+
+**Cons:** Adds latency to encode (one search round-trip). First encode call in a session won't benefit from the teaching unless the model re-invokes. Doesn't solve the single-blob problem on its own — the response format still needs to support multiple artifacts.
+
+**Verdict:** Strong for discovery and teaching. Insufficient alone for extraction.
+
+### Alternative D: Governance-Defined Field Schemas with Format-Agnostic Serialization (Proposed)
+
+The server searches canon at encode time for governance documents that define encoding types. Each type's governance doc defines field semantics: type letter, type name, field schema, quality criteria, trigger words (for fallback). A separate serialization format governance doc defines how fields are serialized (default: TSV). The model outputs structured rows — one per artifact, typed by the first field. The server parses mechanically using the serialization format. For unstructured input, the server falls back to paragraph splitting and dynamic regex classification. The encode response surfaces the governance, teaching the model for subsequent calls. Type definitions and serialization format evolve independently.
+
+**Architecture:**
+
+1. **At encode time:** Server searches canon for docs tagged as encoding-type governance, plus serialization format governance.
+2. **From each type doc:** Extract type letter, field schema, quality criteria, trigger words.
+3. **Parse structured input:** Using the serialization format, split into rows and fields. First field is the type letter. Remaining fields defined per-type by governance.
+4. **Fallback:** If input isn't structured, split by paragraph, classify against governance-derived regex from trigger words.
+5. **Score per-type:** Apply governance-defined quality criteria to the fields of each typed row.
+6. **Return per-type artifacts:** Multiple artifacts in markdown stream, each with its own type, title, quality score, and gaps.
+7. **Teach the model:** Response includes governance definitions — type letters, field schemas, quality criteria, serialization format, custom types. Model learns.
+
+**Pros:**
+- **Pure prompt over code:** Adding a type means writing a governance doc, not changing server code. Changing the format means updating one format doc, not every type doc.
+- **Self-teaching:** The model learns the vocabulary and format from governance docs surfaced in the response.
+- **Extensible:** Custom types work identically to default types — governance doc in, parsing out.
+- **Per-type quality:** Each type has its own field schema and quality criteria from its own governance doc.
+- **Mechanical parsing:** Serialization parsing is string splitting. No regex on the primary path. Near-zero compute.
+- **Minimizes model capacity requirements:** The model is already restructuring content for encode. Adding field structure is trivial for any LLM.
+- **Server stays thin:** Search + format parsing is generic infrastructure. The server doesn't know what DOLCHE means — it knows how to read governance docs and parse typed rows.
+- **Graceful degradation:** Unstructured input falls back to paragraph + regex classification. The response teaches the format. The model converges.
+- **Independent axes:** Type semantics and serialization format are governed by separate docs. Either changes without affecting the other.
+
+**Cons:**
+- Governance docs need a structured format for field schemas and quality criteria (adds a writing convention).
+- First encode in a session may be unstructured (model hasn't learned yet). Fallback handles this.
+
+**Verdict:** This is the architecture that makes governance the single source of truth for encoding behavior. It scales to any vocabulary size, supports ad-hoc types without server changes, teaches the model from the canon, and keeps the server thinner than the current hardcoded approach.
+
+### Alternative E: LLM-in-the-Loop Encoding
+
+Add LLM inference to the encode handler. The server sends the input + governance docs to a model, which produces structured per-type artifacts. The server validates and returns them.
+
+**Pros:** Highest extraction quality. The model understands nuance, context, and ambiguity.
+
+**Cons:** Adds inference latency (seconds, not milliseconds). Adds cost per encode call. Breaks the 0ms encode characteristic. Creates a dependency on model availability. Violates the Vodka Architecture principle of thin, stateless servers. The server becomes an inference orchestrator.
+
+**Verdict:** Right for TruthKit (where the harness governs LLM invocation). Wrong for oddkit (where the server must stay thin and stateless). This is the graduation path, not the current path.
+
+---
+
+## Recommendation
+
+**Alternative D — Governance-defined field schemas with format-agnostic serialization** — is the proposal that beats all others. The model is already restructuring content when it calls encode. Encoding-type governance docs define field semantics per type. A separate serialization format doc defines how fields are serialized (default: TSV). The server parses mechanically. No regex on the primary path. Dynamic regex from governance-defined trigger words handles unstructured fallback. The response teaches the model both the vocabulary and the format. The model converges.
+
+The key insight: the server doesn't need to understand DOLCHE. It needs to understand *how to read governance docs that define encoding types and parse typed rows against their field schemas*. Two independent governance layers — type semantics and serialization format — give maximum flexibility with minimum coupling. The server is the enforcer. The canon is the law.
+
+---
+
+## Resolved Design Questions
+
+1. **Governance doc format:** Separate docs per type — antifragile and easier to find via BM25. The DOLCHE vocabulary doc remains as narrative reference linking to them. Each type governance doc defines: type letter, type name, field schema, quality criteria. Serialization format (TSV default) is governed by a separate doc so types and format evolve independently.
+
+2. **Input format:** Governed by a separate serialization format doc (default: TSV). The model is already restructuring raw conversation into encode input — it's already doing the categorization work. The serialization format is independent of the type definitions so either can change without affecting the other.
+
+3. **Collision handling:** A single encode call can contain multiple rows of different types. Each row is independently typed. Multi-typing happens at the row level — the model decides a concept warrants both a D and a C row by emitting two rows. The model uses judgment; the server parses mechanically.
+
+4. **Response shape:** Markdown stream with per-type sections, each with quality score, gaps, and suggestions. Governance definitions included in the response to teach the model.
+
+5. **Caching:** Module memory cache is already 0ms for cached articles. Governance docs for encoding types are cached identically to all other canon files. No special caching strategy needed — solved infrastructure.
+
+6. **Backward compatibility:** Non-issue. Consumers are LLMs. Dynamic MCP tool usage is expected to change between sessions. Models re-read tool descriptions and response shapes every invocation.
+
+---
+
+## Refined Architecture — Governance-Defined Field Schemas with Format-Agnostic Serialization
+
+The model is already restructuring content when it calls encode. Two independent governance layers define the behavior: encoding-type docs define field schemas (what fields exist per type, quality criteria), and a serialization format doc defines how those fields are serialized (default: TSV). Either layer can change independently.
+
+**Flow:**
+
+1. **At encode time:** Server searches canon for docs tagged as encoding-type governance (or retrieves from 0ms module cache after first fetch). Also reads serialization format governance.
+2. **From each encoding-type doc:** Extract type letter, field schema, quality criteria.
+3. **Parse structured input:** Using the serialization format, split input into rows and fields. First field is the type letter. Remaining fields are defined per-type by governance.
+4. **Per-type quality scoring:** Each type's governance doc defines its own quality criteria applied to its own fields. A Decision without rationale is weak. An Observation without evidence is speculation. A Handoff without next-actions is incomplete. Different types, different fields, different standards.
+5. **Teach the model:** Encode response includes the governance definitions — type letters, field schemas, quality criteria, serialization format, and any custom types. The model learns from the canon, not from the tool description.
+6. **Per-type artifacts in markdown stream:** Response contains per-type sections, each with its own title, quality score, gaps, and suggestions.
+7. **Custom types work identically:** A KB adds a governance doc for "P — Prayer Requests" with its own field schema and quality criteria. Next encode call discovers it, parses P-typed rows against that schema, scores accordingly. No server change.
+
+**Fallback for unstructured input:** If the input isn't in the expected serialization format (models that haven't learned it yet), fall back to paragraph splitting + dynamic regex classification from governance-defined trigger words. The response teaches the format. The model converges on subsequent calls.
+
+**The self-teaching loop:**
+
+First encode call → may be unstructured → server classifies via fallback, surfaces governance with field schemas and serialization format → model learns → subsequent calls are well-structured → server parses mechanically → per-type quality feedback → extraction quality improves within the session.
+
+**What the server does NOT do:**
+
+- LLM inference
+- Domain-specific logic
+- Type definition (that's encoding-type governance)
+- Field schema definition (that's encoding-type governance)
+- Quality criteria definition (that's encoding-type governance)
+- Serialization format definition (that's format governance)
+
+**What the server DOES do:**
+
+- Search canon for encoding-type governance docs and serialization format governance
+- Parse input against governance-defined field schemas using governance-defined format
+- Fall back to paragraph split + dynamic regex for unstructured input
+- Score per-type quality against governance-defined criteria
+- Surface governance in the response to teach the model
+- Return per-type artifacts in markdown stream
diff --git a/odd/encoding-types/constraint.md b/odd/encoding-types/constraint.md
--- a/odd/encoding-types/constraint.md
+++ b/odd/encoding-types/constraint.md
@@ -6,24 +6,28 @@
tier: 2
voice: neutral
stability: semi_stable
-tags: ["odd", "oddkit", "encode", "dolche", "constraint", "encoding-type"]
-epoch: E0008
-date: 2026-04-15
-derives_from: "docs/oddkit/proactive/dolche-vocabulary.md"
+tags: ["odd", "oddkit", "encode", "dolche", "dolcheo", "constraint", "encoding-type", "tsv", "governance", "epoch-8.4"]
+epoch: E0008.4
+date: 2026-04-30
+derives_from: "canon/definitions/dolcheo-vocabulary.md, docs/architecture/encode-architecture-problem-and-gaps.md, canon/principles/prompt-over-code.md"
+complements: "odd/encoding-types/decision.md, odd/encoding-types/observation.md, odd/encoding-types/learning.md, odd/encoding-types/handoff.md, odd/encoding-types/open.md, odd/encoding-types/encode.md, odd/encoding-types/how-to-write-encoding-types.md, odd/encoding-types/serialization-format.md"
governs: "oddkit_encode parsing and quality scoring for type C"
status: active
---
+
# Encoding Type: Constraint (C)
-> What now governs future work. Rules, boundaries, and non-negotiables.
+> What now governs future work. Rules, boundaries, and non-negotiables that emerged from the session. Constraints bind future behavior — they are the artifacts most likely to prevent future mistakes. A constraint without enforcement is a suggestion.
---
## Summary — The Binding Layer
-Constraints bind future behavior and prevent repeated mistakes. A constraint without enforcement is a suggestion.
+Constraints are the artifacts that prevent future mistakes by binding future behavior. They emerge from decisions, observations, and learnings — but once established, they outlive the context that created them. A constraint from six months ago still governs today's work even if nobody remembers why.
+The key discipline: constraints define what cannot be done, not just what should be done. "Use TSV for encode input" is a decision. "The tool description must never hardcode specific keywords" is a constraint — it binds all future work regardless of context.
+
---
## Type Identity
@@ -32,21 +36,76 @@
|---|---|
| Letter | C |
| Name | Constraint |
+| Priority | High — constraints bind future behavior and prevent repeated mistakes |
---
## Field Schema
+When encoding a Constraint, the model outputs a row with the following fields (serialization format governed by `odd/encoding-types/serialization-format.md`):
+
+```
+C {title} {body} {origin} {scope}
+```
+
| Field | Recommended | Description |
|---|---|---|
| type | yes | Always `C` |
-| title | yes | Short summary of the constraint |
-| body | yes | What is bound, limited, or prohibited |
+| title | yes | Short summary of the constraint (≤12 words) |
+| body | yes | The constraint statement — what is bound, limited, or prohibited |
+| origin | no | What decision, observation, or learning produced this constraint |
+| scope | no | "permanent", "until {condition}", "this project", "this epoch", or empty |
+Example:
+
+```
+C Server must never hardcode encoding type keywords The encode tool description and server code must never hardcode specific type keywords because the DOLCHE vocabulary is extensible via governance and hardcoding creates drift between code and canon. D: Governance-defined TSV contract for encode input permanent
+```
+
---
## Trigger Words (Fallback Classification)
+When encode input is unstructured (not TSV), these trigger words classify a paragraph as Constraint:
+-must, must not, shall, never, always, required, prohibited, constraint, cannot
+must, must not, shall, shall not, never, always, required, prohibited, constraint, cannot, non-negotiable, boundary, rule, forbidden, mandatory
+
+---
+
+## Quality Criteria
+
+Each criterion adds 1 to the quality score (max 4):
+
+| Criterion | Check | Gap message if missing |
+|---|---|---|
+| Substance | Body is ≥10 words | "Constraint is too brief — expand what is bound" |
+| Clarity | Body contains a clear prohibition or requirement (must/must not/never/always) | "Make the constraint explicit — what must or must not happen?" |
+| Origin | Origin column is non-empty | "What produced this constraint? Link to the decision or observation" |
+| Scope | Scope column is non-empty | "Is this permanent, temporary, or scoped to a specific context?" |
+
+Quality levels:
+
+| Score | Level | Status |
+|---|---|---|
+| 4 | strong | recorded |
+| 3 | adequate | recorded |
+| 2 | weak | draft |
+| 0–1 | insufficient | draft |
+
+---
+
+## What Makes a Good Constraint Encoding
+
+A strong Constraint answers: what is bound, why it's bound (origin), and how long it's bound (scope). The most common gap is missing scope — constraints without expiration or context become invisible governance debt. "Never do X" is stronger than "don't do X" but "never do X because Y, permanent" is strongest.
+
+The second most common gap is constraints that read like preferences. "We should use TypeScript" is a preference. "All server code must be TypeScript because the CI pipeline only compiles TS" is a constraint — it names the enforcement mechanism.
+
+---
+
+## See Also
+
+- [DOLCHE Vocabulary](klappy://docs/oddkit/proactive/dolche-vocabulary) — the six-dimension framework this type belongs to
+- [Encoding Type: Decision](klappy://odd/encoding-types/decision) — decisions often produce constraints
+- [Prompt Over Code](klappy://canon/principles/prompt-over-code) — why this governance doc exists instead of server code
diff --git a/odd/encoding-types/decision.md b/odd/encoding-types/decision.md
--- a/odd/encoding-types/decision.md
+++ b/odd/encoding-types/decision.md
@@ -6,23 +6,25 @@
tier: 2
voice: neutral
stability: semi_stable
-tags: ["odd", "oddkit", "encode", "dolche", "decision", "encoding-type"]
-epoch: E0008
-date: 2026-04-15
-derives_from: "docs/oddkit/proactive/dolche-vocabulary.md"
+tags: ["odd", "oddkit", "encode", "dolche", "dolcheo", "decision", "encoding-type", "tsv", "governance", "epoch-8.4"]
+epoch: E0008.4
+date: 2026-04-30
+derives_from: "canon/definitions/dolcheo-vocabulary.md, docs/architecture/encode-architecture-problem-and-gaps.md, canon/principles/prompt-over-code.md"
+complements: "odd/encoding-types/observation.md, odd/encoding-types/learning.md, odd/encoding-types/constraint.md, odd/encoding-types/handoff.md, odd/encoding-types/open.md, odd/encoding-types/encode.md, odd/encoding-types/how-to-write-encoding-types.md, odd/encoding-types/serialization-format.md"
governs: "oddkit_encode parsing and quality scoring for type D"
status: active
---
+
# Encoding Type: Decision (D)
-> What was chosen. Explicit commitments with rationale that close options and create direction.
+> What was chosen. Explicit commitments with rationale. Decisions close options and create direction. They are the highest-stakes artifacts because they constrain all subsequent work. A decision without rationale is a debt. A decision without a constraint test is untested.
---
## Summary — The Highest-Stakes Artifact Type
-Decisions close options and create direction. They constrain all subsequent work, making them the most important artifacts to capture.
+Decisions are the encoding type most likely to affect future work. A missed observation can be recovered from the transcript. A missed decision may not surface again. Decisions create direction, close options, and bind future behavior. They deserve the most rigorous quality criteria of any encoding type.
---
@@ -32,21 +34,82 @@
|---|---|
| Letter | D |
| Name | Decision |
+| Priority | Highest — decisions constrain all subsequent work |
---
## Field Schema
+When encoding a Decision, the model outputs a row with the following fields (serialization format governed by `odd/encoding-types/serialization-format.md`):
+
+```
+D {title} {body} {rationale} {alternatives} {reversibility}
+```
+
| Field | Recommended | Description |
|---|---|---|
| type | yes | Always `D` |
-| title | yes | Short summary of the decision |
-| body | yes | What was chosen and why |
+| title | yes | Short summary of the decision (≤12 words) |
+| body | yes | The decision statement — what was chosen and why it matters |
+| rationale | yes | Why this was chosen. Starts with the reasoning, not "because" |
+| alternatives | no | What else was considered. Empty string if none discussed |
+| reversibility | no | "reversible", "permanent", "reversible until {condition}", or empty |
+Example:
+
+```
+D TSV as encode input format Governance defines strict TSV output contract for encode input. Model outputs typed rows, server parses mechanically. TSV is pure string splitting — no regex, no NLP. Model is already restructuring content. Adding column structure is trivial. Free-form prose with regex classification; JSON structured output; model-labeled paragraphs reversible
+```
+
---
## Trigger Words (Fallback Classification)
+When encode input is unstructured (not TSV), these trigger words classify a paragraph as Decision:
+
-decided, decision, chose, choosing, selected, committed to, going with
+decided, decision, chose, choosing, selected, committed to, going with, will use, adopted, settled on, picked, determined, resolved to
+
+These words are used by the server to build dynamic regex for fallback classification only. On the primary TSV path, the type letter `D` is the classifier.
+
+---
+
+## Quality Criteria
+
+Each criterion adds 1 to the quality score (max 5):
+
+| Criterion | Check | Gap message if missing |
+|---|---|---|
+| Substance | Body is ≥10 words | "Decision body is too brief — expand what was chosen" |
+| Rationale | Rationale column is non-empty and ≥3 words | "No rationale — add why this was chosen" |
+| Alternatives | Alternatives column is non-empty | "No alternatives considered — what else was an option?" |
+| Reversibility | Reversibility column is non-empty | "Note whether this is reversible or permanent" |
+| Constraints | Body or rationale mentions what this decision constrains or binds | "What constraints does this decision create?" |
+
+Quality levels:
+
+| Score | Level | Status |
+|---|---|---|
+| 5 | strong | recorded |
+| 3–4 | adequate | recorded |
+| 2 | weak | draft |
+| 0–1 | insufficient | draft |
+
+---
+
+## What Makes a Good Decision Encoding
+
+A strong Decision encoding answers five questions: What was chosen? Why? What else was considered? Is it reversible? What does it constrain? The model doesn't need to answer all five — but quality scoring rewards completeness.
+
+The most common gap is missing rationale. Models often encode the what without the why. The quality feedback loop teaches the model to include rationale in future calls.
+
+The second most common gap is missing alternatives. A decision without alternatives is an assertion, not a choice. Even "no alternatives were discussed" is better than silence — it's honest about the decision's context.
+
+---
+
+## See Also
+
+- [DOLCHE Vocabulary](klappy://docs/oddkit/proactive/dolche-vocabulary) — the six-dimension framework this type belongs to
+- [Prompt Over Code](klappy://canon/principles/prompt-over-code) — why this governance doc exists instead of server code
+- [Encode Does Not Persist](klappy://docs/oddkit/proactive/encode-does-not-persist) — the caller must save encoded artifacts
diff --git a/odd/encoding-types/handoff.md b/odd/encoding-types/handoff.md
--- a/odd/encoding-types/handoff.md
+++ b/odd/encoding-types/handoff.md
@@ -6,24 +6,28 @@
tier: 2
voice: neutral
stability: semi_stable
-tags: ["odd", "oddkit", "encode", "dolche", "handoff", "encoding-type"]
-epoch: E0008
-date: 2026-04-15
-derives_from: "docs/oddkit/proactive/dolche-vocabulary.md"
+tags: ["odd", "oddkit", "encode", "dolche", "dolcheo", "handoff", "encoding-type", "tsv", "governance", "epoch-8.4"]
+epoch: E0008.4
+date: 2026-04-30
+derives_from: "canon/definitions/dolcheo-vocabulary.md, docs/architecture/encode-architecture-problem-and-gaps.md, canon/principles/prompt-over-code.md"
+complements: "odd/encoding-types/decision.md, odd/encoding-types/observation.md, odd/encoding-types/learning.md, odd/encoding-types/constraint.md, odd/encoding-types/open.md, odd/encoding-types/encode.md, odd/encoding-types/how-to-write-encoding-types.md, odd/encoding-types/serialization-format.md"
governs: "oddkit_encode parsing and quality scoring for type H"
status: active
---
+
# Encoding Type: Handoff (H)
-> What comes next and what context the next session needs.
+> What comes next and what context the next session needs. Explicit transfer of state across conversation boundaries. Handoffs are the artifacts most likely to be lost because they describe what hasn't happened yet. A session without handoffs forces the next session to reconstruct context from scratch.
---
## Summary — The Continuity Layer
-Handoffs transfer state across session boundaries. A session without handoffs forces the next session to reconstruct context from scratch.
+Handoffs are how work survives across session boundaries. Every conversation ends. The question is whether the next one starts from zero or starts from where this one left off. Handoffs answer that question by explicitly naming: what's next, what's blocked, what context is needed, and who owns it.
+The key discipline: handoffs describe the future, not the past. "We decided to use TSV" is a decision. "Next session: write the remaining four encoding-type governance docs" is a handoff — it transfers intent across a boundary.
+
---
## Type Identity
@@ -32,21 +36,76 @@
|---|---|
| Letter | H |
| Name | Handoff |
+| Priority | High — handoffs are the most frequently lost artifact type |
---
## Field Schema
+When encoding a Handoff, the model outputs a row with the following fields (serialization format governed by `odd/encoding-types/serialization-format.md`):
+
+```
+H {title} {body} {blocked_by} {owner}
+```
+
| Field | Recommended | Description |
|---|---|---|
| type | yes | Always `H` |
-| title | yes | Short summary of what comes next |
-| body | yes | What needs to happen and what context is needed |
+| title | yes | Short summary of what comes next (≤12 words) |
+| body | yes | What needs to happen and what context is needed to do it |
+| blocked_by | no | What must happen before this can proceed. Empty if unblocked |
+| owner | no | Who owns this next action — a person, role, or "next session" |
+Example:
+
+```
+H Write O, L, C, H encoding-type governance docs Create the remaining four default encoding-type governance docs following the Decision template. Each needs type identity, TSV schema, trigger words, and quality criteria. next session
+```
+
---
## Trigger Words (Fallback Classification)
+When encode input is unstructured (not TSV), these trigger words classify a paragraph as Handoff:
+
-next session, next step, todo, follow up, blocked by, waiting on, continue, remaining, handoff
+next session, next step, todo, to do, follow up, blocked by, waiting on, needs to happen, pick up, continue, remaining, outstanding, handoff, hand off, carry forward, defer
+
+---
+
+## Quality Criteria
+
+Each criterion adds 1 to the quality score (max 4):
+
+| Criterion | Check | Gap message if missing |
+|---|---|---|
+| Substance | Body is ≥10 words | "Handoff is too brief — what context does the next session need?" |
+| Actionability | Body describes a concrete next action, not just a topic | "Make it actionable — what specifically needs to happen next?" |
+| Blocker clarity | Blocked_by column is non-empty OR body explicitly states unblocked | "Is this blocked by anything? State blockers or confirm unblocked" |
+| Ownership | Owner column is non-empty | "Who owns this? A person, role, or 'next session'" |
+
+Quality levels:
+
+| Score | Level | Status |
+|---|---|---|
+| 4 | strong | recorded |
+| 3 | adequate | recorded |
+| 2 | weak | draft |
+| 0–1 | insufficient | draft |
+
+---
+
+## What Makes a Good Handoff Encoding
+
+A strong Handoff answers: what needs to happen, what context is needed to do it, what's blocking it, and who owns it. The most common gap is handoffs that name a topic without naming an action — "encoding architecture" is a topic, "implement TSV parsing in the encode handler" is an action.
+
+The second most common gap is missing blockers. A handoff without blocker information forces the next session to rediscover dependencies. Even "unblocked" is valuable — it confirms the work can start immediately.
+
+---
+
+## See Also
+
+- [DOLCHE Vocabulary](klappy://docs/oddkit/proactive/dolche-vocabulary) — the six-dimension framework this type belongs to
+- [Proactive Session Close](klappy://docs/oddkit/proactive/proactive-session-close) — handoffs are central to session closing
+- [Prompt Over Code](klappy://canon/principles/prompt-over-code) — why this governance doc exists instead of server code
diff --git a/odd/encoding-types/learning.md b/odd/encoding-types/learning.md
--- a/odd/encoding-types/learning.md
+++ b/odd/encoding-types/learning.md
@@ -6,24 +6,28 @@
tier: 2
voice: neutral
stability: semi_stable
-tags: ["odd", "oddkit", "encode", "dolche", "learning", "encoding-type"]
-epoch: E0008
-date: 2026-04-15
-derives_from: "docs/oddkit/proactive/dolche-vocabulary.md"
+tags: ["odd", "oddkit", "encode", "dolche", "dolcheo", "learning", "encoding-type", "tsv", "governance", "epoch-8.4"]
+epoch: E0008.4
+date: 2026-04-30
+derives_from: "canon/definitions/dolcheo-vocabulary.md, docs/architecture/encode-architecture-problem-and-gaps.md, canon/principles/prompt-over-code.md"
+complements: "odd/encoding-types/decision.md, odd/encoding-types/observation.md, odd/encoding-types/constraint.md, odd/encoding-types/handoff.md, odd/encoding-types/open.md, odd/encoding-types/encode.md, odd/encoding-types/how-to-write-encoding-types.md, odd/encoding-types/serialization-format.md"
governs: "oddkit_encode parsing and quality scoring for type L"
status: active
---
+
# Encoding Type: Learning (L)
-> What was understood from the observations. Interpretation with evidence.
+> What was understood from the observations. Interpretation with evidence. Learnings connect observations to meaning — the bridge between "what did we see?" and "what does it mean?" A learning without an observation is speculation. A learning with an observation is knowledge.
---
## Summary — The Interpretation Layer
-Learnings connect observations to meaning. A learning without an observation is speculation. A learning with an observation is knowledge.
+Learnings are where observations become understanding. They require evidence — the observation that prompted the interpretation. Without that link, a learning is just an assertion dressed up as insight.
+The key discipline: learnings explain why, not just what. "The deploy took 47 seconds" is an observation. "Cold starts dominate deploy time because the first fetch always misses cache" is a learning — it names the mechanism and connects to evidence.
+
---
## Type Identity
@@ -32,21 +36,74 @@
|---|---|
| Letter | L |
| Name | Learning |
+| Priority | Medium — learnings inform future decisions but don't constrain them directly |
---
## Field Schema
+When encoding a Learning, the model outputs a row with the following fields (serialization format governed by `odd/encoding-types/serialization-format.md`):
+
+```
+L {title} {body} {evidence} {applicability}
+```
+
| Field | Recommended | Description |
|---|---|---|
| type | yes | Always `L` |
-| title | yes | Short summary of what was learned |
-| body | yes | What was understood and why it matters |
+| title | yes | Short summary of what was learned (≤12 words) |
+| body | yes | The learning — what was understood and why it matters |
+| evidence | no | The observation(s) that support this learning. References to O-typed entries or direct evidence |
+| applicability | no | "local" (this project only), "general" (applies broadly), or "provisional" (needs more evidence) |
+Example:
+
+```
+L Platform-agnostic identification requires URL-level mechanisms Headers are implementation details that vary by platform. URLs are universal. The consumer query param works everywhere because every MCP platform lets users edit URLs. O: Labeled consumers only got credit for MCP initialize events — headers weren't forwarded on tool calls general
+```
+
---
## Trigger Words (Fallback Classification)
+When encode input is unstructured (not TSV), these trigger words classify a paragraph as Learning:
+
-learned, realized, discovered, understood, found that, turns out, insight
+learned, realized, discovered, understood, found that, turns out, the reason is, this means, implies, suggests, the pattern is, insight, takeaway, the mechanism is, because
+
+---
+
+## Quality Criteria
+
+Each criterion adds 1 to the quality score (max 4):
+
+| Criterion | Check | Gap message if missing |
+|---|---|---|
+| Substance | Body is ≥10 words | "Learning is too brief — expand what was understood" |
+| Evidence | Evidence column is non-empty | "What observation supports this? A learning without evidence is speculation" |
+| Mechanism | Body explains why or how, not just what | "Add the mechanism — why does this happen, not just that it does" |
+| Applicability | Applicability column is non-empty | "Is this local to this project, generally applicable, or provisional?" |
+
+Quality levels:
+
+| Score | Level | Status |
+|---|---|---|
+| 4 | strong | recorded |
+| 3 | adequate | recorded |
+| 2 | weak | draft |
+| 0–1 | insufficient | draft |
+
+---
+
+## What Makes a Good Learning Encoding
+
+A strong Learning answers: what was understood, what evidence supports it, why it happens (the mechanism), and how broadly it applies. The most common gap is missing evidence — models often encode interpretations without citing the observations that prompted them. The second most common gap is asserting a pattern without explaining the mechanism.
+
+---
+
+## See Also
+
+- [DOLCHE Vocabulary](klappy://docs/oddkit/proactive/dolche-vocabulary) — the six-dimension framework this type belongs to
+- [Encoding Type: Observation](klappy://odd/encoding-types/observation) — the companion type that provides evidence for learnings
+- [Prompt Over Code](klappy://canon/principles/prompt-over-code) — why this governance doc exists instead of server code
diff --git a/odd/encoding-types/observation.md b/odd/encoding-types/observation.md
--- a/odd/encoding-types/observation.md
+++ b/odd/encoding-types/observation.md
@@ -6,25 +6,29 @@
tier: 2
voice: neutral
stability: semi_stable
-tags: ["odd", "oddkit", "encode", "dolche", "observation", "encoding-type"]
-epoch: E0008
-date: 2026-04-15
-derives_from: "docs/oddkit/proactive/dolche-vocabulary.md"
+tags: ["odd", "oddkit", "encode", "dolche", "dolcheo", "observation", "encoding-type", "tsv", "governance", "epoch-8.4"]
+epoch: E0008.4
+date: 2026-04-30
+derives_from: "canon/definitions/dolcheo-vocabulary.md, docs/architecture/encode-architecture-problem-and-gaps.md, canon/principles/prompt-over-code.md"
+complements: "odd/encoding-types/decision.md, odd/encoding-types/learning.md, odd/encoding-types/constraint.md, odd/encoding-types/handoff.md, odd/encoding-types/open.md, odd/encoding-types/encode.md, odd/encoding-types/how-to-write-encoding-types.md, odd/encoding-types/serialization-format.md"
governs: "oddkit_encode parsing and quality scoring for type O"
+fallback: true
status: active
-fallback: true
---
+
# Encoding Type: Observation (O)
-> What was seen or noticed. Raw facts without interpretation.
+> What was seen or noticed. Raw facts without interpretation. Observations are the evidence layer — they describe reality as encountered, not reality as theorized. An observation that nobody recorded is an observation that never happened for the system's purposes.
---
## Summary — The Evidence Layer
-Observations describe reality as encountered, not reality as theorized. They are the foundation that Learnings, Decisions, and Constraints build on.
+Observations are the foundation that all other DOLCHE types build on. Learnings interpret observations. Decisions respond to them. Constraints emerge from them. Without recorded observations, the rest is speculation.
+The key discipline: observations describe what happened, not what it means. "The deploy took 47 seconds" is an observation. "The deploy is too slow" is a learning. Keeping these separate matters because the same observation can support different interpretations. Recording the raw fact preserves optionality.
+
---
## Type Identity
@@ -33,28 +37,74 @@
|---|---|
| Letter | O |
| Name | Observation |
+| Priority | High — observations are the evidence that grounds all other types |
---
## Field Schema
+When encoding an Observation, the model outputs a row with the following fields (serialization format governed by `odd/encoding-types/serialization-format.md`):
+
+```
+O {title} {body} {source} {verifiability}
+```
+
| Field | Recommended | Description |
|---|---|---|
| type | yes | Always `O` |
-| title | yes | Short summary of what was observed |
-| body | yes | What was seen, measured, or encountered |
+| title | yes | Short summary of what was observed (≤12 words) |
+| body | yes | The observation — what was seen, measured, or encountered |
+| source | no | Where or how this was observed: direct measurement, log output, user report, conversation, etc. |
+| verifiability | no | "verified", "reported", "inferred", or empty. Distinguishes firsthand evidence from secondhand |
+Example:
... diff truncated: showing 800 of 1149 lines
You can send follow-ups to the cloud agent here.
Reviewed by Cursor Bugbot for commit 1195823. Configure here.
…why wasn't the journal included?'); L-03 graduated to C-03

Summary
Phase 1 of E0008.4 — the encode vodka refactor. Migrates the field-schema and quality-criteria governance from
klappy/truthkit-kbinto oddkit canon, copies the architecture brief verbatim, and lays down the implementation handoff for Phase 2.This is canon-only. No code changes. The follow-on PR in
klappy/oddkitwill refactor the parser to read this canon at runtime per Alternative D.What's in this PR
Replaced (5 files): the core encoding-type articles
The thinner versions in
odd/encoding-types/(1.1–1.6KB each) are replaced with TruthKit-KB's enriched versions (4.5–5.1KB each). Each now carries:Articles:
decision.md,observation.md,learning.md,constraint.md,handoff.md.Enriched in place (1 file):
open.mdKept oddkit's DOLCHEO seventh-letter framing (post-2026-04-19, with
facet=opendisambiguation from closed Observation). Ported TruthKit's pattern for Field Schema example, Quality Criteria, and the closing narrative. Did NOT copy TruthKit'squestion.md— that was the older pre-DOLCHEO framing, and the architectural pattern (not the framing) was what mattered.Synced (1 file):
serialization-format.mdSingle tag addition (
encoding-type) to align with TruthKit-KB. Body unchanged.New (2 files)
docs/architecture/encode-architecture-problem-and-gaps.md— verbatim copy of TruthKit-KB's architecture brief, with klappy.dev frontmatter added (including aprovenancefield documenting the migration). Diagnoses six gaps in the current encode parser, evaluates five alternatives, recommends Alternative D — Governance-defined field schemas with format-agnostic serialization.odd/handoffs/2026-04-30-encode-vodka-refactor-alternative-d.md— implementation handoff for Phase 2 in the oddkit repo. Names the scope, acceptance criteria, release-validation-gate requirements, observed pitfalls from the P1.3.x sweep, validator agent checklist, and successor closeout pattern.Why now
H-01 of
odd/ledger/2026-04-20-p1-3-4-encode-canon-parity-landednamed the gap explicitly: the encode parser hardcodes recognition of four English keywords (decision,insight,boundary,override) while DOLCHEO governance defines seven dimensions (D, O, L, C, H, E, Open) plus an extension mechanism. The matcher was synchronized with canon during the sweep; the parser remained disconnected from the vocabulary entirely. The architecture brief was already written inklappy/truthkit-kbbut unused by the consumer that needed it most.Phase 1 puts the governance where the parser will read it. Phase 2 (separate PR in
klappy/oddkit) makes the parser read it.Provenance
The architecture brief and the per-type field schemas / quality criteria originated in
klappy/truthkit-kb(private). Body content is migrated verbatim with klappy.dev frontmatter conventions added. Theprovenancefield on the architecture doc records the origin explicitly.Out of scope (call out for the next gate)
oddkit_encodetool description (that ships with the Phase 2 code refactor).Validation expected
This is canon-only — no Bugbot validator agent gate required (no orchestrate.ts / matcher / governance reads / envelope changes). Standard canon-doc gauntlet applies: frontmatter conventions, voice, claim auditability, cross-link integrity.
The Phase 2 PR will require the full release-validation-gate (Bugbot completed + Sonnet 4.6 read-only validator dispatched via Managed Agents before promotion merge), per
klappy://canon/constraints/release-validation-gate. The handoff document spells this out.Note
Low Risk
Docs/canon-only changes with no runtime behavior modifications; risk is limited to potential confusion if downstream Phase 2 code assumes different schemas or links.
Overview
Adds a new architecture brief (
encode-architecture-problem-and-gaps.md) that documents currentoddkit_encodeparser/governance mismatches and recommends Alternative D (governance-driven, TSV-based, multi-artifact encoding) as the Phase 2 implementation approach.Upgrades the canonical encoding-type docs for
Decision,Observation,Learning,Constraint,Handoff, andOpento include governance-ready TSV field schemas, expanded fallback trigger-word sets, and per-type quality scoring criteria/messages (with a small tag sync inserialization-format.md).Introduces a Phase 2 implementation handoff doc and a Phase 1 closeout ledger to track scope, acceptance criteria, and release-validation expectations for the upcoming
klappy/oddkitcode refactor (this PR itself is canon/docs-only).Reviewed by Cursor Bugbot for commit 3fc56fb. Bugbot is set up for automated code reviews on this repo. Configure here.