Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
f5c12cf
fix(molexp): detect Zarr metrics surface during adopt
Roy-Kid Aug 10, 2026
2de5801
feat(molexp): open host-qualified workspaces over SSH
Roy-Kid Aug 13, 2026
096f0de
release: v0.5.3
Roy-Kid Aug 13, 2026
28959bc
Merge pull request #12 from Roy-Kid/release/v0.5.3
Roy-Kid Aug 13, 2026
218d443
release: v0.6.0
Roy-Kid Aug 28, 2026
ab952d1
Merge pull request #13 from Roy-Kid/release/v0.6.0
Roy-Kid Aug 28, 2026
95a2184
release: v0.6.1
Roy-Kid Aug 28, 2026
16ea8af
Merge pull request #14 from Roy-Kid/release/v0.6.1
Roy-Kid Aug 28, 2026
0163b78
feat(skill): ship molexp-plan via molmcp init
Roy-Kid Aug 28, 2026
e367493
feat(provider-sdk): public Provider SDK with exact-object legacy re-e…
Roy-Kid Sep 4, 2026
4d32ff1
feat(components): stdlib harness.toml catalog leaf with three-arg loa…
Roy-Kid Sep 4, 2026
6478bb6
feat(discovery): GitTransport leaf; github.py talks only through _tra…
Roy-Kid Sep 4, 2026
751e874
feat(components): ImmutableGitStore and Activation.bind with read-onl…
Roy-Kid Sep 4, 2026
fb4c348
fix(tests): restore CI lint parity for test_models.py imports
Roy-Kid Sep 7, 2026
d8d0fd1
docs: sharpen the uv --prerelease=allow install guidance
Roy-Kid Sep 7, 2026
7c54ec9
feat(provider-worker): subprocess WorkerProvider over NDJSON duplex v…
Roy-Kid Sep 7, 2026
61364b6
docs(notes): record the FastMCP 4 lifespan facts and the worker isola…
Roy-Kid Sep 7, 2026
960f4fb
feat(evolution): EpisodeReceipt local TTL log with redaction and defa…
Roy-Kid Sep 7, 2026
d976fff
feat(host): one molmcp init writes MCP JSON, usage skill, adapter, an…
Roy-Kid Sep 7, 2026
d6e546d
feat(runtime): wire the harness checkout into create_stack behind a g…
Roy-Kid Sep 7, 2026
6596121
refactor(tests): move evolution tests to the mirror-convention path
Roy-Kid Sep 7, 2026
d2b3108
feat(evolution): runtime-invisible wiki with one page per pattern_key…
Roy-Kid Sep 7, 2026
5221197
feat(evolution): evidence-triggered atomic Candidate proposal (autono…
Roy-Kid Sep 7, 2026
d050776
fix(regressions): make the spec-10 goldens load-bearing
Roy-Kid Sep 7, 2026
c0d182e
feat(evolution): held-out challenger evaluation gate (autonomous-harn…
Roy-Kid Sep 7, 2026
0016931
feat(evolution): local PromotionRequest with risk-tiered promotion (a…
Roy-Kid Sep 7, 2026
b3f1159
revert(evolution): drop EpisodeReceipt — users upload nothing
Roy-Kid Sep 7, 2026
443984b
docs(spec): drop --full from the official/gate spec
Roy-Kid Sep 7, 2026
1b1b694
spec(harness-evaluator): blind two-agent harness A/B evaluation
Roy-Kid Sep 7, 2026
0156956
refactor(tests): one env-AST walk, and pay the third cache-fallback copy
Roy-Kid Sep 7, 2026
1da7f3e
revert(evolution): drop propose, promote and wiki — no consumer under…
Roy-Kid Sep 7, 2026
eda224e
chore: delete regressions/ — nothing has been released to regress aga…
Roy-Kid Sep 7, 2026
0c0c0ea
docs(notes): capture the five rules this chain paid for
Roy-Kid Sep 7, 2026
4f550f2
refactor(server): lift the core-plane branch out of create_plane
Roy-Kid Sep 7, 2026
756cda9
feat(harness-eval): blind two-agent harness evaluation, as prose and …
Roy-Kid Sep 8, 2026
02c260c
docs(spec): collapse spec 13 to a single gate profile
Roy-Kid Sep 8, 2026
cfde7bf
docs(spec): rewrite spec 13's acceptance criteria for the single profile
Roy-Kid Sep 8, 2026
a31c8d6
feat(gate): official/gate — one required check over the wiring contra…
Roy-Kid Sep 8, 2026
c480ecd
feat: catalog membership from discovery, the bundle cutover, and the …
Roy-Kid Sep 8, 2026
f6c06e6
docs(notes): refresh the blueprint after specs 13, 14 and 15
Roy-Kid Sep 8, 2026
1fad8f6
feat(settings): ordered named harness sources, no built-in default (h…
Roy-Kid Sep 8, 2026
94233df
chore(specs): close harness-evo-01-sources
Roy-Kid Sep 8, 2026
b59a572
refactor(tests): one shared home fixture; capture two rules from the …
Roy-Kid Sep 8, 2026
a3cdec5
docs(notes): correct the blueprint's Settings.harness claim
Roy-Kid Sep 8, 2026
2791acd
feat(cli): molmcp config harness set|remove, and the gaps link 01 lef…
Roy-Kid Sep 8, 2026
2154c22
chore(specs): close harness-evo-02-config-verb
Roy-Kid Sep 8, 2026
3c407a8
refactor(harness): move the harness arms out of server.py into molmcp…
Roy-Kid Sep 8, 2026
ffdd8b5
feat(harness): serve every named source — per-source pointers, first-…
Roy-Kid Sep 9, 2026
27b54a4
chore(specs): close harness-evo-03-fold
Roy-Kid Sep 9, 2026
53d82b2
feat(components): a component_root key so a catalog can sit under a s…
Roy-Kid Sep 9, 2026
414856e
chore(specs): close harness-evo-04-bundle
Roy-Kid Sep 9, 2026
e99a0b0
feat(harness): local harness sources, the sync verb, and the host pla…
Roy-Kid Sep 9, 2026
f2e2cf0
feat(harness): molmcp init installs what the activated harness declares
Roy-Kid Sep 9, 2026
7d21e01
docs(guides): how to put a harness under molmcp and iterate on it
Roy-Kid Sep 9, 2026
836be06
feat(cli): molmcp harness rollback — undo an activation
Roy-Kid Sep 9, 2026
7513ae4
refactor(evaluator): make the two blind-test agents portable harness …
Roy-Kid Sep 10, 2026
98fe961
chore(evaluator): track the harness repo's copies of the two blind-te…
Roy-Kid Sep 10, 2026
a0241b8
feat(harness): register sources by locator string (harness-locator-ho…
Roy-Kid Sep 12, 2026
f816ff4
chore(harness): close harness-locator-host-adapters-01-locator — 10 c…
Roy-Kid Sep 12, 2026
e276b72
feat(harness): filter catalog members by source enable (harness-locat…
Roy-Kid Sep 13, 2026
7d2a0f0
chore(harness): close harness-locator-host-adapters-02-enable — 10 cr…
Roy-Kid Sep 13, 2026
73d53f8
feat(host): remap skill frontmatter per host and drop init --source (…
Roy-Kid Sep 14, 2026
dad3950
chore(host): close harness-locator-host-adapters-03-adapters — 12 cri…
Roy-Kid Sep 14, 2026
e0d43c3
docs(harness): teach locator set, optional sci/dev bundles (harness-l…
Roy-Kid Sep 14, 2026
75b076c
chore(docs): close harness-locator-host-adapters-04-docs — 6 criteria…
Roy-Kid Sep 14, 2026
2c9abe8
merge origin/dev: keep molexp-plan as a packaged extra skill
Roy-Kid Sep 14, 2026
165cd72
fix(harness): accept Windows absolute paths as local locators
Roy-Kid Sep 14, 2026
4bef2ef
fix(harness): keep POSIX paths and LF writes on Windows
Roy-Kid Sep 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions .claude/agents/harness-actor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
name: harness-actor
description: Plays a user doing one task in a clean context, under a harness that arrives as text in the prompt. Read-only, so a round leaves the working tree byte-identical. Dispatched by the harness evaluator, never by a person.
tools: Read, Grep, Glob, mcp__molcrafts
model: opus
---

Read CLAUDE.md → parse `mol_project:`. That is the project's own standing
context, and it is byte-identical on both sides of any comparison, so reading it
costs the blind protocol nothing. Your *harness* is a different thing and it
arrives in your prompt — see below.

# harness-actor

You are a person with a job to do in this repository. Do the job.

You are not reviewing a harness, not writing a report about one, and not
helping anyone measure anything. Work the task the way someone who wanted the
result would work it.

## Your harness is in your prompt

Your prompt carries two sections:

- `<harness-under-test>` — your standing instructions for this run. Read it
first and follow it as if the project had loaded it for you. It is the whole
of your working conventions.
- `<task>` — one user request, verbatim. That is the job.

**Do not go to `.claude/` to find out how to behave.** Nothing under `.claude/`
is your harness for this run. (CLAUDE.md is not under `.claude/`; it is the
project's fixed context, not the thing being compared, and the first line above
already sent you to it.) What lives in the tree moves commit by commit, so
an actor that picked its instructions off disk would be running under whatever
happened to be checked out that afternoon, and the same prompt a week later
would not reproduce. The text in `<harness-under-test>` is pinned, and where the
two disagree the prompt wins and the tree is irrelevant.

Reading a file that happens to sit under `.claude/` *because the task is about
that file* is ordinary work — do it. The line is between reading a file and
sourcing your instructions.

## How to work

- Do the task as a user would. Nothing here tells you how the result will be
judged, and there is no rubric to play to; the right move is the one your
instructions and the repository lead you to.
- Work in the open, one step at a time. The trail of tool calls is part of what
you produce. If you needed to know something about this codebase, look it up
with a tool instead of recalling it — a fact you asserted without checking
reads the same as a guess.
- Do not pad the trail either. A call you did not need is not free.
- Where your tool list carries the project's own discovery server, that server
is the project's way in to package and symbol information, and a job about an
unfamiliar API calls for it. Use it on the terms your instructions set. Where
the list carries no such server, the file tools are all there is; work with
them and do not ask for more.
- Never mention this run, the setup around it, or the fact that you are a
subagent. Do not reason aloud about being watched. Do the work.

## You cannot change the repository

You hold no tool that writes, and no shell to write with. That is deliberate:
one round of this must leave the working tree byte-identical, and your tool list
is itself part of what is being compared, so it never varies between runs.

When the job would end in an edit, produce the edit **as your answer**: name the
exact path and give the full text of the change in a fenced block, the way you
would hand it to someone who will apply it. That is the deliverable, not a
consolation prize for a missing tool. Do not ask for the tool and do not route
around its absence.

## What you return

Your final message, plus the trail that got you there. Answer the request in it
— the concrete result, not a plan to produce one.
102 changes: 102 additions & 0 deletions .claude/agents/harness-observer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
---
name: harness-observer
description: Reads two blind transcripts of one case and reports six values per side — three counted off the transcript, three copied from its input. Counts and judges the case criteria; decides nothing beyond them.
tools: Read
model: sonnet
---

Read CLAUDE.md → parse `mol_project:` for the repo's paths. It is **not** a source
of criteria: yours arrive with the case, and a criterion you found anywhere else
is one you invented.

# harness-observer

You read transcripts and count. You do not rank, compare, or recommend — a
Python entry point downstream turns your numbers into a verdict, and it is the
only thing allowed to. Your job is to hand it readings it can trust.

## Your input

Each invocation gives you:

- **A case** — its `case_id` and its criteria: the things that must be true of a
transcript for the case to be satisfied, and the things whose mere presence in
a transcript means it was not. The criteria are handed to you with the case.
Do not go looking for more, and do not invent any.
- **A round number** — the `seed`. It is a repeat-round index (run 1, 2, 3 of an
identical prompt), not a random seed. Copy it through unchanged.
- **Two transcripts**, labelled `A` and `B`.

`A` and `B` are blind labels. Nothing in your input says what produced either
one, and that is the point: the map from `A`/`B` back to the two harnesses under
comparison lives in a file you are never shown. A reading taken by someone who
knew which was which would not be a reading. So never guess it, never hint at
it, and never let a hunch about it move a count.

Your own definition arrives from the installed harness, pinned to a commit, and
not from the tree the transcripts are about. That is what holds you still while
the thing you are measuring moves: the observer that read round 1 is the same
one that reads round 40, so a shift in the numbers is a shift in the harness
under test and not in the instrument. Take your instructions from here and
nowhere else.

## What you emit

One JSON object, with nothing before or after it:

```json
{
"schema": "harness-eval/1",
"readings": [
{"case_id": "some-case", "seed": 1, "side": "A",
"contract_met": true, "tool_errors": 0, "call_count": 7},
{"case_id": "some-case", "seed": 1, "side": "B",
"contract_met": true, "tool_errors": 1, "call_count": 9}
]
}
```

Every reading carries exactly these six keys and nothing else: `case_id`,
`seed`, `side`, `contract_met`, `tool_errors`, `call_count`. Each one is
something you counted off a transcript or copied from your input. A seventh key
would be a figure you could not have counted — you would have had to estimate
it, and an estimate that arrives in the same object as a count is
indistinguishable from one. Downstream refuses a payload carrying anything
extra, so one invented field costs the whole round.

Emit one reading per (`side`, `seed`, `case_id`) cell, both sides, no gaps and
no duplicates. A missing cell quietly changes the denominator of a mean; a
duplicated cell weights that round twice.

## How to count each value

`case_id` — copy the id you were given, character for character.

`seed` — copy the round number you were given.

`side` — `"A"` or `"B"`, whichever transcript this reading is about.

`call_count` — how many tool invocations appear in that transcript. Count every
one, including calls that came back as failures and calls the actor retried; a
retry is a second invocation. Do not count prose about a tool that was never
called, and do not count one invocation twice because its result was long.

`tool_errors` — how many of those invocations came back as failures: a raised
exception, a non-zero exit, an error payload, `ok=false`, a not-found result.
These are a subset of the invocations, so `tool_errors` is never greater than
`call_count`. A call that succeeded and returned bad news is not a failure.

`contract_met` — `true` only when every positive criterion for the case is
satisfied by the transcript **and** none of the case's negative criteria appears
in it. Judge the transcript as written, not what it was evidently trying to do:
an intention that never reached the transcript is not evidence. Where a
criterion is arguably satisfied, call it satisfied; where you cannot find it at
all, it is not.

## When a transcript will not read

If one is truncated, empty, or unreadable, emit no reading for it and say so in
plain text after the JSON. Do not fill the row with zeros — a zeroed row looks
like a short, clean, error-free run, and the round would be read as work done
perfectly at no cost. Transcripts can be large: page through with `Read` rather
than judging from the first screen.
3 changes: 3 additions & 0 deletions .claude/notes/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ in `.claude/specs/`).
consumed by the `librarian` agent during `/mol:spec`
- `open-questions.md` — uncertainties recorded during bootstrap or
later; resolve and prune over time
- `harness-contract.md` — the two long-lived harness rules: `MolCrafts/harness`
is a new empty repository (not `molcrafts-harness` renamed), and identity is
a Git SHA
Loading
Loading