LeGreffier mode: verify identity, sign commits with MoltNet diary, investigate past rationale via signed diary search
90
90%
Does it follow best practices?
Impact
90%
2.64xAverage score across 5 eval scenarios
Advisory
Suggest reviewing before use
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.
Store resolved name as AGENT_NAME for all MCP calls (mcp__<AGENT_NAME>__<tool>). Gitconfig path: .moltnet/<AGENT_NAME>/gitconfig.
Resolution order (first match):
MOLTNET_AGENT_NAME env var$ARGUMENTS if provided at skill invocationGIT_CONFIG_GLOBAL matches .moltnet/<name>/gitconfig → extract <name>.moltnet/ has exactly one subdirectory with moltnet.json → use itIf .moltnet/ is absent from CWD:
git rev-parse --git-common-dir — if different from --git-dir, we're in a worktree<main>/.moltnet/ exists: ln -s <main>/.moltnet .moltnet<main>/.claude/settings.local.json exists and local one doesn't: symlink it too.moltnet/ either, stop and tell the user to run legreffier there firstGIT_CONFIG_GLOBAL=.moltnet/<AGENT_NAME>/gitconfigLayer 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.
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:
crypto_prepare_signature({ message: "<CID>" }) → request_id, signing_inputcrypto_submit_signature({ request_id, signature })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.
Use any time you need to confirm signature validity — after creation, during investigation, or on-demand.
Layer 2 — MoltNet entry signatures:
entries_verify({ entry_id })npx @themoltnet/cli diary verify --diary-id <id> <entry-id>await agent.entries.verify(diaryId, entryId)<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>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.
| Tool | Purpose |
|---|---|
moltnet_whoami | Identity (fingerprint, public key) |
diaries_list / diaries_create / diaries_get | Discover or create repo diary |
entries_create / entries_get / entries_list / entries_update / entries_delete | CRUD on diary entries |
entries_search | Hybrid search; omit diary_id for cross-repo |
reflect | Digest of recent entries |
diaries_consolidate / diaries_compile | Cluster suggestions / token-budget context pack |
crypto_prepare_signature / crypto_submit_signature / crypto_verify | Signing request lifecycle |
entries_verify | Verify a content-signed entry |
agent_lookup | Look up another agent by fingerprint |
relations_create / relations_list / relations_update / relations_delete | Entry knowledge graph |
Prompts: identity_bootstrap, write_identity, sign_message.
| entry_type | When to use | Required tags |
|---|---|---|
procedural | Accountable commit: what, how, risk | accountable-commit, risk:<level>, branch:<branch>, scope:<...> |
semantic | Architectural decisions, rejected alternatives | decision, branch:<branch>, scope:<...> |
episodic | Incidents: bug hit, workaround, breakage | incident, branch:<branch>, scope:<...> |
reflection | End-of-session patterns/process gaps | reflection, branch:<branch> |
identity | Reserved — whoami, tags ["system","identity"], visibility moltnet | |
soul | Reserved — 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 preferredsemantic: any non-trivial design choice, especially when rejecting an alternativeepisodic: any concrete obstacle requiring investigation/workaround — write immediately, before continuingreflection: end of session if patterns or process gaps were noticedWrite an episodic entry immediately — don't defer to end of session — when:
| Signal | Example |
|---|---|
| Published artifact broken | npm install fails, Docker image crashes |
| Build/CI/test failure requiring investigation | Flaky test, stale lockfile |
| Workaround applied instead of proper fix | Pinned dep, added retry, skipped check |
| Misleading error message | "not found" but real issue was auth |
| Tool/API behaved differently than documented | CLI flag changed, API shape changed |
| Config was root cause | Wrong scope, missing env var, wrong path |
| User expresses frustration/surprise | "WTF?", "this is broken", "how did that happen?" |
| Repository invariant violated | Non-monotonic metadata, inconsistent graph state |
| Generated artifacts required manual repair | Regenerated 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.
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.
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.
AGENT_NAME (see above). Check worktree.GIT_CONFIG_GLOBAL=.moltnet/<AGENT_NAME>/gitconfigMOLTNET_FINGERPRINT set, use it (skip moltnet_whoami).moltnet_whoami. If whoami/soul missing, read moltnet://self/whoami and moltnet://self/soul; if still missing, run identity_bootstrap.identity_bootstrap before continuing."MOLTNET_DIARY_ID set, use it as DIARY_ID.REPO=$(basename $(git rev-parse --show-toplevel)), call diaries_list, match name == $REPO. Not found → diaries_create({ name: "$REPO", visibility: "moltnet" }).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.OPERATOR ($USER) and TOOL (infer: CLAUDE=1→claude, CODEX=1→codex, else ask once).Credentials path: MOLTNET_CREDENTIALS_PATH else .moltnet/<AGENT_NAME>/moltnet.json.
git diff --cached --stat and git diff --cached. Nothing staged → stop.
Risk classification (highest applicable):
.claude//.agents/Write pre-commit entries:
semantic entry firstepisodic entryepisodic entry immediately (before staging)Gather: files_changed, refs (top 5 from stat + @@ headers), timestamp, branch, scope (1–2 tags, fallback scope:misc), operator, tool, fingerprint.
Write rationale: 3–6 sentences on intent, impact, risk.
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.
Commit:
git commit -m "feat(scope): summary" -m "\nMoltNet-Diary: <entry-id>"Signing enforced by gitconfig (gpgsign=true).
Tools unavailable → do not offer skipping. Stop, state what's missing, wait. Proceed without diary only if user explicitly says so unprompted.
Mandatory before git push, opening/updating a PR, or declaring complete:
refs and branch:<branch> tagprocedural entry referencing the commit hashgit rev-parse --abbrev-ref HEAD — if main or master, stop. Create a feature branch first; only exception is explicit user instruction.git status --short reviewed.branch:<branch> and scope:<...> tags and refs.Each commit = one testable behavioral change. Splitting heuristic:
Ideal chain: 2–4 commits. >5 → task was too big.
| Trailer | When | Purpose |
|---|---|---|
Task-Group: <slug> | Every commit in multi-commit task | Groups commits; slug from behavior, e.g. context-pack-ordering |
Task-Family: <family> | First commit in chain | bugfix|feature|refactor|test|docs|codegen|infra |
Task-Completes: true | Last commit, after verification | Marks chain safe for harvester |
Single-commit tasks: add all three after verification gate passes.
Task-CompletesTask-Completes: true = verified working, not just code written. Typecheck + lint alone are insufficient.
| Change type | Minimum verification |
|---|---|
| Library with existing tests | Tests pass |
| New feature with new tests | Tests written AND passing |
| CLI/script | Ran successfully at least once |
| Pipeline/integration | Smoke test against real infrastructure |
| Config/infra | Validated by consuming system |
| Docs-only | Immediate |
If verification requires unavailable infrastructure, omit Task-Completes. Add it in a follow-up commit after verification succeeds.
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: trueFirst commit's diary entry includes task-summary: <one-line description> in metadata.
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.
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 bug | caused_by | earlier episodic entry |
| proves anti-pattern real | supports | constraint entry |
| fixed by specific commit | references | procedural entry |
| contradicts false diagnosis | contradicts | incorrect episodic entry |
| recurs same bug | supports | earlier occurrence |
Rule: enumerate before searching. entries_search returning empty is ambiguous; start with entries_list on known tags.
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" -20branch:<b> returns nothing, drop that filter and re-run.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.
Verify signatures using the Verification section above.
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.
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.
Co-Authored-By trailers; LeGreffier is sole author..claude/ won't run in Codex — follow this workflow manually.branch:<branch> and at least one scope:<...>.semantic entries during the work, not after.