CtrlK
BlogDocsLog inGet started
Tessl Logo

getlarge/legreffier

LeGreffier mode: verify identity, sign commits with MoltNet diary, investigate past rationale via signed diary search

90

2.64x
Quality

90%

Does it follow best practices?

Impact

90%

2.64x

Average score across 5 eval scenarios

SecuritybySnyk

Advisory

Suggest reviewing before use

Overview
Quality
Evals
Security
Files
name:
legreffier
description:
LeGreffier mode for Claude & Codex when GIT_CONFIG_GLOBAL=.moltnet/gitconfig; use to verify bot identity or commit signing key, sign commits with MoltNet diary (one per repo), investigate past rationale via signed diary search with relevance/recency weights, check git history or audit trail, and answer questions like "why did this break", "why did we do this", "show me the reasoning", or "what does the diary say". Also triggers for episodic diary entries when something breaks, a workaround is applied, or the user expresses surprise/frustration (e.g. "WTF", "how did that happen", "this is broken", "why did this break").

LeGreffier Skill (Claude & Codex)

Single skill for accountability: verify identity, write typed diary entries, sign commits with diary links, and investigate rationale. Works in Claude and Codex; no reliance on .claude hooks. Each repository has its own diary named after the repo.

Agent name resolution

Store resolved name as AGENT_NAME for all MCP calls (mcp__<AGENT_NAME>__<tool>). Gitconfig path: .moltnet/<AGENT_NAME>/gitconfig.

Resolution order (first match):

  1. MOLTNET_AGENT_NAME env var
  2. $ARGUMENTS if provided at skill invocation
  3. GIT_CONFIG_GLOBAL matches .moltnet/<name>/gitconfig → extract <name>
  4. .moltnet/ has exactly one subdirectory with moltnet.json → use it
  5. Multiple subdirectories → list them and ask the user

Worktree detection

If .moltnet/ is absent from CWD:

  1. git rev-parse --git-common-dir — if different from --git-dir, we're in a worktree
  2. Common dir's parent = main worktree root
  3. If <main>/.moltnet/ exists: ln -s <main>/.moltnet .moltnet
  4. If <main>/.claude/settings.local.json exists and local one doesn't: symlink it too
  5. If main worktree has no .moltnet/ either, stop and tell the user to run legreffier there first

When to trigger

  • Commits/staging while GIT_CONFIG_GLOBAL=.moltnet/<AGENT_NAME>/gitconfig
  • Verify signing identity (name/email/key), "bot verification", "commit signing"
  • Explain past decisions: "why was X changed", "what was the reasoning", "check the audit", "what does the diary say", "show me the history", "git history"
  • Any session that changes files or produces a commit (diary entry mandatory before declaring complete)
  • Something breaks, a workaround is applied, or user expresses surprise/frustration

Two signature layers

Layer 1 — Git SSH (commit-level): gpg.format=ssh, key at .moltnet/<AGENT_NAME>/ssh/id_ed25519.pub. Automatic on every git commit via gitconfig. Verify with: git verify-commit <hash>.

Layer 2 — MoltNet Diary (entry-level): Ed25519 over a structured payload. The <signature> tag in entries must contain the base64 Ed25519 signature (stdout of CLI sign), NOT the UUID request ID.

Signing flow

CLI (preferred):

npx @themoltnet/cli diary create-signed \
  --diary-id <DIARY_ID> --type <entryType> --title "<title>" \
  --content "<content>" --tags "tag1,tag2"

Progress → stderr; entry JSON → stdout.

MCP multi-step:

  1. crypto_prepare_signature({ message: "<CID>" })request_id, signing_input
  2. crypto_submit_signature({ request_id, signature })
  3. entries_create({ ..., signing_request_id: "<request_id>" })

Canonical JSON for CID:

{
  "c": "<content>",
  "t": "<title>",
  "tags": ["<sorted>"],
  "type": "<entryType>",
  "v": "moltnet:diary:v1"
}

Null title → "". Null/empty tags → []. Tags sorted alphabetically.

Verification

Use any time you need to confirm signature validity — after creation, during investigation, or on-demand.

Layer 2 — MoltNet entry signatures:

  • MCP: entries_verify({ entry_id })
  • CLI: npx @themoltnet/cli diary verify --diary-id <id> <entry-id>
  • SDK: await agent.entries.verify(diaryId, entryId)
  • Manual: extract <signature> value. 88-char base64 → crypto_verify({ signature }). UUID → "contains request ID, not verifiable." semantic/episodic entries without signing → "unsigned."

Layer 1 — Git SSH commit signatures:

  • git verify-commit <hash>

Immutability

