Private
Public Access
0
0
Files
manual_slop/conductor/code_styleguides/knowledge_artifacts.md
T
ed 35c6cca134 docs: agent workflow docs + regular docs (v2.3 surfacing)
Per user request 'use your remaining context to update agent workflow
docs and then regular docs based on what was discussed in this report',
this commit creates/updates 15 files derived from the v2.3 nagent
review (the 12 new nagent additions + the 4 memory dimensions
reframing + the cache strategy + the RAG discipline + the knowledge
harvest pattern).

Agent workflow docs (4 files):
- AGENTS.md (UPDATE): add @import line to canonical DOD + 'Code
  Styleguides' section pointing to the 6 new styleguides + new
  'Human-Facing Documentation' section pointing to ./docs/AGENTS.md
- conductor/workflow.md (UPDATE): new section 'Additions (2026-06-12)
  - the 12 patterns from the latest nagent corpus' with TDD
  protocols for knowledge harvest, cache ordering, compaction, RAG
  discipline
- conductor/product-guidelines.md (UPDATE): new sections 'Memory
  Dimensions (added 2026-06-12)' + 'See Also - Updated' with the
  6-styleguide catalog
- docs/AGENTS.md (NEW): the agent-facing mirror of docs/Readme.md
  (per the nagent CLAUDE.md pattern). 10 sections + the per-tier
  reading path + the 4 memory dimensions + the caching strategy +
  the knowledge harvest + the RAG discipline + the feature flags

Regular docs (11 files):
- 6 new styleguides (the convention catalog):
  * data_oriented_design.md: the canonical DOD reference (Tier
    0/1/2; 3 defaults to reject; 8 core defaults; 7-question
    simplification pass; 10-question self-check; 4 memory
    dimensions in Manual Slop context)
  * agent_memory_dimensions.md: the 4 memory dims (curation /
    discussion / RAG / knowledge) + when to use each + the
    boundaries
  * rag_integration_discipline.md: the conservative-RAG rule
    (opt-in, complement, provenance, no mutation, feature-gated,
    graceful failure)
  * cache_friendly_context.md: stable-to-volatile context
    ordering + the cache TTL GUI contract + the byte-comparison
    test
  * knowledge_artifacts.md: the knowledge harvest pattern
    (category files, provenance, sha256 ledger, digest
    regeneration, 'delete to turn off')
  * feature_flags.md: file presence vs config flags vs CLI flags
- 3 new project docs (the cross-cutting guides):
  * guide_agent_memory_dimensions.md: the cross-cutting guide on
    the 4 dims + the decision tree
  * guide_caching_strategy.md: caching across providers +
    stable-to-volatile ordering + cache TTL GUI + the byte-
    comparison test + the 5th provider (claude-code)
  * guide_knowledge_curation.md: the knowledge memory guide (4th
    dim) + the 5 category files + per-file notes + the digest +
    the ledger + the harvest workflow
- 2 existing doc updates:
  * guide_mma.md: new sections 'Delegation as context management'
    + 'The 4 memory dimensions (the MMA scope)'
  * guide_ai_client.md: new section 'Cache strategy and the 12-
    layer model' + the 5th provider (claude-code)

All files use the same style as the v2.3 review (the user's preferred
format): 7-column tables, no JSON, SSDL shape tags, forth/array
notation, file:line citations, ASCII sketches where useful. The
human Readme files (Readme.md, docs/Readme.md) are NOT modified
(per repeated user instruction).

The 5th provider (claude-code) is documented in guide_ai_client.md
+ the data_oriented_design.md references the nagent pattern as the
source of the canonical rules.

The cross-references are bidirectional: the 6 styleguides reference
the 3 project docs; the 3 project docs reference the 6 styleguides;
the 2 doc updates reference both; AGENTS.md + ./docs/AGENTS.md
provide the entry points.
2026-06-12 13:50:40 -04:00

17 KiB

Knowledge Artifacts (the harvest pattern)

