Mulch Expertise Management System

repository·main·Indexed 43 days ago

https://github.com/jayminwest/mulch

A structured expertise management system for AI agent workflows that allows agents to record, query, and prime themselves with accumulated project knowledge. Mulch stores expertise as typed JSONL records in git-trackable files, enabling learnings to compound across sessions and teammates. It includes a CLI (@os-eco/mulch-cli) for managing domains, recording insights (such as conventions, failures, and decisions), and generating AI-optimized context via the `ml prime` command.

Tokens
49.7K
Snippets
150
Records
205
Agent score
55%

What's inside Mulch

  1. What is Mulch?

    main
    Mulch is a structured expertise management system designed for AI agents. It allows you to store, query, and inject project learnings across different sessions using git-tracked JSONL files. This ensures that knowledge gained during one interaction is preserved and available for future tasks.
  2. Use field aliases to handle legacy data

    main

    The aliases field in a custom_types definition allows you to map canonical field names to legacy names. When Mulch reads a record containing a legacy field name, it automatically rewrites it to the canonical name. This enables seamless schema evolution without requiring manual migration scripts.

    custom_types:
      incident:
        extends: failure
        required: [severity, pager_link]
        aliases:
          pager_link: [pagerduty_url, page_url] # Accepts legacy names on read
  3. The Mulch Soft-Archive Contract

    main

    Mulch uses a soft-archive mechanism to manage record decay. Archived records are moved to .mulch/archive/<domain>.jsonl and include a # ARCHIVED banner, a status: "archived" field, and an archived_at timestamp.

    To reverse an archival, use the following command:

    ml restore <id>
  4. Understand the record decay and auto-archive model

    main

    Records in Mulch have a freshness_score that determines their relevance. As a record ages or receives challenges, its score drops. When the score falls below a threshold, the record is flagged for archiving.

    Decay Logic: Score is calculated based on: (confirmations_in_window × type_weight) + recency_bonus − (challenges × challenge_weight) − staleness_penalty.

    Decay Rates by Type:

    • failure, decision, reference: Decay slowly (high importance).
    • convention, pattern: Decay normally.
    • note: Decays fastest (intended for short-lived scratch).
    • guide: Barely decays (long-form documentation).

    Cleanup Operations:

    • ml prime --maybe-prune: Performs a cadence-based cleanup (using a .mulch/.last-prune stamp file) to archive stale records.
    • ml doctor: Flags records that are candidates for archiving.
    • ml restore: Recovers any records that were auto-archived.
    • Note on Deletion: Auto-archive is a 'soft-archive'. Hard deletion is only performed if the --hard flag is explicitly used.

    Configuration:

    • prime.auto_prune: Set to weekly or off to control the automatic cleanup cadence.
  5. Understand base record fields in Mulch

    main

    Every record in Mulch, regardless of its type, contains a set of standard fields. While many are managed automatically by Mulch (like id, recorded_at, and status), others are provided by the user during the ml record command.

    User-provided fields

    • type: The registered type (e.g., convention, decision).
    • classification: The trust tier: foundational, tactical, or observational. Defaults to tactical.
    • tags: A list of searchable strings.
    • relates_to: A list of mx-ids for related records.
    • supersedes: A list of mx-ids this record replaces (triggers decay on ml prune).
    • owner: An opaque string for organizational use.

    System-managed fields

    • id: A stable, human-readable ID stem combined with a date.
    • status: The lifecycle state: draft, active, deprecated, or archived.
    • evidence: An object containing metadata like commit, date, issue, file, etc. Git information often auto-populates this.
  6. Understand the Mulch storage architecture

    main

    Mulch stores expertise as typed JSONL records in the .mulch/ directory. This structure ensures that expertise is git-trackable and easily shared across teams.

    Directory Structure:

    • .mulch/expertise/<domain>.jsonl: One file per domain containing typed, structured records (one per line).
    • .mulch/mulch.config.yaml: Configuration for domains and governance settings.

    Record Model:

    • Six Record Types: convention, pattern, failure, decision, reference, guide.
    • Three Classification Tiers: foundational, tactical, observational (these govern shelf life and pruning).
    • Concurrency: Uses advisory file locks and atomic writes to ensure safe concurrent access from multiple agents.
  7. Configure record ownership and review workflows

    main

    Mulch supports an ownership model to resolve conflicts and manage stale records. Ownership is resolved using the following priority chain:

    1. An explicit --owner @handle flag provided during record creation.
    2. A lookup in the project's CODEOWNERS file based on the record's file_anchors.
    3. A default_owner specified per-domain in mulch.config.yaml.
    4. The Git author of the commit that recorded the information.

    You can use ml review to list records owned by your current user that require attention.

    # List records owned by the current user that need review
    ml review
  8. Multi-Agent Safety and Concurrency

    main

    Mulch is designed for environments where multiple agents might access the expertise store simultaneously. It employs several safety mechanisms:

    • Advisory File Locking: Uses O_CREAT|O_EXCL lock files with a 50ms retry interval and a 5s timeout. Stale locks are detected after 30s. Write commands (record, edit, delete, compact, prune, doctor --fix) all utilize this locking.
    • Atomic Writes: Uses a temporary file + rename strategy to prevent partial or corrupt JSONL writes.
    • Git Integration: Uses a merge=union strategy in .gitattributes to ensure that during git conflicts, all unique lines from both sides are preserved, preventing loss of expertise.
    • Read-Only Safety: Commands like prime, query, search, status, and validate do not require locking and can run concurrently without interference.
  9. Concurrency and Multi-Agent Safety in Mulch

    main

    Mulch is designed for multi-agent workflows where several agents record expertise concurrently. It ensures data integrity through three primary mechanisms:

    1. Advisory file locking: Write commands acquire a .lock file before modifying JSONL files. It retries every 50ms for up to 5 seconds. Stale locks (>30s) are automatically removed.
    2. Atomic writes: Mutations are written to a temporary file first and then atomically renamed into place to prevent corruption during crashes.
    3. Git merge strategy: ml init configures .gitattributes with merge=union, allowing parallel branches to append-merge JSONL lines without conflicts.

    Command Safety Levels

    Safety levelCommandsNotes
    Fully safe (read-only)prime, query, search, rank, status, validate, learn, readyNo file writes. Safe for any number of agents at any time.
    Safe (locked writes)record, edit, delete, delete-domain, compact, prune, archive, restore, doctorAcquires per-file locks. Multiple agents can target the same domain; locks serialize access.
    Serialize (setup ops)init, add, onboard, setupModifies config or external files. Run once during setup, not during parallel agent work.
  10. Understand the `.mulch/` directory layout

    main

    Mulch uses a specific directory structure within your repository to manage configuration, records, and recipes. Understanding these paths is critical for manual maintenance and automation.

    PathPurposeWritten byRead by
    .mulch/mulch.config.yamlConfigurationyou / ml initevery command
    .mulch/expertise/<domain>.jsonlLive recordsml record, ml edit, ml compact, ml restore, ml doctor --fixml prime, ml search, ml query, ml rank, ml status, ml validate, ml learn, ml ready, ml sync
    .mulch/archive/<domain>.jsonlSoft-archived recordsml pruneml search --archived, ml restore
    .mulch/recipes/<name>.tsTypeScript provider recipeyouml setup, ml setup --list
    .mulch/recipes/<name>.shShell provider recipeyouml setup, ml setup --list
    .mulch/hooks/*.shLifecycle hook scriptsyouml record, ml prime, ml prune
    .mulch/README.mdAuto-generated layout docml inithumans
    .gitattributesmerge=union rulesyougit
  11. Mulch Record Schema and Types

    main

    Mulch enforces strict schemas for 6 specific record types using Ajv. When creating records, ensure you include the required fields:

    TypeRequired FieldsOptional Fields
    conventioncontent-
    patternname, descriptionfiles
    failuredescription, resolution-
    decisiontitle, rationale-
    referencename, descriptionfiles
    guidename, description-

    Classifications & Shelf Life: Records are categorized to manage their lifecycle:

    • foundational: Permanent records.
    • tactical: Automatically pruned after 14 days.
    • observational: Automatically pruned after 30 days.

    Linking & Metadata:

    • Evidence: Use --evidence-commit or --evidence-bead flags when recording.
    • Linking: Use relates_to and supersedes (string arrays) to connect records.
    • Outcomes: Records can contain an array of outcomes including status, duration, test_results, and agent.
  12. Attach records to files or directories

    main

    You can anchor records to specific locations in your repository to ensure they are surfaced during context-aware operations like ml prime --files <path> or ml prime --context.

    • files[]: A list of repo-relative file paths. Use this when a record is about specific files or lines. It is auto-populated from the current git context if omitted. Note that records using files[] are invalidated if the file is renamed or moved.
    • dir_anchors[]: A list of repo-relative directory paths. Use this when wisdom applies to a whole module or folder. This is more resilient to file churn within the directory. You can set this explicitly using --dir-anchor <path> (repeatable).

    Matching Logic: A record matches a file path if the path is explicitly listed in files[] OR if any entry in dir_anchors[] is an ancestor of that path.