CIDv1 + Ed25519 makes entries tamper-proof. Use for: identity and soul (always), reflection (important stances), semantic (architecture/security decisions), procedural (high-risk commits).

Once contentSignature is set, content, title, entryType, tags, contentHash, contentSignature, signingNonce are permanently blocked. superseded_by is always allowed.

MCP tool reference

ToolPurpose
moltnet_whoamiIdentity (fingerprint, public key)
diaries_list / diaries_create / diaries_getDiscover or create repo diary
entries_create / entries_get / entries_list / entries_update / entries_deleteCRUD on diary entries
entries_searchHybrid search; omit diary_id for cross-repo
reflectDigest of recent entries
diaries_consolidate / diaries_compileCluster suggestions / token-budget context pack
crypto_prepare_signature / crypto_submit_signature / crypto_verifySigning request lifecycle
entries_verifyVerify a content-signed entry
agent_lookupLook up another agent by fingerprint
relations_create / relations_list / relations_update / relations_deleteEntry knowledge graph

Prompts: identity_bootstrap, write_identity, sign_message.

Memory types

entry_typeWhen to useRequired tags
proceduralAccountable commit: what, how, riskaccountable-commit, risk:<level>, branch:<branch>, scope:<...>
semanticArchitectural decisions, rejected alternativesdecision, branch:<branch>, scope:<...>
episodicIncidents: bug hit, workaround, breakageincident, branch:<branch>, scope:<...>
reflectionEnd-of-session patterns/process gapsreflection, branch:<branch>
identityReserved — whoami, tags ["system","identity"], visibility moltnet
soulReserved — soul entry, tags ["system","soul"], visibility private

Default: semantic. Never use values outside this list.

Write-timing:

  • procedural: every medium/high-risk commit (required); low-risk optional but preferred
  • semantic: any non-trivial design choice, especially when rejecting an alternative
  • episodic: any concrete obstacle requiring investigation/workaround — write immediately, before continuing
  • reflection: end of session if patterns or process gaps were noticed

Episodic triggers (immediate capture)

Write an episodic entry immediately — don't defer to end of session — when:

SignalExample
Published artifact brokennpm install fails, Docker image crashes
Build/CI/test failure requiring investigationFlaky test, stale lockfile
Workaround applied instead of proper fixPinned dep, added retry, skipped check
Misleading error message"not found" but real issue was auth
Tool/API behaved differently than documentedCLI flag changed, API shape changed
Config was root causeWrong scope, missing env var, wrong path
User expresses frustration/surprise"WTF?", "this is broken", "how did that happen?"
Repository invariant violatedNon-monotonic metadata, inconsistent graph state
Generated artifacts required manual repairRegenerated file needed hand-fix

Heuristic: >2 minutes investigating before finding a fix → episodic entry. Write before continuing if an invariant was violated or tool output was manually patched.

Metadata conventions

Every entry includes a <metadata> block:

<metadata>
operator: <$USER>
tool: <claude|codex|cursor|cline|...>
timestamp: <ISO-8601 UTC>
branch: <git branch>
scope: <comma-separated>
refs: <1-5 file paths, symbols, packages, or endpoints>
signer: <fingerprint>   # signed entries only
risk-level: <low|medium|high>   # procedural entries only
files-changed: <int>            # procedural entries only
</metadata>

refs formats: libs/auth/src/middleware.ts, libs/auth/, @moltnet/auth, libs/auth/src/middleware.ts:validateJWT, fastify, ory-keto, POST /diaries/:id/entries, tsconfig.json. Include 1–5; prefer specific stable identifiers.

For procedural entries: extract refs from git diff --cached --stat (file paths) and @@ hunk headers (function/class names). For semantic/episodic: reference affected modules/services/files.

Subagent delegation

When subagents are available, delegate diary entry composition (metadata gathering, ref extraction, entries_create call) to a subagent. Primary agent decides what to record; subagent handles how to structure and submit.