Status: Styleguide; codifies the knowledge harvest pattern: category files, provenance, sha256 ledger, digest regeneration, "delete to turn off." Date: 2026-06-12 Cross-refs: conductor/code_styleguides/agent_memory_dimensions.md §4; conductor/code_styleguides/feature_flags.md; docs/guide_knowledge_curation.md; conductor/tracks/nagent_review_20260608/nagent_review_v2_3_20260612.md §3.1, §4.

What this is. The 4th memory dimension (per agent_memory_dimensions.md §4) is the durable, provenance-aware, user-editable knowledge store. It's a layer, not a snapshot: category files are the source of truth; the digest is a projection; the ledger is the audit log. This styleguide names the files, the formats, the harvest workflow, and the "delete to turn off" pattern.


0. The one-glance directory layout

~/.manual_slop/knowledge/
├── facts.md                      # - {statement} {provenance}
├── decisions.md                  # - {statement, reason} {provenance}
├── questions.md                  # - {question} {provenance}
├── playbooks.md                  # - **{name}**: {steps} {provenance}
├── tasks.md                      # ## Open / ## Done
├── files/
│   └── {file_id}.md              # per-file notes (keyed by inode)
├── digest.md                     # bounded 4KB; the projection; "delete to turn off"
├── ledger.json                   # sha256-of-content audit log
└── prompts/
    └── harvest-conversation.md   # user-editable harvest prompt

1. The category files (the source of truth)

1.1 facts.md (durable statements)

# Facts

- The MCP dispatch uses a flat if/elif chain. 4 places, 45 tools. [from: 2026-05-12-investigate-dispatch, 2026-05-12]
- ai_client.py has 5 separate per-provider history lists, each with their own lock. Switching providers mid-session loses history. [from: 2026-05-13-state-mutation-matrix, 2026-05-13]
- RAG is opt-in. Default-off in new projects. [from: 2026-06-12-rag-discipline, 2026-06-12]

The shape: - {statement} {provenance}. Plain markdown. Append-only. User-editable.

1.2 decisions.md (decisions with reasons)

# Decisions

- Knowledge harvest is a complement to curation + discussion, not a RAG replacement. [from: 2026-06-12-candidate-11, 2026-06-12]
- Cache TTL defaults to 5 min (Anthropic) + 60 min (Gemini); configurable per-discussion. [from: 2026-06-12-cache-strategy, 2026-06-12]

The shape: - {statement} {provenance}. The "why" lives in the LLM's harvest output; the user's edits override.

1.3 questions.md (unanswered questions)

# Questions

- Where does intent resolution live — per-verb, per-block, or global? [from: 2026-06-12-follow-up-b, 2026-06-12]
- How should the knowledge digest TTL be exposed in the GUI? [from: 2026-06-12-cache-ttl, 2026-06-12]

The shape: - {question} {provenance}. Open questions are valuable — they're the TODO list the next session can act on.

1.4 playbooks.md (reusable sequences)

# Playbooks

- **Knowledge Harvest**: scan -> classify -> LLM-distill -> append -> digest -> reclaim. [from: 2026-06-12-candidate-11, 2026-06-12]
- **Stable-to-Volatile Cache Ordering**: identify Instance: boundary -> pass to --cache-prefix-chars. [from: 2026-06-12-candidate-12, 2026-06-12]
- **Candidate Verification (TBD)**: read src/ai_client.py:run_discussion_compression -> check failure mode. [from: 2026-06-12-candidate-15, 2026-06-12]

The shape: - **{name}**: {steps} {provenance}. Playbooks are the "I did this once; here it is" record. Future workers use them directly.

1.5 tasks.md (open and done)

# Tasks

## Open
- Create canonical DOD file at conductor/code_styleguides/data_oriented_design.md. [from: 2026-06-12-candidate-16, 2026-06-12]
- Verify Candidate 15 by reading src/ai_client.py:run_discussion_compression. [from: 2026-06-12-candidate-15, 2026-06-12]

## Done
- Read nagent source in full (18 files). [from: 2026-05-15, 2026-05-15]
- Wrote v2.3 review (272KB / 3965 lines). [from: 2026-06-12-v2.3, 2026-06-12]

The shape: - {task} {provenance}. The two sections are manually maintained; the harvest places open items in ## Open and done items in ## Done.

1.6 files/{file_id}.md (per-file notes)

