AI Initiative logoAI Initiative
Reading room / Manthan

D-315 — Manthan cohort-1 post-mortem

A dated case study of a multi-worker coordination cohort: dispatch races, portable tooling failures, atomic claims, evidence handoff and proposed improvements.

Historical audit / evidence record
This is the retained source text, not a new validation of its claims. Numerical results and comparative claims require the accompanying source review before public reuse.

Read the connected case study or article →

Not explicitly stated in source · Date not established

Provenance, authorship and editorial notes

D-315 — Manthan cohort-1 post-mortem

Id

manthan-herald-protocol-audits-manthan-cohort1-postmortem

Project

Manthan

Source path

manthan/herald-protocol/audits/manthan-cohort1-postmortem.md

Source sha256

abc1c1c0e7585f43005a61988bdc8b56c0a7ff72ce97d75a90e816ac18c236c0

Local copy sha256

abc1c1c0e7585f43005a61988bdc8b56c0a7ff72ce97d75a90e816ac18c236c0

Repo head

3a94084024a3434cef331fecac91c2c3b43f0313

Date
Not recorded
Date basis

Original publication date not established; Git revision date retained separately.

Author

Not explicitly stated in source

Author basis

Unknown; Git committer not substituted for document author.

Last source commit

8b75d1b0157c20a0a463f0e31008ef9f73099beb

Last source commit date

2026-06-08T07:40:14Z

Document status

Historical audit / evidence record

Summary

A dated case study of a multi-worker coordination cohort: dispatch races, portable tooling failures, atomic claims, evidence handoff and proposed improvements.

Topics
  • Adversarial review and case studies
  • TL;DR
  • Cohort-1 inventory
  • What broke and what I fixed
Theme

Adversarial review and case studies

Priority

core

Related ids

No entries recorded.

Evidence concerns
  • Recovered source text is preserved verbatim. Assertions, comparisons, commands and results belong to its original context; they were not re-executed or externally re-verified during this corpus review.
  • Historical record: current deployment, task completion and test status must not be inferred from this date-bound document.
Word count

2518

Bytes

17691

Source relative

herald-protocol/audits/manthan-cohort1-postmortem.md

Remote mirror

manthan/herald-protocol/audits/manthan-cohort1-postmortem.md

Full text preserved

True

D-315 — Manthan cohort-1 post-mortem

Period covered: 2026-05-05 12:50Z (system bootstrap) → 2026-05-05 14:09Z (D-310 submit, ~80 minutes wall clock).

Cohort scope: First end-to-end run of the manthan coordination system across distinct workers. 17 findings on disk, 16 done in TODO, 9 distinct worker identities (5 dispatched by me as Agent subagents, 4 external agents the user injected from other sessions/runtimes).

This document is a hands-dirty look at what broke, what worked, and what should change before cohort-2.


TL;DR


Cohort-1 inventory

Worker identitySourceTask(s)Claim → submit
driver-claudethis sessionD-302, D-306, D-308, D-310, D-311mostly 5–25 min each
worker-Adispatched by meT-0017 min
worker-Bdispatched by meT-0128 min
worker-Cdispatched by meT-0139 min
worker-Ddispatched by meT-0168 min
worker-Edispatched by meT-002 (CBOR-det lib)11 min
worker-Fdispatched by meT-020 (lost race; clean exit)n/a
worker-glossary-2026-05-05externalT-019, D-30912, 2 min
codex-20260505-envelope-cheatsheetexternalT-0181 min
codex-20260505-feedback-intakeexternalD-3051 min
worker-opus-2026-05-05-T020externalT-020 (won race vs worker-F)5 min
worker-opus-2026-05-05-D301externalD-3018 min

Total: 16 tasks done, ~80 minutes elapsed, single human in the loop.


What broke and what I fixed

B1. mapfile: command not found (macOS bash 3.2)

Site: manthan/bin/list initial draft used mapfile -t lines < <(grep ...).

Symptom: list failed silently on macOS where bash --version reports 3.2.57 (Apple ships an old GPLv2-era bash). mapfile is bash 4+.

Fix: rewrote with the portable while-IFS-read idiom:

while IFS= read -r line; do
  ...
done < <(grep ...)