Session activation

  1. Resolve AGENT_NAME (see above). Check worktree.
  2. Set env: GIT_CONFIG_GLOBAL=.moltnet/<AGENT_NAME>/gitconfig
  3. Load identity:
    • If MOLTNET_FINGERPRINT set, use it (skip moltnet_whoami).
    • Otherwise call moltnet_whoami. If whoami/soul missing, read moltnet://self/whoami and moltnet://self/soul; if still missing, run identity_bootstrap.
    • Hard gate: unknown fingerprint after above steps → stop. "Identity incomplete — run identity_bootstrap before continuing."
  4. Resolve diary:
    • If MOLTNET_DIARY_ID set, use it as DIARY_ID.
    • Otherwise: REPO=$(basename $(git rev-parse --show-toplevel)), call diaries_list, match name == $REPO. Not found → diaries_create({ name: "$REPO", visibility: "moltnet" }).
  5. Identity check: git config user.name && git config user.email && git config user.signingkey && git config gpg.format. Expected: name=AGENT_NAME, email ...+<AGENT_NAME>[bot]@users.noreply.github.com, signingkey=.moltnet/<AGENT_NAME>/ssh/id_ed25519.pub, format=ssh. If any missing, set GIT_CONFIG_GLOBAL and restart.
  6. Resolve OPERATOR ($USER) and TOOL (infer: CLAUDE=1claude, CODEX=1codex, else ask once).

Accountable commit workflow

  1. Credentials path: MOLTNET_CREDENTIALS_PATH else .moltnet/<AGENT_NAME>/moltnet.json.

  2. git diff --cached --stat and git diff --cached. Nothing staged → stop.

    • Scope gate: one coherent change set with a single rationale. Signals for splitting: >8 files, >300 insertions, or >2 workspace packages touched.
    • Mixed/unrelated work → split before committing.
  3. Risk classification (highest applicable):

    • High: crypto/random/hash, CI/automation, dependency lockfiles, auth/secrets
    • Medium: new files, config, UI, protocol docs, scripts in .claude//.agents/
    • Low: tests-only, comments/formatting, minor docs
  4. Write pre-commit entries:

    • Non-trivial design choice → semantic entry first
    • Concrete incident occurred → episodic entry
    • Generated artifacts malformed/repaired → episodic entry immediately (before staging)
  5. Gather: files_changed, refs (top 5 from stat + @@ headers), timestamp, branch, scope (1–2 tags, fallback scope:misc), operator, tool, fingerprint.

  6. Write rationale: 3–6 sentences on intent, impact, risk.

  7. Create diary entry via CLI:

    moltnet diary commit \
      --diary-id "$DIARY_ID" \
      --rationale "<3-6 sentences>" \
      --risk <low|medium|high> \
      --scope "<scope1,scope2>" \
      --operator "$OPERATOR" \
      --tool "$TOOL" \
      --credentials ".moltnet/<AGENT_NAME>/moltnet.json"

    Output (stdout): {"entryId":"<uuid>","signature":"<base64>"}. Parse entryId.

    Optional flags: --signed (immutable, use for high-risk), --title, --importance <1-10>, --extra-tags, --api-url. Auto-generated tags: accountable-commit, risk:<level>, branch:<branch>, scope:<s>. Auto-derived metadata: signer, branch, files-changed, refs, timestamp.

    For high-risk + --signed, verify after using the Verification section above. Fallback (Go CLI unavailable): npx @themoltnet/cli diary create-signed or MCP multi-step flow.

  8. Commit:

    git commit -m "feat(scope): summary" -m "\nMoltNet-Diary: <entry-id>"

    Signing enforced by gitconfig (gpgsign=true).

  9. Tools unavailable → do not offer skipping. Stop, state what's missing, wait. Proceed without diary only if user explicitly says so unprompted.

Hard gate: no ship without diary

Mandatory before git push, opening/updating a PR, or declaring complete:

  • At least one diary entry per logical commit group
  • Every entry has refs and branch:<branch> tag
  • If a commit happened without an entry → create a catch-up procedural entry referencing the commit hash

Pre-push checklist

  1. git rev-parse --abbrev-ref HEAD — if main or master, stop. Create a feature branch first; only exception is explicit user instruction.
  2. git status --short reviewed.
  3. At least one diary entry per change group.
  4. Entries have branch:<branch> and scope:<...> tags and refs.
  5. Commit message references diary entry id(s).

Commit shaping for task extraction

Each commit = one testable behavioral change. Splitting heuristic:

  • Commit 1: behavior change
  • Commit 2: tests (if not inline and <20 lines)
  • Commit 3: codegen/regeneration
  • Commit 4: cleanup/docs (if needed)

Ideal chain: 2–4 commits. >5 → task was too big.

Task-chain trailers

TrailerWhenPurpose
Task-Group: <slug>Every commit in multi-commit taskGroups commits; slug from behavior, e.g. context-pack-ordering
Task-Family: <family>First commit in chainbugfix|feature|refactor|test|docs|codegen|infra
Task-Completes: trueLast commit, after verificationMarks chain safe for harvester

Single-commit tasks: add all three after verification gate passes.

Verification gate for Task-Completes

Task-Completes: true = verified working, not just code written. Typecheck + lint alone are insufficient.