# /repo/src/ai_client.py

- Uses `cache_control: {"type": "ephemeral"}` blocks for Anthropic caching. [from: 2026-06-12-investigate-cache, 2026-06-12]
- The 5 per-provider history lists are gated by their own locks. [from: 2026-05-13-state-mutation-matrix, 2026-05-13]
- `run_discussion_compression` failure mode: TBD (Candidate 15). [from: 2026-06-12-candidate-15, 2026-06-12]

The shape: - {note} {provenance}. Keyed by file_id (the st_dev:st_ino of the file). Survives renames within the same filesystem.

The file_id pattern (per nagent's bin/helpers/nagent_file_edit_lib.py:file_id_for_path):

def file_id_for_path(path: Path) -> str:
    """Stable file identity across renames. Returns 'device:inode'."""
    stat = path.stat()
    return f"{stat.st_dev}:{stat.st_ino}"

The "files" category in the harvest output has a special branch: if the path resolves to an existing file, the note goes to knowledge/files/{file_id}.md; if not, the note falls back to facts.md as {path}: {note} {provenance}. The note survives, just loses the per-file binding.


2. The digest (digest.md)

The digest is a projection of the category files, bounded to 4KB. It's injected as the {knowledge} block in the initial context.

The format (per nagent's regenerate_digest):

# Knowledge digest
(regenerated by nagent-gc; edit the category files, not this file)

## Open tasks
- Create canonical DOD file at conductor/code_styleguides/data_oriented_design.md. [from: 2026-06-12-candidate-16, 2026-06-12]

## Open questions
- Where does intent resolution live — per-verb, per-block, or global? [from: 2026-06-12-follow-up-b, 2026-06-12]

## Decisions
- Knowledge harvest is a complement to curation + discussion, not a RAG replacement. [from: 2026-06-12-candidate-11, 2026-06-12]

## Facts
- nagent has 5 providers; Manual Slop has 8. [from: 2026-06-12-v2.3, 2026-06-12]

## Playbooks
- **Knowledge Harvest**: scan -> classify -> LLM-distill -> append -> digest -> reclaim. [from: 2026-06-12-candidate-11, 2026-06-12]

The ordering is fixed: Open tasks, Open questions, Decisions, Facts, Playbooks (per nagent's DIGEST_SECTIONS = (('Open tasks', 'tasks_open'), ('Open questions', 'questions'), ('Decisions', 'decisions'), ('Facts', 'facts'), ('Playbooks', 'playbooks'))).

Within each section, newest first (because the category files are append-only; reversing gives newest-first).

Truncation: if the sections don't fit in 4KB, the rest is truncated with a visible (truncated; see the category files for the rest) note.

"Delete to turn off": if all sections are empty, the digest is deleted:

# In regenerate_digest
if not sections:
    if target.is_file():
        target.unlink()    # delete to turn off
    return None

The injection point (in aggregate.py:run):

# In aggregate.py:run (the consumer of the digest)
knowledge_digest_path = paths.knowledge_dir() / "digest.md"
if knowledge_digest_path.is_file():
    knowledge_digest = knowledge_digest_path.read_text(encoding="utf-8")
    stable_prefix.append(f"{{knowledge}}\n{knowledge_digest}\n{{/knowledge}}\n")

3. The ledger (ledger.json)

The ledger is the sha256-of-content audit log. It gates deletion on a proven harvest.

The format:

{
  "entries": {
    "<sha256-of-conversation-content>": {
      "path": "/home/user/.nagent/conversations/<name>-<uuid>",
      "status": "harvested",
      "at": "2026-06-12T14:23:45.123456+00:00",
      "items": {
        "facts": 3,
        "decisions": 2,
        "tasks_done": 1,
        "tasks_open": 0,
        "questions": 1,
        "playbooks": 0,
        "files": 1
      },
      "deleted": true
    },
    "<sha256-of-another-conversation>": {
      "path": "...",
      "status": "harvest-failed",
      "at": "2026-06-12T14:24:00.000000+00:00",
      "deleted": false,
      "error": "provider 'openai' not available"
    }
  }
}

The status values:

Status Meaning Action
harvested LLM distillation succeeded; items appended to category files reclaim (unlink)
harvest-failed LLM distillation failed after retries keep the conversation; record the error
deleted-unharvested User passed --no-harvest; the conversation is reclaimed without LLM reclaim (unlink)
too-large File > 1MB; kept without harvesting keep

The sha256-of-content dedup: two conversations with the same content share a ledger entry. The second is reclaimed without paying the LLM cost again.


4. The harvest workflow

4.1 The 7-category schema (the LLM output)

The LLM's harvest output is strict JSON (no prose, no markdown fence):

{
  "facts": [
    {"statement": "The system has 4 memory dimensions", "detail": ""}
  ],
  "decisions": [
    {"statement": "Knowledge harvest is a complement to curation + discussion", "detail": "not a RAG replacement"}
  ],
  "tasks_done": [
    {"statement": "v2.3 review identified 10 future-track candidates", "detail": ""}
  ],
  "tasks_open": [
    {"statement": "Create canonical DOD file at conductor/code_styleguides/data_oriented_design.md", "detail": "Candidate 14"}
  ],
  "questions": [
    {"statement": "Where does intent resolution live — per-verb, per-block, or global?", "detail": ""}
  ],
  "playbooks": [
    {"name": "Knowledge Harvest", "steps": "scan -> classify -> LLM-distill -> append -> digest -> reclaim"}
  ],
  "files": [
    {"path": "/repo/src/ai_client.py", "note": "Cache TTL GUI: per-discussion state; cache hit rate per provider"}
  ]
}

The prompt (in prompts/harvest-conversation.md; user-editable, root-first resolution):

# Harvest durable knowledge from a manual_slop conversation

You are given one conversation (or a summary of one). Extract only knowledge that
stays useful after this conversation is deleted. Return only JSON in exactly this
form (no prose, no markdown fence):

[the 7-category schema above]

Category rules:
- facts: durable statements about systems, repositories, tools, environments, or
  constraints that were learned, not assumed.
- decisions: choices that were made, with the why in `detail`.
- tasks_done: concrete work completed in this conversation.
- tasks_open: work that was started, planned, or requested but not finished.
- questions: questions raised and never answered.
- playbooks: command sequences or processes that worked and are reusable; `steps`
  is the runnable sequence.
- files: a note tied to one specific file path (use the absolute path seen in
  the conversation).

General rules:
- Empty arrays are valid and expected: most conversations contain nothing durable.
  Do not invent items to fill categories.
- One item per distinct piece of knowledge; keep `statement` to one sentence.
- `detail` is optional context; omit it or use "" when the statement stands alone.
- Do not include conversation mechanics, tool output noise, retries, or one-off
  trivia (timestamps, token counts, transient errors).

4.2 The retry budget

HARVEST_MAX_ATTEMPTS = 2. The retry is at the parse level (not the API level):

def harvest_conversation(path, provider, model, config_path, *, generate, summarize=None):
    content = read_or_summarize(path, provider, model)
    template = harvest_prompt_path().read_text(encoding="utf-8").strip()
    last_error = None
    for attempt in range(HARVEST_MAX_ATTEMPTS):
        prompt = build_harvest_prompt(template, path.name, content, retry=attempt > 0)
        response = generate(prompt, provider, model)
        try:
            return parse_harvest_json(response)
        except (json.JSONDecodeError, ValueError) as exc:
            last_error = exc
    raise RuntimeError(f"harvest output invalid after {HARVEST_MAX_ATTEMPTS} attempts: {last_error}")

The retry-suffix: on retry, append \nYour previous reply was not valid JSON. Return only the JSON object.\n to the prompt. The LLM sees its previous (malformed) output and a one-line correction.

The strict parser (tolerates code-fence; otherwise strict):

def parse_harvest_json(text: str) -> dict:
    stripped = text.strip()
    fence = JSON_FENCE.match(stripped)        # tolerates ```json ... ```
    if fence:
        stripped = fence.group(1).strip()
    payload = json.loads(stripped)
    if not isinstance(payload, dict):
        raise ValueError("harvest output is not a JSON object")
    harvested = {}
    for category in ITEM_CATEGORIES:
        rows = payload.get(category, [])
        harvested[category] = rows if isinstance(rows, list) else []
    return harvested

4.3 The size limits (the budgets)

Constant Value Why
SUMMARIZE_THRESHOLD_BYTES 64 KB Files > 64KB get summarized first
MAX_HARVEST_SOURCE_BYTES 1 MB Files > 1MB are kept (not harvested)
DIGEST_MAX_BYTES 4 KB The bounded digest size
HARVEST_MAX_ATTEMPTS 2 Retry budget on parse failure

The "too-large" branch (the budget guard):

if artifact.size_bytes > MAX_HARVEST_SOURCE_BYTES:
    entries[sha] = {"status": "too-large", "deleted": False}
    emit(f"kept (too large): {label}")
    continue

4.4 The dry-run-by-default safety

The harvest CLI defaults to dry-run. Without --apply, the CLI classifies, estimates cost, and prints a report. No mutation.

$ python -m src.knowledge_harvest
artifacts: live:42, user-kept:3, prune:0, harvest:17, keep:1
harvest candidates: 2.3MB (~600K input tokens), prune candidates: 0B
dry run; pass --apply to harvest and reclaim

$ python -m src.knowledge_harvest --apply
reclaimed: 2.3MB
harvested items: facts:42, decisions:18, tasks_done:7, tasks_open:3, questions:5, playbooks:2, files:11
digest: /home/user/.manual_slop/knowledge/digest.md
ledger: /home/user/.manual_slop/knowledge/ledger.json

5. The "delete to turn off" pattern (per feature_flags.md)

The principle. Feature flags should be data, not config. If a feature is gated by the presence of a file, the user can turn it off by deleting the file. No GUI toggle, no env var, no config.toml edit. Just rm.

The knowledge harvest pattern: rm ~/.manual_slop/knowledge/digest.md → no {knowledge} block is injected. Re-enable by running python -m src.knowledge_harvest --apply (which regenerates the digest).

The implementation:

# In aggregate.py:run (the consumer)
knowledge_digest_path = paths.knowledge_dir() / "digest.md"
if knowledge_digest_path.is_file():
    knowledge_digest = knowledge_digest_path.read_text(encoding="utf-8")
    stable_prefix.append(f"{{knowledge}}\n{knowledge_digest}\n{{/knowledge}}\n")
# else: skip; the file is the switch

The general pattern recurs in 3 places:

  1. regenerate_digest deletes the digest when sections are empty
  2. The aggregate.py:run injection check is the load-bearing one
  3. The Knowledge panel shows the file state (so the user knows what to do)

The alternative (config toggle) is also supported: [ai_settings.knowledge].digest_enabled = false. See feature_flags.md for the rule on when to use file presence vs config.


6. The graceful failure modes

Failure Handling
LLM returns invalid JSON Retry (up to 2 attempts); on 2nd failure, mark harvest-failed in the ledger; keep the conversation
File > 1MB Mark too-large in the ledger; keep the conversation
File > 64KB Summarize via run_subagent_summarization (or equivalent); use the summary as the LLM input
Provider not available Mark harvest-failed; keep the conversation
Network timeout Same; mark harvest-failed; keep the conversation
Disk full writing to category files Raise; mark harvest-failed; keep the conversation (don't reclaim)

The pattern: critical operations complete; non-essential post-steps are best-effort. The marker is visible. The user can re-run.


7. The cross-references

  • conductor/code_styleguides/agent_memory_dimensions.md §4 — the knowledge dim in context
  • conductor/code_styleguides/feature_flags.md — the "delete to turn off" pattern
  • conductor/code_styleguides/cache_friendly_context.md — where the digest is injected (layer 7, stable)
  • conductor/code_styleguides/data_oriented_design.md §1.2 — "Design around a model of the world" (the anti-pattern)
  • data_oriented_error_handling_20260606 — the Result[T, ErrorInfo] pattern for the harvest LLM call
  • docs/guide_knowledge_curation.md — the user-facing deep-dive
  • conductor/tracks/nagent_review_20260608/nagent_review_v2_3_20260612.md §3.1, §4 — the nagent pattern that informed this styleguide