Lesson: manthan/bin/* runs in whatever shell environment the worker has. Default to POSIX-portable constructs. mapfile, readarray, [[ ... ]] (where avoidable), ${var,,} lowercase, &> file, etc. are out.


B2. Activity log entries containing literal $(date)

Site: the initial coordinator/submit drafts used Write tool to append to manthan/state/log/activity.log with content like "2026-05-05T... $(date) SUBMIT ...".

Symptom: Write does not perform shell expansion. Resulting log lines contained the literal string $(date) instead of a timestamp.

Fix: all activity-log writes go through Bash with echo ... >> activity.log, never through Write.

Lesson: Write is for inert content; Bash is for expansion. Document this in manthan/AGENT_PROTOCOL.md (or a comment at the top of bin scripts) so future contributors don't repeat it.


B3. Coordinator grep -c ... || echo 0 producing multi-line output

Site: manthan/bin/coordinator's STATUS.md regeneration logic.

Symptom: count=$(grep -cE '^\| T-' TODO.md || echo 0) — when grep matched, it returned an integer; when it didn't match (exit code 1), the || echo 0 substituted 0. But grep -c outputs 0 on its own when there are zero matches (and exit code 1), so the chain produced "0\n0" — two lines — and downstream arithmetic broke with value too great for base.

Fix: changed to count=$(grep -cE '^\| T-' TODO.md || true) and accept that grep-c emits 0 on no-match. Pre-clean any stray newlines if needed via tr -d '\n'.

Lesson: Don't double-default. grep -c already gives a numeric on no-match; the || echo 0 was redundant and wrong. When you find yourself defending a tool against its own normal output, re-read the man page first.


B4. BSD-sed silent failure / homebrew PATH inheritance

Site: manthan/bin/submit originally used gsed -i 's/| pending /| done /' TODO.md — homebrew GNU sed's in-place flag.

Symptom: Worker-B (subagent) submitted T-012; the activity log recorded the submit; but TODO.md still showed T-012 | pending. Status was inconsistent across two oracles. After investigation: subagent shells didn't inherit homebrew's /opt/homebrew/bin in PATH, so gsed was missing. gsed printed an error to stderr; submit script didn't fail because set -e wasn't set on that line; activity log got written first, TODO didn't.

Fix: rewrote both submit and release to use perl -i -pe instead of any sed variant. perl ships with macOS, with most Linuxes, and behaves identically. Single rule:

perl -i -pe "if (/^\\| $task_id /) { s/\\| (pending|claimed|blocked)( +)\\|/| done    \$2|/ }" "$TODO"

Lesson: In a multi-runtime environment, "available everywhere" beats "available where I tested it." sed/gsed/awk variants are a swamp; perl is reliably uniform.

Adjacent lesson: set -euo pipefail at the top of every bin script. Submit was robust enough to record the activity log before the sed call failed — but if the sed call had been first, we'd have lost the activity-log entry. Failing loud is better than failing partial.


B5. submit cp source==destination error

Site: manthan/bin/submit <task_id> <worker> <findings_src>.

Symptom: Some workers (driver-claude included) wrote findings directly to the canonical destination (manthan/state/findings/<task>/) rather than a tmp dir. Submit then ran cp -R "$findings_src" "$findings_dest" which errored: "cp: ... and ... are identical (not copied)."

Fix: compute absolute paths, skip the copy when src and dest resolve to the same path:

src_abs="$(cd "$(dirname "$findings_src")" && pwd)/$(basename "$findings_src")"
dest_abs="$(pwd)/$findings_dest"
if [[ "$src_abs" == "$dest_abs" ]]; then
  : # already in place
else
  cp -R "$src_abs" "$dest_abs"
fi

Lesson: Treat both calling conventions as legitimate. Some workers stage to tmp; some write directly. The submit script should be a no-op for the latter, not an error. Document both calling patterns in AGENT_PROTOCOL.md.


B6. patch.py Path(__file__).parents[3] instead of .parents[4]

Site: the D-306 substitution script manthan/state/findings/D-306/patch.py.

Symptom: when the subagent ran the script, its CWD was the project root, but __file__ resolution counted parents from the script's own location. With the script at manthan/state/findings/D-306/patch.py, parents[3] lands on manthan/, not the project root. The script tried to read THREATS.md from the wrong directory.

Fix: parents[4]. Verified by counting: patch.py → D-306 → findings → state → manthan → repo-root. Five hops; index 4.

Lesson: Don't trust the __file__ path for project-root resolution. Either:

The first option is cleanest for one-shot scripts. The third is cleanest for any multi-script tooling.


P1. Dispatch race vs external agent (worker-F vs worker-opus-T020)

Setup: I was dispatching parallel subagents on T-002, T-019, T-020 (cohort-2). Simultaneously, the user invoked an external opus agent on T-020 from another session. Worker-F's CLAIM raced with worker-opus-2026-05-05-T020's CLAIM.

Outcome: Atomic-mkdir claim worked correctly. worker-opus-2026-05-05-T020 won the race (mkdir succeeded for them, EEXIST for worker-F). Worker-F honoured the AGENT_PROTOCOL.md "no racing" rule and exited cleanly, no submission produced. The external agent submitted T-020 5 minutes later. No double-execution.

The cost: I spent ~30 seconds writing the worker-F prompt; that effort was wasted.

Lesson — for orchestrators (me, future drivers, and any human dispatcher):

1. Read STATUS.md immediately before dispatch, not from a stale view earlier in the session. The status changes constantly when external agents are active.
2. Bias toward self-claiming first, then dispatching. The driver can claim a task atomically before spawning the subagent; the subagent then verifies it owns the claim. This eliminates the race window.
3. Or: dispatch with --check-claim semantics. The subagent's first action is claim, and if it fails, exit cleanly with a clear message. Worker-F did exactly this; the system worked, the orchestrator just wasted a dispatch.

For cohort-2, I'll preview status right before each Agent invocation, and prefer batching sequential dispatches between status reads.


What worked well

W1. Atomic mkdir-claim

This is the cornerstone of the whole system. Across 9 distinct workers, 17 claims, and at least one observed race, the mkdir-EEXIST-on-collision contract held. No double-execution. The activity log gives an audit trail for any reviewer.

The contract is simple enough that external agents (codex-, worker-opus-) implemented it correctly from JOIN.md alone. Simplicity is the feature.

W2. JOIN.md as onboarding

The flat onboarding doc — "if you can read files, run shell scripts, and write files, you can contribute" — produced four external-agent submissions with no human-mediated handholding. This is exactly what we hoped for: the system is self-onboarding for any sufficiently capable agent.

The two codex-20260505- submissions and the two worker-opus- submissions are textually clean, follow the receipt template, and integrate directly into the findings tree. No coordination overhead beyond the system itself.

W3. perl-i over sed

After B4, perl-i became the universal in-place editor for this project. It's worked across:

Recommendation: codify "perl -i for in-place edits, never sed" in manthan/AGENT_PROTOCOL.md. New in-place edits should default to perl regardless of whether the worker is on macOS, Linux, or some other Unix-flavoured runtime.

W4. Findings directory as the unit of work

A submission is a directory containing RECEIPT.md plus arbitrary outputs (audits, registries, scripts, regenerated test vectors). This is more flexible than "submission is a single file" because:

W5. launchd LaunchAgent for 24×7 maintenance

com.herald.manthan-coordinator.plist runs the coordinator every 600s. Across the cohort, it:

This is a low-cost, high-resilience piece. Even if the human and all interactive sessions go away, the coordinator keeps the system tidy.


Concrete improvements for cohort-2

In rough priority order:

I1. Codify portability rules in AGENT_PROTOCOL.md

Add a "Tooling discipline" section with:

Estimated effort: 30 minutes.

I2. Pre-flight status check tool

Add manthan/bin/preflight <task_id> that:

This is a thin wrapper around what I already check informally. Codifying it lets orchestrators (and external agents) verify before spending effort.

Estimated effort: 20 minutes.

I3. Self-claim-then-dispatch pattern for orchestrators

When the orchestrator (driver) wants to dispatch a subagent on a task:

1. Driver runs claim <task_id> <subagent_name> itself first.
2. Dispatch the subagent with the task ID and instructions to verify it owns the claim (via cat manthan/state/claims/<task_id>/owner).
3. If verification fails, subagent exits without submitting.

This eliminates the race window between dispatch and the subagent's first action.

Document this in manthan/templates/agent-prompt.md as the recommended dispatch pattern.

Estimated effort: 15 minutes (template change only).

I4. manthan/bin/timeline for cohort metrics

A small tool that walks the activity log and emits per-task latency, per-worker throughput, and a histogram of claim-to-submit durations. Useful for retrospective review and for catching workers that are stuck (claim with no submit after 2× median latency).

Estimated effort: 45 minutes.

I5. Submission template per task type

Different task types want different output shapes:

Document the conventions in AGENT_PROTOCOL.md and provide stub templates in manthan/templates/<task-type>/.

Estimated effort: 1 hour.

I6. External-agent pre-claim hint

JOIN.md should explicitly point external agents at the "claim before doing anything" rule. The cohort-1 external agents got this right by reading AGENT_PROTOCOL.md, but a one-line note in JOIN.md ("Check STATUS.md, claim atomically before working") would reduce the chance of effort waste in cohort-2.

Estimated effort: 5 minutes.


Metrics summary


Outstanding observations (not actionable in D-315 scope)


Recommendation for next steps

Apply I1, I2, I6 before cohort-2 dispatch. They're cheap and reduce the dominant failure modes.

Defer I3, I4, I5 to opportunistic completion — useful but not blocking.

The cohort-1 system is production-ready for continued use as-is. The improvements above are sharpening, not stabilising.


— D-315, completed by driver-claude, 2026-05-05.

Continue exploring