Change typeMinimum verification
Library with existing testsTests pass
New feature with new testsTests written AND passing
CLI/scriptRan successfully at least once
Pipeline/integrationSmoke test against real infrastructure
Config/infraValidated by consuming system
Docs-onlyImmediate

If verification requires unavailable infrastructure, omit Task-Completes. Add it in a follow-up commit after verification succeeds.

Commit message format

git commit -m "feat(scope): summary" -m "MoltNet-Diary: <entry-id>
Task-Group: <slug>
Task-Family: <family>
Task-Completes: true"

Omit Task-Family on non-first commits; omit Task-Completes on non-last.

Stacked example:

# Commit 1
fix(database): stabilize context pack ordering
MoltNet-Diary: abc123
Task-Group: context-pack-ordering
Task-Family: bugfix

# Commit 2
test(database): add ordering assertions
MoltNet-Diary: def456
Task-Group: context-pack-ordering
Task-Completes: true

First commit's diary entry includes task-summary: <one-line description> in metadata.

Entry templates

Semantic (architectural decisions)

Decision: <one sentence>
Alternatives considered: <what else was evaluated>
Reason chosen: <why>
Trade-offs: <what was given up>
Context: <constraints>

<metadata>
operator: <user> | tool: <tool> | timestamp: <ISO-UTC>
branch: <branch> | scope: <scope> | refs: <modules/packages/endpoints>
</metadata>

entry_type: semantic, tags: ["decision","branch:<b>","scope:<s>"], importance: 6–8, visibility: moltnet.

Episodic (incidents)

What happened: <failure or surprise>
Root cause: <why>
Fix applied: <resolution>
Watch for: <how to avoid next time>

<metadata>
operator: <user> | tool: <tool> | timestamp: <ISO-UTC>
branch: <branch> | scope: <scope> | refs: <file/tool/service where incident occurred>
</metadata>

entry_type: episodic, tags: ["incident","branch:<b>","scope:<s>"], optionally workaround, importance: 4–7, visibility: moltnet.

After creating, link with relations_create when meaningful:

This incident...Relation...connects to
caused by earlier bugcaused_byearlier episodic entry
proves anti-pattern realsupportsconstraint entry
fixed by specific commitreferencesprocedural entry
contradicts false diagnosiscontradictsincorrect episodic entry
recurs same bugsupportsearlier occurrence

Investigation workflow

Rule: enumerate before searching. entries_search returning empty is ambiguous; start with entries_list on known tags.

  1. Enumerate (parallel):

    • entries_list({ diary_id, tags: ["accountable-commit","branch:<b>"], limit: 20 })
    • entries_list({ diary_id, tags: ["decision","branch:<b>"], limit: 20 })
    • entries_list({ diary_id, tags: ["incident","branch:<b>"], limit: 20 }) (failures only)
    • git log --all --grep="MoltNet-Diary:" --format="%H %s" -20
    • If branch:<b> returns nothing, drop that filter and re-run.
  2. Targeted search (only after enumeration):

    entries_search({
      query: "<specific question>",
      limit: 5,
      entry_types: ["semantic","episodic"],
      w_relevance: 1.0, w_recency: 0.3,  // 0.1 if >14 days
      w_importance: 0.2
    })

    Omit diary_id for cross-repo. Retry with 2–3 shorter phrasings before concluding no entry exists.

  3. Verify signatures using the Verification section above.

  4. Report per entry: type, date, importance, signer, signature status, content summary, linked commit or "none". Conclude with answer, verification status, and explicit gap note if no entry covers the question.

GitHub CLI authentication

When GIT_CONFIG_GLOBAL=.moltnet/<AGENT_NAME>/gitconfig, prefix gh commands:

GH_TOKEN=$(npx @themoltnet/cli github token --credentials "$(dirname "$GIT_CONFIG_GLOBAL")/moltnet.json") gh <command>

Token cached ~1 hour. On 401, delete gh-token-cache.json next to moltnet.json and retry.

Allowed: gh pr, gh issue, gh api repos/{owner}/{repo}/contents/..., gh repo view/clone. Do NOT use GH_TOKEN for releases, actions, packages, etc.

Reminders

  • No Co-Authored-By trailers; LeGreffier is sole author.
  • Hooks from .claude/ won't run in Codex — follow this workflow manually.
  • Tag every entry with branch:<branch> and at least one scope:<...>.
  • Write semantic entries during the work, not after.
  • Never "skip diary due to time constraints." If MoltNet tools are unavailable and user insists, ask for explicit approval; otherwise do not commit.
Workspace
getlarge
Visibility
Public
Created
Last updated
Publish Source
CLI
Badge
getlarge/legreffier badge