Iteration 3: online gateway foundations, agent isolation, and a few independent low-risk stories¶
Goal¶
Three things move forward together, because none of them depends on the others reaching completion first:
- Phase 1 of the online view (spec 0.10 §15B, proposed; replaces the gateway foundations of 0.9): Drive snapshots; two candidate views, a Sheet with a requests tab and a Claude artifact board on trial, one chosen after the trial; a one-way mirror of items to GitHub issues in the private repo
mmdep-inboxfor ChatGPT's read-only connector; requests pulled back intospecifyitems. Free plans only. No approvals online. Then the harness spike H1 (start, pause, resume over aspecifyitem; spec §6). The gateway work moves to phase 2 (G0, Bluehost) and phase 3 (G1–G3, AWS), each after its gate. - The two safety gaps carried from iteration 2 (story 11, the agent account; story 5, the Test Runner that depends on it) — both deferred out of iteration 2 specifically because running untrusted generated code under Atul's own account was judged worse than shipping late (see iteration-02.md, #37).
- A handful of independent low-risk stories (release command, evidence retention, a
doctorcheck, two documentation preludes) that don't block on, or get blocked by, either of the above.
Done when (0.10): phase 1 runs (D, V, IM, GH, RQ merged; the V trial has a chosen view; Atul's mmdep-inbox and token steps done); H1's spike report is written, with its relay steps and waiting times in the ledger; story 11's account exists and story 5's Test Runner runs a real task's tests under it from a test env (T); F (the feedback item type) and TR (the triage item type) are merged; R, E, M, S1 and S2 are each merged or explicitly deferred with a reason. G0–G3 are no longer part of this iteration (phases 2 and 3).
What iteration 2 taught us (inputs to this plan)¶
- Item #41 (the commit-recording gap): every release before it installed
mainintommdep_runtimefrom a plain local path, which never records the commit — sommdep doctor's "runtime vs main" check has likely been silently wrong on every release so far. Fixed in #41/#42; R (the release command) bakes thegit+file://install in from the start instead of repeating the mistake in automated form. - A session-start process gap: item #38 was approved on 2026-09-27 but sat unmerged until a routine "check closed items I haven't acted on" pass caught it the next session. Worth remembering as a standing habit, not a story here.
- #37 (story 11's mechanism): answered — stored credential in Windows Credential Manager, started via
CreateProcessWithLogonW, needspywin32. Conditions: standard non-admin account, workspaces-only access, nevermmdep_runtimeormmdep_hubitself, and amanualitem with the admin steps. - The turn 6 low-memory stop: the full test suite was once stopped because the machine ran critically low on memory during local-model and Codex work, with no warning from
mmdep doctor. M closes that gap.
Stories¶
Each story is labelled with its risk class under spec 0.4/0.6 (high-risk stories keep their own approval item; low-risk stories merge to iter-03/integration once a second-opinion reviewer exists, and to main directly until then, per spec §9).
0. iter-03/release-baseline: iteration 2 close-out and doctor baseline · S · low¶
Resolved by #43 (atul, 2026-10-02): "Story 0 = this plan branch, done on merge." Story 0 is iter-03/plan itself and is done when it merges to main. The assumed scope below (C1) is superseded and kept only as a record of what was proposed.
This story's scope is my own assumption, not something you specified — flagged here for you to confirm or replace when you approve this plan (principle 6: proceed on a logged assumption). I read "0" as mirroring iteration 2's story 0 (a short, read-only prep step before the iteration's real work): confirm items #39, #41 and #42 actually landed as expected, and that mmdep doctor run from the real runtime shows a clean "runtime vs main" (the thing #41 was fixed to make possible). If you meant something else by "0", say so and I'll redo it.
- Definition of done: a short note in this file's Status section recording the runtime's
--versionoutput anddoctor's full check list, taken from what you already reported (or a fresh run if #42 hasn't landed yet by the time this starts). - Dependencies: none. Should complete before G0, so the gateway substream starts from a confirmed-clean baseline.
G0. iter-03/gateway-oauth-research: online gateway, step 1 — OAuth and identity-provider research · M · high · docs only¶
Moved to phase 2 by spec 0.10 (proposed): the OAuth research now serves the stateless MCP server on Bluehost, after gate 1 → 2. Kept below as the record of the 0.9 plan.
Research what Claude's and ChatGPT's remote MCP connector support actually requires (OAuth 2.1 flows, redirect URIs, token scopes, any per-vendor connector-registration process), and evaluate identity-provider choices — a minimal custom OAuth server, AWS Cognito, or a third party like Auth0 — against setup cost, monthly cost at this scale, and maintenance.
- Definition of done: a research note under
docs/research/; at least onespecifyitem raised per open decision (the identity-provider choice, at minimum); no code. - Dependencies: none — first in the gateway substream, since G1 can't fix a schema around unanswered identity questions.
- High risk: not because of what it touches (it's read-only research), but because the gateway is a new, internet-reachable surface, and getting the identity model wrong here is expensive to unwind later.
G1. iter-03/gateway-spec: online gateway, step 2 — the detailed gateway spec · M · high · docs only¶
Moved to phase 3 by spec 0.10 (proposed), with G2 and G3. Kept below as the record of the 0.9 plan.
Write the detailed design (spec 0.9 §15A gives the outline; this story fills in the parts that need engineering detail, not policy): the snapshot's field allowlist made concrete (exact field names and types, not the illustrative list in 0.9), the request-queue schema, the MCP tools the gateway exposes to chat clients, the IaC approach (SAM, CDK or Terraform — decide here), the ap-south-1 region choice written up with reasoning, and a budget/cost alarm design (a CloudWatch billing alarm at a stated threshold).
- Definition of done: the doc is written, consistent with spec §15A/§16 and with G0's answered
specifyitems; no code. - Dependencies: G0 (needs the identity-provider decision to write a real OAuth flow into the schema).
G2. iter-03/gateway-fake-pushpull: online gateway, step 3 — local push/pull against a fake gateway · L · high¶
Implement, in this repo, the local orchestrator's push (snapshot upload, allowlisted-and-redacted per G1/spec 0.9) and pull (queue drain → real item creation) logic, tested against a fake/mock gateway (a local HTTP stub in tests, not a live AWS endpoint) — proving the protocol G1 defined before the real gateway exists to talk to.
- Definition of done: tests passing against the fake gateway, covering: push cadence and payload shape (matches the allowlist exactly, nothing extra), redaction applied before the payload is built, and pull → validate → create-item, including a malformed or oversized queued request being rejected without crashing the pass.
- Dependencies: G1 (the protocol must be specified before it's implemented).
G3. iter-03/gateway-repo: online gateway, step 4 — the mmdep-gateway repository · L · high · needs Atul for the deploy¶
Create the separate mmdep-gateway repository implementing G1's design on AWS serverless (API Gateway, Lambda, DynamoDB or S3 per G1's IaC choice), targeting the MCP SDK's current major version. CI runs offline/local tests only — no live AWS calls from CI. The actual AWS deploy is run by Atul, never an agent, per spec §15.6/§15.7 and the DR-001 checklist.
- Definition of done: the repo exists and is pushed (by Atul, since forge writes are his per CLAUDE.md), its offline test suite is green, and a
manualitem is raised with the deploy steps and the read-only-connector-access check from DR-001. - Dependencies: G2 (the protocol must be proven against a fake gateway before the real one is built to match it).
11. iter-03/agent-account: separate Windows account for agents · L · high · needs Atul¶
Carried from iteration 2 (#8, #37). Agent scripts, CLI agents and the Test Runner run as a dedicated local account (mmdep-agent), started via a stored credential in Windows Credential Manager and CreateProcessWithLogonW (new dependency: pywin32). Access: modify rights on mmdep_hub\workspaces\<p>\<id> only — no mmdep_hub\mmdep.db, no mmdep_hub\repos, no mmdep_runtime, no C:\Projects, no Atul's profile.
- Definition of done: the account-launch code exists with a fake runner for tests; a
manualitem gives Atul the admin steps (create the account, set ACLs, store the credential); the acceptance test is a live sandbox run under the real account. - Dependencies: none technically, but sequenced before story 5, which needs it to run generated code safely.
- Rework from #46 (2 Oct):
- fail closed: mode
offruns everything as the current user, explicitly (doctor warns; ledger says so). Moderequiredwith an unusable account raisesAgentAccountError, and the task parks with amanualitem holding the doctor fix. Never a silent fallback. Oneagent.accountledger entry per run. - timeout and kill switch stop the whole tree through a Job object; there is a live test and a fake.
- stdin is a file (empty, or the prompt), and the stdin guard test covers it. Temp stdio files sit in a current-user-only folder and are deleted in
finally. - the account is required for scripts; CLI agents opt in one by one (default off).
- release note:
pywin32, no--no-deps(story R). - setup steps revised in #48 (supersedes #44).
5. iter-03/test-runner: Test Runner role · M · low · after 11¶
Carried from iteration 2 (originally story 5). A per-project test_command in agents.yaml runs as plain code in the task worktree, after the agent and before the approve item, under the agent account from story 11. Results (pass/fail counts, duration, output tail) go to the ledger and the approve item; a failure counts as a failed attempt (iteration 2 story 4).
- Definition of done: matches iteration 2's original story 5 definition, unchanged, now actually runnable because story 11 exists.
- Dependencies: story 11 (this is the reason both moved together).
D. iter-03/drive-snapshots: Drive snapshots and request pull · S · high (since 3 Oct: it puts data on Google Drive and raises inbox items from untrusted files)¶
As built (atul, 3 Oct):
- JSON plus a markdown summary, not md and xlsx.
- Allowlist: items (number, type, title, status, project, priority, created, updated), tasks (id, project, state, title), the last test result per project, and a doctor summary. No item bodies until a redaction rule is approved.
- Request files from the Drive requests folder become specify items: size limit, type checks, raised_by=drive-request.
- The folder paths are asked in #60.
The original plan text follows. Phases B (Sheets API updates) and C (a requests tab) stay in iteration 3b, as before; xlsx is dropped.
Write md and xlsx snapshots to a Drive for Desktop synced folder (configured path).
- Definition of done: a docs-build step writes the configured snapshot files to a local folder standing in for the synced folder in tests (never a real cloud-synced path in CI); a manual note on where to point the real folder.
- Content (0.10): exactly spec §15A's field allowlist, redaction applied as the second line; a test fails if any other field appears.
- Dependencies: none since 0.10 (it was "after G2"). D is now phase 1's base; the push code G2 would have proven is phase 3's, and D writes files, not HTTP.
- Target (0.10 rework): a Drive for desktop synced folder is preferred (#58).
Phase 1 of the online view (spec 0.10 §15B, proposed)¶
V. iter-03/views-trial: two candidate views, chosen after a trial · S · low · after D¶
Both views stay in the plan until the trial ends (#58); a specify item then asks Atul which one to keep.
- (a) Sheet: a Google Sheet that reads D's snapshot files, with Apps Script views (open items by age and type, per project, the board) and a requests tab. No secrets in the Sheet.
- (b) Claude artifact board (trial): reads the snapshots through the viewer's own Drive connector, and writes request files into a Drive requests folder. Stores no secrets.
- Definition of done: (a)'s Apps Script source in this repo (
online/apps-script/) and (b)'s page, each tested against sample snapshot files with obviously fake content; amanualitem for Atul to create the Sheet and open the board; the trial's notes and thespecifyitem.
IM. iter-03/issue-mirror: one-way mirror of items to mmdep-inbox issues · M · high · after D¶
Each open item becomes one issue in the private repo mmdep-inbox, so that ChatGPT's read-only GitHub connector can see the inbox. Issues carry allowlisted, redacted fields, with labels for type and project; they are updated when the item changes and closed when it closes. One way: issue edits change nothing in MMDEP.
- Writer (C8, confirmed in #59): MMDEP's desktop push, with a fine-grained token scoped to
mmdep-inboxissues, held in Credential Manager. Apps Script is read only. - Definition of done: the mirror with tests of the item → issue mapping against a fake GitHub (allowlist and redaction asserted); a
manualitem for Atul: createmmdep-inbox, create the token, store it. - High risk: it handles a token and puts item text on a third-party service.
GH. iter-03/github-readonly: read-only GitHub adapter · M · high¶
Moved from iteration 4 (0.10). Reads issues (with labels) from a named repository, and branch-protection and ruleset state, with a fine-grained read-only token held in Credential Manager, never gh logged in as Atul. Token identity goes in the ledger.
- Definition of done: the adapter with tests against a fake HTTP server;
mmdep doctorreports the token's state; no write call exists in the code (a test asserts only GET is used).
RQ. iter-03/requests-back: requests into specify items · M · high · after GH and D¶
The local orchestrator pulls requests from three sources: the Sheet's exported request rows, request files in the Drive requests folder, and mmdep-request issues (through GH). It checks them (size, known requester, duplicates) and turns each valid one into a specify item, with the source recorded (#58). Text is untrusted input; nothing online can approve.
- Definition of done: tests for valid, oversized, unknown-kind and unknown-requester requests, a request that says "approve #N" (it becomes an ordinary item and approves nothing), and a request seen twice (one item).
H1. iter-03/harness-spike: start, pause, resume, hand over · M · high · right after D¶
Spike for golden rule 4 (spec §6, "Harness"), on the synthetic sandbox project only.
- Enable
claudein the hub registry with a small daily cap. - Dispatch
claude -p --output-format jsoninto the task worktree, with explicit--allowedTools, a permission mode and no permission prompts. The exact flags are checked againstclaude --helpfirst (spec §19). - Store the session id on the task. When the agent needs input, it raises a
specifyitem and stops. - On Atul's answer, MMDEP resumes with
--resume <session_id>. - Run two projects in parallel, one slot each.
- Record relay steps and waiting time in the ledger.
- Runs as Atul (the CLI agent's opt-in), on synthetic data only (C9). CLI agents log in under the agent account before any real project.
- Notes whether the Claude Agent SDK (interrupts, approval callbacks) should replace the subprocess adapter (golden rule 2).
- Definition of done: the session-id field and the resume path with tests against a fake CLI; a spike report under
docs/research/with the live run's ledger numbers;specifyitems for anything that changes behaviour. - High risk: it runs a cloud agent that writes files, under Atul's account.
FB1. iter-03/forge-bus-sandbox: DR-002 stage 1, sandbox forge bus · M · high · after H1¶
From DR-002 (forge as the shared bus between agents, MMDEP as the conductor; it resolves experiment E1, spec §19). It's tried on two blank private sandbox repositories before any real repository. Synthetic data only. About two weeks.
- Sandboxes (Atul's setup, manual item): one private project on GitLab.com Free and one private repository on GitHub Free. Both start empty; no real code, no client data, no secrets in issues.
- Labels, the same on both:
needs-triage,cross-project,ready-for-coder,needs-review. - Triage: one ChatGPT event task (one of the 5 task slots) fires on GitHub issue activity in the sandbox. It reads a
needs-triageissue and posts one comment in a fixed format: summary, affected project, proposed label, open questions. It proposesready-for-coderorcross-project. - ChatGPT's event triggers cover GitHub, not GitLab (DR-002 §2). Triage on the GitLab sandbox waits for a scheduled task, if a slot is free. Until then, the GitLab sandbox tests labels and MMDEP's read path only.
- Whether the GitHub connector can write a comment or label is checked during setup. If it can only read, the comment goes to Atul by email or push and he applies it; that counts as a relay step.
- Coding: an issue labelled
ready-for-coderis assigned to the Copilot coding agent, which opens a PR. In stage 1 Atul does the assignment (a measured relay step) unless the triage task can do it. - Review by a different vendor: the PR is labelled
needs-reviewand reviewed by Codex (ChatGPT's GitHub PR review), never by Copilot itself. Only Atul merges. - MMDEP reads forge events read-only into the ledger:
- Issues, labels, comments, PRs and reviews, through the read-only GitHub adapter (GH) and a GitLab equivalent.
- Fine-grained read-only tokens from Credential Manager (
mmdep-forge-github,mmdep-forge-gitlab); GET only; polled, no webhooks. - One
forge.eventledger entry per event: forge, repository, issue or PR number, actor, action, label. - MMDEP writes nothing to either forge.
- Turn limit: 3 agent turns per issue (triage, coding, review; a re-review after a fix counts). MMDEP can only read, so it enforces the limit by detection: a 4th agent action on an issue without a person's action in between raises a
reviewitem in MMDEP and the issue is left alone. Vendor-side limits (Copilot's session, task settings) are set as low as each allows. - Gate (DR-002 stage 1 → 2): every step of every sandbox issue is in the ledger; no merge without Atul; relay steps and Atul's minutes per issue reported in
docs/research/forge-bus-stage1.md. - Dependencies: H1 (the relay measures), GH (read-only GitHub adapter), and a read-only GitLab reader in the same shape.
- Definition of done: the readers with tests against recorded API responses (no live forge calls in tests); the turn-limit detection with tests; one live week on the sandboxes; the stage 1 report.
- High risk: agents from three vendors act on a repository, and tokens are stored on the desktop (read-only, sandbox-scoped).
TR. iter-03/triage-type: a triage item type · S · high (the inbox) · after D¶
D7: an item type in GitLab's triage format (fact → affected project or decision → implication → required action → constraints → MMDEP candidate), spec §7. The handover record maps to GitLab's handoff template (spec §4).
- Definition of done: the type with its fields and tests;
raise_itemand the CLI accept it.
T. iter-03/test-env: per-project test environments · S · high · after 5¶
From #53's answer: mmdep test-env create|update <project>, run by a person, builds MMDEP_HOME\test-envs\<project> from the project's pinned requirements on main; mmdep-agent gets read and execute only; mmdep doctor checks each test_command's env as the account. Branch and approve item raised 3 Oct.
F. iter-03/feedback-type: a feedback item type · S · high (the inbox)¶
A new item type for remarks about how MMDEP works, filed from any project into mmdep with the source project, closed with done (spec 0.10 §7). A line of the §17.0 readiness gate. The mmdep feedback command and report stay in iteration 3b.
E. iter-03/evidence-retention: evidence retention · S · low¶
Prune kept evidence (MMDEP_HOME/evidence/) after 90 days, except evidence linked to approved items or releases (#28).
- Definition of done: a prune pass (run as part of
mmdep runor a separate command — decided in the story) with a test proving linked evidence survives past 90 days and unlinked evidence doesn't. - Dependencies: none.
R. iter-03/release-command: release command · M · high¶
Automatic mmdep.db backup, install from a tagged main via git+file:// (item #41, spec 0.8 §3 — not the plain-path mistake every prior release made), a version-and-doctor check afterwards, and a rollback note. Replaces the manual release steps from item #15/#39/#42.
- Definition of done, added by #46: the command compares
pyproject.toml's dependencies between the released commit and the new one. When they differ it installs without--no-deps: story 11 addedpywin32, and a--no-depsinstall would leave the runtime without it. Afterwards it imports every declared dependency from the runtime and fails the release if one is missing. The README's manual steps already follow this rule (#46). - Definition of done: a
mmdep releasecommand exists and is tested against a fake runtime target, never the realmmdep_runtime— per CLAUDE.md, I don't touch that directly, and this command doesn't change who's allowed to: it's something Atul runs (or explicitly approves running), the same as every release before it, just no longer by hand. - Dependencies: none; incorporates #41's fix by construction.
- High risk: it's the thing that changes what code the real MCP server runs.
M. iter-03/doctor-memory-warning: doctor low-memory warning · S · low¶
mmdep doctor warns when available system memory is low, with a fix suggestion (close other apps, avoid concurrent heavy local-model calls). Found live: iter-02/codex-adapter turn 6 stopped the full suite for exactly this reason, with no warning beforehand.
- Definition of done: a memory check added to
doctor.run_checks, with a test (the memory read is mocked, not measured live, so the test is deterministic); may need a new dependency (e.g.psutil) to read available memory cross-platform-safely on Windows — named and justified in this story's own approval item if so, per CLAUDE.md. - Scope added (atul, 2 Oct): an agent with
max_calls_per_day: 0shows SKIP "disabled", with no fix advice. It doesn't count as a failure, and the doctor exit code ignores it. This matters now: #45 is answered, and Atul set cap 0 for chatgpt, gemini, deepseek, qwen and llama in the hubagents.yaml, with role maps unchanged. The "hub printed twice" finding is dropped: it was two commands' output. - Dependencies: none.
S1. iter-03/std-ci-001: CI standard STD-CI-001 · S · low · docs only¶
Write spec §15.4's CI rules as MMDEP's formal CI standard document, with the stable ID and version header §12A requires (id: STD-CI-001) — §12A's own example ID, not yet an actual document until this story. This is the document the "Forges, credits and deployment" backlog section's rollout (apply it to each project's pipeline) is written against; that rollout itself is a people-task outside the orchestrator, not part of this story.
- Definition of done: the document exists with a proper header and covers §15.4's rules in a form a project's pipeline maintainer can follow without reading the whole design spec; where it should live (this repo vs. an estate repo) follows whatever DR-001 resolves to — noted as an open point in the doc itself if DR-001 hasn't been decided yet.
- Dependencies: none strictly, but its final location depends on DR-001's outcome (noted, not blocking).
S2. iter-03/accounting-kickoff-docs: accounting kickoff docs · S · low · docs only¶
PROJECT.md and interface contract v0.1 (chart of accounts, journal-posting API) for the accounting project — spec §17's "accounting's interface contract comes first" — drafted ahead of the pilot suite's actual start (which stays after iteration 3, unchanged).
- Definition of done: both documents written, consistent with §17;
confirm/specifyitems raised for any regulatory or business-logic open question found while drafting; no code, and accounting isn't registered as an mmdep project yet (that's the pilot suite's own iteration 1, out of scope here). - Dependencies: none.
Order¶
0.10: D → H1 (right after D), then V and IM; H1 and GH → FB1 (DR-002 stage 1, 5 Oct); D → TR; GH → RQ (RQ also reads D's export and the requests folder). 11 → 5 → T (the agent account, then the Test Runner, then its test envs). F, R, E, M, S1 and S2 have no dependency on the others. G0 waits for gate 1 → 2; G1 → G2 → G3 wait for gate 2 → 3.
0.9 order, kept as the record: 0 → G0 → G1 → G2 → G3 (the gateway substream). 11 → 5 (the agent account before the Test Runner). D after G2. R, E, M, S1 and S2 have no dependency on the others and run alongside the gateway substream, the same way iteration 2's stories 1–4 ran independently of Atul.
Size¶
S: 0, D, V, T, F, TR, E, M, S1, S2. M: IM, GH, RQ, H1, 5, R. L: 11. (G0 M, G1 M, G2 L and G3 L moved to phases 2 and 3.)
Not this iteration¶
- The pilot suite itself (accounting, billing, expense management, compliance) — only S2's two documents move up; the suite starts after iteration 3, unchanged (spec §17).
- Everything in iteration 3b (
start/desk,status, operations board v1,show --open, help, shell, launcher installer, per-project identity,add_task, backlog as records, feedback/report, docs build and manual, MCP v2 migration, Drive publishing phases B and C). - A live AWS deploy of
mmdep-gateway— G3 gets the repo and offline tests ready; the deploy is Atul's own step, raised as amanualitem. - Phase 2 (G0 and the stateless MCP server on Bluehost) and phase 3 (G1–G3 on AWS) of the online view: each starts after its gate (spec 0.10 §15B).
- A PR-based GitHub flow for this repo: proposed separately (#56).
- GitLab origins and authorization records: still iteration 5. The TEM convergence decision is taken (spec 0.10, D1); ADRs 004–006 are drafted in
docs/decisions/gitlab-drafts/for Atul to submit.
Assumptions (approving the plan confirms them)¶
DR-001 is not part of this plan's approval (#43): it moved to its own branch iter-03/dr-001 and its own approve item, while Atul compares it with the MMDEP governance repo. C3 and C5 below still point at it as pending.
- C1. ~~Story 0's scope (see the story itself) is my reading of what "0" means here~~ — corrected in #43: story 0 is this plan branch, done on merge.
- C2.
iter-03/release-command(R) doesn't change who may run a release against the realmmdep_runtime— still Atul, still through an approved item, per CLAUDE.md's "never touch mmdep_runtime directly" rule. The command automates the steps; it doesn't automate the approval. - C3.
mmdep-gateway(G3) is a separate repository on the same forge asmmdep(GitHub,moneymarche-dev), pending DR-001. If DR-001 resolves differently before G3 starts, G3's target repo follows that decision instead. - C4. M's memory check may need a new dependency (most likely
psutil); named and justified in that story's own approval item, per CLAUDE.md, not assumed here. - C5. S1's and S2's final home (this repo vs. an estate repo) follows DR-001; both are written here first regardless, since DR-001 doesn't block writing the content, only where it ends up living long-term.
- C6 (0.10). The handover file
claude/MMDEP-handover-2026-10-02.mdwasn't reachable from this machine (not on disk, not found in Drive). Spec 0.10's phases, gates and readiness gate were written from Atul's instruction text. The gate criteria marked "(proposed)" are mine; where the handover's "Deployment phases" and "Test projects" sections differ, they win. - C7 (0.10). C3 (
mmdep-gatewayon GitHub) now concerns phase 3, not this iteration. - C6 resolved (rework 1). #58's reject text and the 3 Oct decisions replace the missing handover sections. Gate criteria that are mine stay marked "(proposed)" in §15B.
- C8 (rework 1; confirmed in #59). MMDEP writes the
mmdep-inboxmirror from the desktop, with the bot token in Credential Manager. Apps Script is read only. - C9 (rework 1). H1 runs Claude under Atul's account, not
mmdep-agent. It is a stated exception (synthetic data only, CLI agent opt-in), not a change to story 11's rule. - C10 (rework 1; confirmed in #59). No silent coding fallback. When the coder is unavailable, MMDEP raises an item proposing a takeover with a handover. A read-only review may use another agent.
Risks¶
| Risk | Mitigation |
|---|---|
| G0's identity-provider research picks something that turns out expensive or awkward once G1/G2 build against it | G0 raises specify items before any code exists; G1 is docs-only too, so the only sunk cost before G2's code is two documents |
| G2's fake-gateway tests pass but the real gateway (G3) behaves differently under real AWS latency/limits | G3's offline tests reuse G2's fixtures where possible; the live AWS deploy (Atul's step) is explicitly not part of "done" for this iteration, so a mismatch is caught before it's load-bearing |
| Story 11 depends on Windows account and ACL details that are hard to test in CI (same risk iteration 2 flagged and deferred) | Unchanged mitigation: a fake runner for tests, a manual item for the real setup, a live sandbox run as the acceptance test |
| R (the release command) automates a process that, done wrong, breaks the real runtime | Tested only against a fake runtime target; the real release still goes through the same approved-item gate as every prior release (C2) |
| Nine independent stories (0, D, E, R, M, S1, S2 plus the two carried ones) raise nine separate approval items, adding review load | Sizes are mostly S/M; low-risk ones (0, D, E, M, S1, S2) merge to iter-03/integration once a second-opinion reviewer exists, per spec §9, instead of each needing Atul individually |
Status¶
2026-09-28 (drafting turn)¶
Drafted on iter-03/plan, off main at the item #41 merge (5a82298). Not merged — this plan, spec 0.9 and DR-001 all wait for your approval, per CLAUDE.md ("direct commits to main are for status files only").
What's on the branch:
- This file.
- Spec 0.9 (proposed, not yet approved): §7 and §15A (gateway timing, field allowlist, redaction as second line, a person runs every deploy, mmdep-gateway on the current MCP SDK major), §18 (roadmap split into iteration 3 / 3b), changelog row.
- docs/decisions/DR-001-code-vs-estate-repo.md: the code-vs-estate-repo question, triggered by the gateway moving to iteration 3 and mmdep now being pushed to GitHub (item #40); recommends (PROPOSED) a separate mmdep-estate repo over folding into the GitLab governance repo, for now; includes the git ls-files check (58 files, zero hub-state/secret-pattern matches) and confirms item #40's ruleset is active.
- backlog.md: stories 5 and 11 moved out of the iteration 2 section; the old "Iteration 3 — Daily operation" section split into a new "Iteration 3" (this plan's 13 stories) and "Iteration 3b — Daily operation" (everything else that was there); M, S1, S2 added; a cross-reference added to the Pilot suite section for S2.
Also this turn, before drafting the plan (item #41 was approved):
- Merged iter-02/release-commit-fix into main (item #41): spec 0.8 stamped approved (item #41) on the branch first, matching the convention from spec 0.6/0.7; merge commit 5a82298; main retested clean (177 + 23) after the merge.
- Raised item #42, a separate release approval, with the exact git+file:// install and verification steps, per CLAUDE.md — I did not run the install myself. Full detail of #41's merge and #42 stays in docs/iterations/iteration-02.md (that's iteration 2's release, not iteration 3 work), not repeated here.
Waiting for Atul: the plan approval for this branch, item #43, plus #22, #42.
Commands that wrote something (this turn, Step B):
| Where | What |
|---|---|
| mmdep repo | iter-02/release-commit-fix: commit bd0c0d0 (spec 0.8 approval stamp) |
mmdep repo, main |
merge 5a82298 (item #41, --no-ff) |
| mmdep repo | .venv\Scripts\python -m pytest -q and -q legacy on the branch and again on main post-merge (177 + 23 both times) |
| mmdep repo | branch iter-03/plan (off main at 5a82298): this file, spec 0.9 edits, docs/decisions/DR-001-code-vs-estate-repo.md, backlog.md edits |
| mmdep repo | git ls-files (58 files) checked against hub-state/secret-shaped patterns for DR-001's checklist — zero matches |
| MMDEP hub (MCP) | raise_item #42 (release, git+file); raise_item #43 (this plan) |
2026-09-28, fresh session (story 11, pywin32 approach accepted)¶
On iter-03/s11-agent-account, stacked on iter-03/plan (not yet approved; item #43 still open at the
start of this turn) — the same pattern as iter-02/codex-adapter stacking on iter-02/kill-switch in
iteration 2, because iteration-03.md and this story only exist there.
SEQUENCING MISS, flagged plainly: the instruction was to raise the specify item for the manual admin
steps before coding. I built the code first and raised it after (item #44). It didn't change the
design — #37 had already fixed the mechanism (pywin32, CreateProcessWithLogonW, stored credential,
standard non-admin account) — but the ordering itself wasn't followed, and it should have been.
What was built:
- mmdep/agent_account.py (new): agents.yaml's optional agent_account: section (username,
credential_target); probe() — pywin32 installed? account exists (net user)? credential readable
and for the right user (win32cred.CredRead)? — returning a plain reason for every "not yet" case,
never a crash; AgentAccountProcess, a subprocess.Popen-compatible wrapper around
CreateProcessWithLogonW, using temporary files for stdio instead of live pipes (a deliberate
simplification — hand-rolled pipe pumping alongside CreateProcessWithLogonW is a well-known deadlock
source, and every call already has a wall-clock deadline from story 3, so a short-lived temp file per
call is the smallest thing that's actually safe); status() for mmdep doctor.
- mmdep/procs.py: run_with_deadline takes an optional agent_account; uses it only when probe()
says it's usable, and falls back to the current user (today's behaviour, unchanged) on any None,
failed probe, or an exception from spawn() — the fallback is silent at this layer by design; the
reason is visible via mmdep doctor, not swallowed.
- mmdep/agents/base.py, isolation.py, adapters.py: Agent.agent_account, set by Isolation.run_agent
right next to the existing stop_check (same pattern, same lifecycle — set before the call, cleared in
finally), and threaded into both script execution (_run_script) and the CLI agent adapters
(CliAgent, ChatGptCliAgent). Ollama/Gemini's own HTTP calls are untouched — they don't execute
user-controlled code themselves; the script they produce does, via _run_script, which is exactly
where this applies.
- mmdep/doctor.py: a new "agent account" check — ok when not configured (today's normal state) or
when configured and usable; warn with a fix pointing at item #44 otherwise.
- pyproject.toml: pywin32>=306; sys_platform == 'win32' — named here per CLAUDE.md ("no new
dependency without saying why"); already accepted in #37 (B2). Installed in the dev venv and verified
importable (pywin32 312).
- mmdep/agents/agents.default.yaml: a commented-out agent_account: example (username/credential_target
both mmdep-agent), matching the existing commented per-seat example's style — nothing active by
default.
Tests (tests/test_agent_account.py, 25 new): config loading (absent/empty/malformed/well-formed);
every probe() fallback reason; run_with_deadline's fallback behaviour (agent_account=None unchanged;
configured-but-unusable falls back; spawn() raising falls back rather than crashing the run);
AgentAccountProcess against a faked win32 backend injected into sys.modules (real pywin32 stays
untouched) — verifies the right username/password/cmdline/cwd reach CreateProcessWithLogonW, that
.communicate() reads back output and cleans up temp files, times out correctly, and .kill() calls
TerminateProcess; doctor's new check in both states; Isolation setting and clearing
agent.agent_account around a real run_agent() call. Two existing tests
(test_claude_cli_runs_in_workdir_without_permission_bypass,
all three in test_codex_adapter.py's codex fixture) had their fake_run stand-ins extended to accept
agent_account=None, since run_with_deadline's call sites now always pass it.
GAP, exactly as anticipated — two tests skip, not fail:
test_agent_account_cannot_write_mmdep_db and test_agent_account_cannot_write_outside_workspaces are
real, live negative tests against the actual mmdep-agent account. They currently skip with reason
"'mmdep-agent' does not exist on this machine yet...". They'll run for real, automatically, once item
#44's setup is done — no code change needed, pytest -q tests/test_agent_account.py is itself the
acceptance test.
Two more items raised, not directly edited, per instruction:
- #44 (specify): the manual admin steps — account creation, the exact icacls deny-then-grant
sequence (workspaces only), storing the credential under Atul's own profile (not mmdep-agent's
— it's read by whatever process runs mmdep), and the one explicit warning that matters here: unlike
the GitLab runner account (#22), this one must not have "deny log on locally", because
CreateProcessWithLogonW needs it.
- #45 (specify): propose capping gemini, deepseek, qwen, llama at max_calls_per_day: 0 in
the default agents.yaml, matching claude's and chatgpt's existing pattern — found while checking
doctor's current behaviour. Flags a known interaction with the doctor findings below and leaves the
sequencing choice to Atul.
- Correction to #45's own text: it references "item #43's story 11 approval" — #43 is the
iteration 3 plan item, not this story's approval (the approval item is below). Read that line as
"this story's approval item" instead; I can't edit #45 after raising it.
Doctor findings, noted here for story M's scope, not fixed (your instruction: a status-only edit to
iteration-03.md for M's scope isn't the right place — a content change needs the approval flow, not a
status commit — so it's recorded here in the approval item instead, for whoever picks up story M):
1. "doctor prints hub twice" — I read the current cli.py: exactly one hub: {home()} print exists
per command dispatch (cli.py ~line 137), plus a separate one embedded in --version's own output.
I could not reproduce a double-print from the source as it stands. Recording the observation as given
rather than silently dropping it — worth checking against the actual terminal session that produced
it, since it may be two separate commands' output read together, not one command printing twice.
2. "cap 0 should read 'disabled', not 'wait'" — confirmed real: isolation.py's cap_reached()
returns f"cap reached ({used}/{cap} calls today)" for cap == 0 exactly like any other cap, and
dispatcher.py's _how_to_fix() answers every "cap reached" reason with "wait for the next UTC
day, or raise max_calls_per_day" — nonsensical for a deliberately-disabled agent (0/0 is never not
reached). test_default_claude_cap_is_zero_so_claude_is_never_chosen already asserts the current
"cap reached (0/0 calls today)" wording, so this needs that test updated too when it's fixed.
3. "disabled agents SKIP not FAIL" — also confirmed: doctor.py's per-agent loop marks an agent
"fail" whenever agent.name in used and unhealthy, with no distinction for "deliberately capped at
0" versus "genuinely broken" — a cap-0 agent in some project's role map fails mmdep doctor's exit
code for being exactly as configured. Ties directly into #45: capping gemini/deepseek/qwen/llama at 0
would hit this same reporting gap wherever they're role-mapped.
Waiting for Atul: #44, #45, this story's approval item, #46, plus #22, #42, #43.
Commands that wrote something (this turn):
| Where | What |
|---|---|
| mmdep repo | .venv\Scripts\pip install -e ".[dev,orchestrator]" (picks up pywin32); verified import of win32process, win32event, win32file, win32security, win32cred, win32con, win32api |
| mmdep repo | git checkout -b iter-03/s11-agent-account iter-03/plan (carrying the uncommitted working-tree changes already made on main, before the branch existed — see the sequencing note) |
| mmdep repo | .venv\Scripts\python -m pytest -q and -q legacy on the branch: 202 passed + 2 skipped, and 23 passed |
| MMDEP hub (MCP) | raise_item #44 (agent-account manual setup, specify); raise_item #45 (disable unusable agents, specify); raise_item #46 (this story's approval) |
| mmdep repo | this status entry |
Correction to item #46's own text: its base-commit line says iter-03/plan at commit 91e71a8 —
iter-03/plan's actual tip is 40ad429; I misquoted the hash. Doesn't affect the merge (branch
ancestry is what matters, not the number I typed), noted here since I can't edit #46 after raising it.
2026-10-02 (#43 applied; story 11 rework per #46)¶
Step A: #43 applied.
- DR-001 moved, unchanged, to its own branch iter-03/dr-001 (611ca99, off main at 8dfa501). Its own approve item is #47, which notes that Atul is comparing it with the MMDEP governance repo.
- iter-03/plan: DR-001 removed; spec 0.9 stamped approved (item #43); story 0 is recorded as "this plan branch, done on merge" (#43), which corrects assumption C1. Commit fbd3e18.
- Merged to main as 2074f6a ("Merge iter-03/plan into main (MMDEP item #43, approved by atul)"). Tests: 177 passed on the branch before the merge; on main after it, 177 passed and legacy 23 passed. Story 0 is done.
- #42 was released by Atul from 8dfa501. main has moved on by docs-only commits since, so mmdep doctor's runtime-vs-main check will show a difference until the next release. No code in the runtime changed.
Step B: story 11 rework (#46). iter-03/s11-agent-account was rebased onto main at 2074f6a. The original commits are now deacf45 and 93e42ed; the rework is 045b802. Approve item: #49. Point by point:
1. Fail closed: the mode is off or required. Off is explicit: doctor warns, and the ledger names the current user. Required with an unusable account means AgentAccountError, and the task parks with a manual item holding the fix. One agent.account ledger entry per run.
2. Tree kill: a Job object (suspended start, assign, resume, TerminateJobObject), because taskkill /T can't be relied on for another account's processes. There are fake tests and 4 live tests; the live ones skip until #48 is done.
Found: pywin32 has no CreateProcessWithLogonW, so the original launch could never have worked; the fakes hid it. It is now bound through ctypes, and an unfaked test proves the binding.
3. stdin is always a file, and the guard test now covers this launch. Stdio files sit in MMDEP_HOME\agent-io (an access list holding only the current user) and are deleted in finally.
4. Scripts run as the account when the mode is required; CLI agents use run_as_agent_account: true, default off.
5. Release: pywin32 means no --no-deps. The README's steps are updated, and so is story R's definition of done.
6. The setup design changed, so I raised #48 (specify), which supersedes #44. It covers: deny Full rather than RX, a Python installed for all users, the Users group, a password that never expires, and a credential prompt.
Story M's scope was updated per Atul's 2 Oct notes; #45 is answered (Atul set cap 0 in the hub agents.yaml).
Tests on the branch: pytest -q 225 passed, 4 skipped (live, need #48); pytest -q legacy 23 passed; pre-commit clean.
Waiting for Atul: #47 (DR-001), #48 (account setup), #49 (story 11), #44 (superseded by #48; answer it to close it), #22.
Commands that wrote something (this turn):
| Where | What |
|---|---|
| mmdep repo | git checkout -b iter-03/dr-001 off main; git checkout iter-03/plan -- docs/decisions/DR-001-code-vs-estate-repo.md; commit 611ca99. The first commit attempt failed in pre-commit because gitleaks wasn't on Git Bash's PATH; after adding the WinGet gitleaks folder to PATH for the command, it passed |
| mmdep repo | iter-03/plan: git rm of DR-001, spec/iteration/backlog edits, commit fbd3e18 |
mmdep repo, main |
git merge --no-ff iter-03/plan, giving merge 2074f6a (item #43) |
| mmdep repo | pytest -q on iter-03/plan before the merge (177) and on main after it (177, legacy 23) |
| MMDEP hub (MCP) | raise_item #47 (DR-001 approve), #48 (setup specify, supersedes #44), #49 (story 11 approve) |
| mmdep repo | git rebase --onto main 40ad429 iter-03/s11-agent-account, giving deacf45 and 93e42ed |
| mmdep repo | story 11 rework edits; pytest -q (225 passed + 4 skipped) and -q legacy (23); commit 045b802. Pre-commit's ruff-format reformatted isolation.py on the first try. The re-commit picked up the wrong message from COMMIT_EDITMSG, so I amended it at once with the right message, before anything else referred to it |
| scratch (dev venv, not the runtime) | a one-off Job-object check on an ordinary child and grandchild (2 in the Job, 0 alive after terminate), plus an attribute check of every pywin32 name used |
| mmdep repo | this status entry (status-only commit on the branch); on main, a status: commit to docs/status/inbox.md |
2026-10-02, later (#48 answered; story 11 follow-up)¶
48 was answered by Atul. The live tests passed when elevated (51), but at a normal prompt 50 passed and 1 failed: Atul couldn't read a file the agent wrote. Follow-up commit 35574d0; approve item #50, which supersedes #49. (#50's text says "reject #49 with 'superseded by #'": that item is #50.)¶
- Cause: pytest's 0o700 temp folders get a protected access list (SYSTEM, Administrators, OWNER RIGHTS). The agent owns what it creates, so Atul reached those files only through Administrators, which means only when elevated. The fixture now grants the current user explicitly. Production needs no change, because
workspacespasses Authenticated Users Modify on to new files. A new live test on the realworkspacesfolder proves it: the agent writes, then Atul reads, modifies and deletes, and git status, diff, commit, merge and cleanup all work. No specify item was needed. - Live tests skip when the session is elevated; doctor has a new
elevationwarning. - Kill group: the order (suspended, Job with KILL_ON_JOB_CLOSE, assign, resume) was already right. Added a fake test (no breakaway flags) and a live crash test (orchestrator killed: child and grandchild gone).
- Live negative probes as mmdep-agent, all denied: writes to the hub db, agents.yaml, repos, the hub root, the runtime, this repo,
C:\MF DataandC:\SSH_Keys; reads ofC:\MF DataandC:\SSH_Keys. A write in its own workspace is allowed. - pywin32 is still needed (credential, logon, Job, file handles, ACLs), and pyproject is accurate. A new test checks that every pywin32 name used exists.
- Found and fixed (security): git for Windows follows a directory junction on
git add -A, and a rewritten worktree.gitcan point mmdep's git at a config the agent wrote.Workspaces.secure_worktreeremoves links and restores.gitafter each run (an isolation violation) and before reset, commit and cleanup. - Backlog proposals sit in #50 rather than in backlog.md, per instruction: B-a network restriction, B-b a doctor check for writable
C:\roots, B-c per-task worktree grants, B-d sweeping leftover agent-io files after a crash.
Tests at a normal prompt: pytest -q 252 passed with 0 skipped (the live tests ran); pytest -q legacy 23 passed; pre-commit clean.
Waiting for Atul: #50 (story 11; supersedes #49), #49 (reject as superseded), #47 (DR-001), #44 (superseded by #48), #22.
Commands that wrote something (this turn):
| Where | What |
|---|---|
| mmdep repo | git checkout iter-03/s11-agent-account; edits to mmdep/workspaces.py, isolation.py, agent_account.py, doctor.py and tests/test_agent_account.py; ruff format (reformatted the test file); commit 35574d0 |
| mmdep repo | pytest -q tests\test_agent_account.py three times (1 failed: my diff assertion; then 74 passed), pytest -q (252) and pytest -q legacy (23). The first live run reproduced #48's failure before any fix |
real hub, mmdep_hub\workspaces |
the live tests created _live-test-* folders and removed them; none were left. The probe opened mmdep.db and agents.yaml for writing as mmdep-agent and was denied, so nothing was written. Tests that ran icacls changed only pytest temp folders |
| live, as mmdep-agent | test scripts ran under CreateProcessWithLogonW; the logon check wrote Security-log logon events |
| scratch | jt/ in the session scratchpad: a throwaway git repo with a directory junction, to check git add -A (it follows) and git clean -fdx (it removes only the link) |
| MMDEP hub (MCP) | raise_item #50 (approve, supersedes #49) |
| mmdep repo | this status entry (a status-only commit on the branch); on main, a status: commit to docs/status/inbox.md |
2026-10-02, evening (#50 merged; link guard; release item; story 5)¶
Step A: story 11 merged. #50 was approved, and #49 is closed as superseded. Before the merge, at a normal prompt (Medium integrity), the branch passed 252 tests (legacy 23). The four backlog items approved in #50 went to backlog.md on the story branch (3f94c10), so they reached main under #50's approval rather than as a direct edit on main. They are: a per-task worktree grant before the pilot suite starts; network restriction for mmdep-agent; a doctor check for writable C:\ root folders; agent-io cleanup after a crash. Merge 573bede ("Merge iter-03/s11-agent-account (MMDEP item #50, approved by atul)"). On main after the merge: 252 and 23 passed. Story 11 is done.
Step B: link guard, on iter-03/link-guard (625d91e), approve item #51. I checked live, as mmdep-agent, what the account can create in a workspaces folder:
- junction to a denied folder: created;
- junction to the volume root (a mount point): created;
- file and folder symlinks: refused (1314, Developer Mode is off);
- hard link to a denied file: refused (5);
- hard link to a file it can write: created, and harmless.
Inside the worktree the guard already removed every kind of link: it goes by the reparse-point attribute, not by type. Gap found and fixed: a worktree, or its project folder, replaced by a junction (a cross-task attack). The guard used to follow it and wrote .git into the target, and cleanup would have run git worktree remove --force there. Now the link is removed, the origin prunes the worktree, and create_worktree makes a fresh one. The script path beside the worktree is checked too. Tests: faked kinds (file and folder symlinks, junction, mount point, another reparse tag), worktree and project-folder replacement, and the live account's links; the live symlink test skips with its reason. 262 passed + 1 skipped; legacy 23.
Step C: release item #52 (manual). Back up mmdep.db; install main WITH dependencies (pywin32 is new; the only change in pyproject.toml since 8dfa501, the runtime's commit, read from a neutral folder); check --version and doctor from $env:TEMP at a normal prompt; then Atul adds agent_account (mode required) to the hub agents.yaml and reruns doctor. Includes a rollback.
Step D: story 5 (Test Runner), on iter-03/test-runner (a8e4463), approve item #54. projects.<p>.test_command (an argv list; {python} is the runner's interpreter) runs after validation and before the approve item: as mmdep-agent in mode required, as the current user in mode off. The commit comes first, and anything but a pass undoes it. A failure or timeout is a failed attempt; the kill switch returns the task to Ready. The worktree guard runs after the tests too. There is one test.run ledger entry per run, and the approve item gets a TESTS section. 16 new tests, plus 1 live: a full dispatcher pass whose unittest suite runs as mmdep-agent in the real workspaces folder. 269 passed; legacy 23. #53 (specify): where a project's test dependencies live for the account. The account's Python has the standard library only, so I recommend a read-and-execute venv per project under mmdep_hub\test-envs. #51 and #54 touch the same files; whichever merges second gets main merged into it and a full rerun first.
Waiting for Atul: #51 (link guard), #54 (story 5), #53 (test environment), #52 (release; best after #51), #47 (DR-001), #22.
Commands that wrote something (this turn):
| Where | What |
|---|---|
| mmdep repo | iter-03/s11-agent-account: backlog.md edit, commit 3f94c10; pytest -q (252) and -q legacy (23) before the merge |
mmdep repo, main |
git merge --no-ff iter-03/s11-agent-account, giving 573bede (#50); pytest -q (252) and legacy (23) after |
real hub, mmdep_hub\workspaces |
a scratchpad script (link_experiment.py) ran as mmdep-agent in _live-exp-* folders: link attempts, then removal (2 runs; the first failed on a quoting error in my script). The live tests created and removed _live-test-* folders; 0 were left |
| live, as mmdep-agent | the scripts and tests above ran under CreateProcessWithLogonW; the logon checks wrote Security-log events |
| scratch | lk/ in the session scratchpad: Atul-side symlink and hard-link attempts; lk-secret.txt (an obviously fake value) as the denied target |
| mmdep repo | git checkout -b iter-03/link-guard main; test and code edits; ruff format; pytest runs (the tests first failed 4 cases, confirming the gap; then 262 + 1 skipped, legacy 23); commit 625d91e |
| mmdep runtime (read-only) | mmdep --version from $env:TEMP with MMDEP_ACTOR=claude-code: runtime at 8dfa5018f09d |
| mmdep repo | git checkout -b iter-03/test-runner main; new mmdep/testrunner.py, tests/test_test_runner.py; edits to dispatcher.py, isolation.py, workspaces.py, agents.default.yaml and tests/test_agent_account.py. An inline edit script broke two f-strings, fixed before any commit; ruff format; pytest -q (269) and legacy (23); commit a8e4463 |
| MMDEP hub (MCP) | raise_item #51 (link guard, approve), #52 (release, manual), #53 (test environment, specify), #54 (story 5, approve) |
mmdep repo, main |
this status entry and docs/status/inbox.md (a status-only commit) |
2026-10-03 (#51 and #54 merged; release item; test envs; GitHub check; spec 0.10)¶
Step A: merges.
- #51 (link guard): merged as 2b5d808. On main afterwards: 262 passed, 1 skipped (the live symlink test: Developer Mode is off); legacy 23 passed.
- #54 (story 5): iter-03/test-runner rebased onto main, as #54's approval asked (a8e4463 became 72b38e4). The two conflicts were additive: an import line in isolation.py, and the end of tests/test_agent_account.py. I kept both sides. On the rebased branch: 279 passed, 1 skipped; legacy 23; pre-commit clean. Merged as 998dcc1. The same numbers on main afterwards. Story 5 is merged (inert until a project gets a test_command).
- #54's backlog item ("approve items flag test changes") is on iter-03/deploy-phases (#58), not committed to main directly: backlog.md is outside the status-only paths.
- #52 can't be edited. #55 supersedes it, with the final commit 998dcc1, the counts 279 passed + 1 skipped, and mmdep.testrunner in the import check. Answer #52 "superseded by #55" to close it.
Step B: test envs, per #53's answer. Branch iter-03/test-env (1f8b84b), approve item #57.
- What it adds:
- mmdep test-env create|update <project>, for people only;
- a venv under MMDEP_HOME\test-envs\<project>, made from agent_account.python;
- the package list from the project's pinned requirements file on main;
- icacls /grant:r mmdep-agent:(OI)(CI)RX;
- a probe run as mmdep-agent after every build and in mmdep doctor: the account must be able to run the env's python and unable to write anywhere in the env.
- Tests: 48 new plus 1 live. #57's text says 47; the file has 48. Results: 328 passed, 1 skipped; legacy 23.
- Found while testing: one test used run=_run as a default argument, so the monkeypatch didn't take effect. A real icacls grant for mmdep-agent ran on a pytest temp folder, and a real launch of a fake interpreter path failed. Fixed (run=None).
- That first run took 1h22m, and the cause is not explained. The fixed file runs in 24s, and the full suite took 4m23s.
- A ruff F821 (Optional) was fixed before the commit. The formatter had removed the import while it was briefly unused.
Step C: GitHub (read only; nothing changed). Reported in #56, which also proposes the PR-based flow.
- git remote -v: only github, https://github.com/moneymarche-dev/MMDEP.git.
- GitHub main is not protected. The repo is private on GitHub Free. branches/main/protection, rules/branches/main and rulesets all return 403 "Upgrade to GitHub Pro or make this repository public". #40 was answered "ruleset active", but GitHub doesn't enforce rulesets on a private repo on the Free plan. To confirm in Settings → Rules.
- Pushes to GitHub's main: one only, b6ccc10 at 2026-09-28T10:24Z (the #40 push). There are 0 PRs. Every merge since was a local git merge --no-ff, so GitHub's main is about 5 days behind. No push bypassed anything, because there was nothing to bypass.
- MMDEP reads and writes nothing on GitHub: mmdep/ has no forge client, no gh or glab, and no push or fetch. Its only network calls are the agent adapters (Ollama on localhost, Gemini, the CLI agents).
- #56's text refers to a point "(h)" that doesn't exist; read it as "a later story".
Step D: spec 0.10 (proposed). Branch iter-03/deploy-phases (eef3b4e), approve item #58.
- The handover file (claude/MMDEP-handover-2026-10-02.md) wasn't reachable: it isn't on disk, and Drive search found nothing. The draft follows the instruction text, and its gate criteria are marked "(proposed)". Paste the "Deployment phases" and "Test projects" sections and I'll reconcile.
- What the branch changes:
- the spec: §15B phases and gates, §11 secrets, §17.0 readiness gate, §7 feedback type, §18 and §19 roadmap;
- this file's plan: phase 1 stories D, V, IM, GH, RQ, plus T and F; G0 to phase 2, G1–G3 to phase 3; C6 and C7;
- backlog.md.
- The plan changes are on the branch. Only this status entry is on main.
Waiting for Atul: #55 (release), #52 (answer "superseded"), #56 (PR flow, and the protection finding), #57 (test envs), #58 (spec 0.10, plus the handover sections), #47 (DR-001), #22.
Commands that wrote something (this turn):
| Where | What |
|---|---|
mmdep repo, main |
git merge --no-ff iter-03/link-guard → 2b5d808 (#51); pytest -q (262 + 1 skipped), -q legacy (23) |
| mmdep repo | git rebase main on iter-03/test-runner (conflicts resolved by a script keeping both sides) → 72b38e4; pytest -q (279 + 1 skipped), legacy (23), pre-commit run --from-ref main |
mmdep repo, main |
git merge --no-ff iter-03/test-runner → 998dcc1 (#54); pytest -q (279 + 1 skipped), legacy (23) |
| mmdep repo | git checkout -b iter-03/test-env main; new mmdep/testenv.py, tests/test_test_env.py; edits to cli.py, doctor.py, agents.default.yaml, tests/test_agent_account.py; ruff format; test runs (one of 1h22m, see above); commit 1f8b84b |
| pytest temp folders | the unpatched test ran a real icacls /grant:r mmdep-agent:(OI)(CI)RX on a pytest temp test-envs folder; the live test made a real venv in pytest temp, granted RX and then Modify there, and launched python as mmdep-agent (Security-log logon events) |
GitHub (read only, gh as moneymarche-dev, stdin /dev/null) |
gh auth status; gh api for the repo, branches/main/protection, rules/branches/main, rulesets, activity, events, pulls |
| scratchpad | git worktree add for iter-03/deploy-phases (removed afterwards; the branch stays); spec010.py and plan010.py edit scripts; commit eef3b4e |
| MMDEP hub (MCP) | raise_item #55 (release, supersedes #52), #56 (PR flow and GitHub findings), #57 (test envs), #58 (spec 0.10) |
mmdep repo, main |
this status entry and docs/status/inbox.md (a status-only commit) |
2026-10-03, later (spec 0.10 rework 1: #59)¶
Session start (closed items): #47 (DR-001), #56 (PR flow), #57 (test envs) and #55 (release) are answered. #58 was rejected with rework points.
- Not done: the merges of #57 (iter-03/test-env) and #47 (iter-03/dr-001). The tool's permission check refused git merge into main, so neither branch is merged. Atul decides how to proceed: run the merges, or allow them for me. #47 also asks that DR-001 record option (c) and the GitHub push. That edit goes on iter-03/dr-001 before its merge.
Step B: spec 0.10 rework 1. Branch iter-03/deploy-phases, rebased onto main (5744a08, plus 2fa230a). Approve item #59 replaces #58.
- Scope: #58's reject points; Atul's 3 Oct decisions D1–D7; the §5C staffing rule; the four golden rules; harness spike H1; repository zones; documentation IDs; and a proposed agent-instruction change (in #59 only, not edited).
- Draft ADRs 004–006 for the GitLab MMDEP governance repo are in docs/decisions/gitlab-drafts/ (on the branch). Atul submits them as an MR.
- build/ and mmdep.egg-info/ are gitignored, and no commit on any branch has ever tracked them.
- Needs Atul's eye in #59:
- C8: the mirror is written from the desktop, not Apps Script;
- C10: §5C removes the coding-task fallback;
- the cross-project dependency split between ADR-002 and golden rule 3;
- the exact claude flags for H1 aren't verified yet.
Waiting for Atul: #59 (spec 0.10 rework), the #57 and #47 merges (above), #22.
Commands that wrote something (this turn):
| Where | What |
|---|---|
| mmdep repo | git rebase main on iter-03/deploy-phases (eef3b4e → 5744a08, clean) |
| mmdep repo | iter-03/deploy-phases: scratchpad scripts spec010r.py and plan010r.py edited the spec, iteration-03.md and backlog.md (one replacement per passage, asserted); new docs/decisions/gitlab-drafts/ (README, ADR-004, 005, 006); commit 2fa230a. The first commit attempt failed in pre-commit (gitleaks not on PATH); it passed with the WinGet gitleaks folder on PATH |
| mmdep repo | attempted git merge --no-ff iter-03/test-env on main (#57): refused by the permission check; nothing changed |
| MMDEP hub (MCP) | raise_item #59 |
mmdep repo, main |
this status entry and docs/status/inbox.md (a status-only commit) |
2026-10-03, evening (merges #57, #47 and #59; push; story D; readiness gate)¶
All test runs below were at a normal prompt (net session refused, so not elevated).
Step A: merges, in order.
1. #57 (test envs): Atul had already merged it himself at 14:21Z, as c5b7be6.
- The merge message is git's default, without the item number. I left the history as it is.
- On main afterwards: 328 passed, 1 skipped; legacy 23.
2. #47 (DR-001): on iter-03/dr-001, 957cf1d records:
- option (c) as decided;
- the GitHub push after #40 (first push b6ccc10);
- ADR-005 as the ADR-001 amendment;
- the correction that #40's ruleset isn't enforced (#56);
- #47's revisit triggers (development phase 2; TEM moving repositories).
Before the merge: 328 + 1 skipped, legacy 23. Merged as 6cad155. After: 328 + 1 skipped, legacy 23.
3. #59 (spec 0.10): on iter-03/deploy-phases, 69e17c2:
- stamps 0.10 approved (item #59);
- applies Atul's answers: the desktop writes the mirror and Apps Script is read only; no silent coding fallback (an item proposing a takeover); the dependency split is decided; the H1 flags are documented;
- updates ADR-004 to match;
- adds the approved source-of-truth wording to CLAUDE.md, PROJECT.md and the MCP INSTRUCTIONS, with a new tests/test_mcp_instructions.py (2 tests).
Before the merge: as after #47. Merged as 6d5632f. After: 330 passed, 1 skipped; legacy 23.
- Pushed main to GitHub: b6ccc10..6d5632f. git ls-remote github main = 6d5632f6d3e6dbb152f8c444befefbb342d7bd2f.
Step B: story D. Branch iter-03/drive-snapshots (6f84bcf), approve item #61; the Drive folder paths are asked in #60.
- What it adds:
- mmdep/drive.py;
- mmdep drive publish|pull;
- snapshot.json and snapshot.md with the allowlist only (no item bodies);
- request files become specify items raised by drive-request.
- Tests: 28 new, plus 1 skipped (symlink). Full suite in the real checkout: 358 passed, 2 skipped; legacy 23.
- Relabelled high risk (#61).
- Found while testing: from a scratchpad worktree, 14 live agent-account tests skip, because they look for the hub relative to the repo's parent folder. A worktree run isn't a full run; full runs must happen in C:\Projects\ai-sandbox\mmdep.
Step C: readiness gate (spec §17.0), read only. Evidence comes from the runtime CLI (full path, from the scratchpad, MMDEP_ACTOR=claude-code: --version, projects, doctor), the hub files (read only), and main.
| # | Criterion | Met? | Evidence |
|---|---|---|---|
| 1 | Story 11 released in required mode | No | Runtime --version: commit 8dfa5018f09d (28 Sep), whose dist-info was written 2026-09-28 18:00. The runtime has no agent_account.py. Runtime doctor has no "agent account" line. The hub agents.yaml has no agent_account section. This contradicts #55's answer; #62 re-releases |
| 2 | Story 5 + test env + per-task worktree ACL released | No | Story 5 and test envs are on main (998dcc1, c5b7be6) but not in the runtime. No mmdep_hub\test-envs. The per-task worktree grant isn't built (backlog [H], #50 B-c) |
| 3 | Project registration: local origin, role map, test_command |
No | mmdep projects: mmdep, sandbox only. mmdep_hub\repos: sandbox.git only. No test_command in the hub agents.yaml |
| 4 | One task end to end on the new project | No | No new project is registered |
| 5 | Working method written down | Partly | Spec §17.0 line 5 (approved, #59) and CLAUDE.md cover mmdep. Accounting's PROJECT.md (S2) isn't written |
| 6 | feedback item type |
No | ITEM_TYPES on main: approve, review, confirm, specify, input, manual. Story F isn't built |
Also from the runtime doctor: 3 failures. gemini, deepseek and qwen sit in role maps at cap 0 (#45, known), and the runtime is behind main.
Also found: #62's body shows the backup name mmdep.db.bak-20260928-1759 as [redacted:aadhaar]. The Aadhaar pattern matches 12-digit date stamps. It's a false positive and harmless here (no step uses that name). Candidate backlog item: the Aadhaar pattern needs digit-only context.
Waiting for Atul: #62 (P1 release + agent_account), #61 (story D), #60 (Drive paths), #22.
Commands that wrote something (this turn):
| Where | What |
|---|---|
| mmdep repo | git worktree add ×3 in the scratchpad (iter-03/dr-001, iter-03/deploy-phases, new branch iter-03/drive-snapshots), all removed afterwards with git worktree remove |
| mmdep repo | iter-03/dr-001: commit 957cf1d (DR-001 decision) |
| mmdep repo | iter-03/deploy-phases: commit 69e17c2 (#59 stamp, answers, wording, a new test) |
mmdep repo, main |
git merge --no-ff iter-03/dr-001 → 6cad155 (#47); git merge --no-ff iter-03/deploy-phases → 6d5632f (#59); pytest -q and -q legacy before and after each (counts above) |
| GitHub | git push github main (b6ccc10..6d5632f), stdin /dev/null; git ls-remote to verify |
| mmdep repo | iter-03/drive-snapshots: stash, rebase onto main, commit 6f84bcf; test runs in the worktree (PYTHONPATH) and in the real checkout |
| pytest temp folders | test hubs, Drive folders and request files; the live agent-account tests ran as mmdep-agent in the real-checkout run |
| MMDEP hub (MCP) | raise_item #60 (specify, Drive paths), #61 (approve, story D), #62 (manual, release) |
| runtime CLI (read only) | mmdep --version, projects, doctor, with MMDEP_ACTOR=claude-code, from the scratchpad. Doctor's probes write nothing in the hub |
mmdep repo, main |
this status entry and docs/status/inbox.md (a status-only commit) |
2026-10-04/05 (merge #61, push; Drive dry run; worktree grant and story F; readiness from the runtime)¶
All test runs were in the real checkout at a normal prompt. Started from Atul's pasted plan, confirmed in the session.
Read first:
- #62 answered done, but the response text is the unfilled template (<commit shown>). The runtime agrees with it anyway: --version gives e4c346f867a0 (runtime); doctor shows agent account required OK, not elevated, runtime = main; and there's a backup mmdep.db.bak-20261004-1418.
- #60's answer also has <path> placeholders. Atul gave the paths in the session: G:\My Drive\MMDEP\snapshots and G:\My Drive\MMDEP\requests. Both exist.
- #61 approved, with two backlog items.
Step A:
- Pushed e4c346f.
- On iter-03/drive-snapshots, 6c5b58a adds #61's two backlog items: a requester allowlist [H] before the requests folder is shared; the Aadhaar pattern vs 12-digit date stamps [L].
- Before the merge: 358 passed, 2 skipped; legacy 23. Merged as 6feb2db (#61). After: 358 + 2 skipped; legacy 23.
- Pushed e4c346f..6feb2db. ls-remote = 6feb2dbc.
Step B: Drive.
- Dry run of mmdep drive publish (dev copy) on a scratch hub: a read-only SQLite backup of mmdep.db, plus a copy of agents.yaml with a drive section pointing at scratchpad folders. The real hub and its agents.yaml weren't touched.
- Result: 62 items (1 open, 61 closed within 14 days), 1 task, 0 test results, 13 doctor checks.
- Fields:
- top level: generated_at, mmdep_version, window_days, items, tasks, tests, doctor;
- items: number, type, title, status, project, priority, created, updated;
- tasks: id, project, state, title;
- doctor: name, status.
- No item-body text found. snapshot.md has 31 lines, with sections Open items, Tasks, Last test run per project and Doctor.
- #63 (manual): release 6feb2db, then the exact drive: lines (parsed with drive.config back to the two paths), then publish and pull.
Step C: two stories.
1. Per-task worktree access, iter-03/worktree-grant (19f10df), approve #64 (P1, high).
- A grant per launch on the task's own worktree only, removed in finally.
- A hub-wide lock: one launch as the account at a time.
- Grants left on other worktrees are swept first.
- OWNER RIGHTS is restricted on the worktree (SYNCHRONIZE only), so the account can't re-grant itself files it made in another task's run.
- Child entries are reset after each run.
- A doctor check, worktree access, fails while workspaces grants the account or has an OWNER RIGHTS allow entry. On the real hub (dev copy, read only) it fails today: #48's grant. Removing it is Atul's step 3 in #64, after the release.
- 14 new tests, 4 of them live as mmdep-agent. Full suite: 373 passed, 3 skipped; legacy 23.
- Found: pytest temp folders inherit OWNER RIGHTS:(OI)(CI)(F), which hands the account its own files back. The first live run caught it. The fixture now copies the real folder's entries, and doctor checks for it.
- Also: the first combined pytest run printed nothing for 600s and was stopped. Every rerun completed normally (the same files took 1m48s and 40s). Not reproduced.
2. Story F, the feedback item type, iter-03/feedback-type (f457f71), approve #65 (high, per Atul in the session; the prompt said [L]).
- feedback, closed with done only.
- raise_item(<source>, "feedback", ...) files into mmdep with source_project, an additive column.
- MCP raise_item doc and inbox() updated.
- 8 tests. Full suite: 366 passed, 2 skipped; legacy 23.
Step D: readiness gate (spec §17.0), from the runtime. Evidence: the runtime CLI with the full path, from %TEMP%, MMDEP_ACTOR=claude-code (--version, doctor, projects), plus an import check with the runtime's python.
| # | Criterion | Met? | Evidence |
|---|---|---|---|
| 1 | Story 11 released in required mode | Yes | Runtime e4c346f867a0; doctor OK agent account required: scripts run as mmdep-agent; OK elevation not elevated; hub agent_account.mode: required |
| 2 | Story 5 + test env + per-task worktree ACL released | No | mmdep.testrunner and mmdep.testenv import from the runtime. But there's no mmdep_hub\test-envs and no test_command in the hub, and mmdep.worktree_access isn't in the runtime (#64 is open). Doctor: runtime e4c346f867a0, main 6feb2dbc008e (#63 releases) |
| 6 | feedback item type |
No | The runtime's ITEM_TYPES: approve, confirm, input, manual, review, specify (#65 is open) |
Lines 3–5 are unchanged from 3 Oct.
Waiting for Atul: #64 (P1, worktree access), #65 (story F), #63 (release + Drive config), #22.
Next, without Atul: nothing blocks on these. When #64 and #65 are approved: merge both, run the tests before and after each, push. Then one release item for all three, so Atul installs once.
Commands that wrote something (this turn):
| Where | What |
|---|---|
| GitHub | git push github main three times (6d5632f..e4c346f, e4c346f..6feb2db, then this status commit), stdin /dev/null; git ls-remote to verify |
| mmdep repo | iter-03/drive-snapshots: backlog.md edit, commit 6c5b58a (the first try failed in pre-commit: gitleaks not on PATH; it passed with the WinGet folder on PATH) |
mmdep repo, main |
git merge --no-ff iter-03/drive-snapshots → 6feb2db (#61); pytest -q and -q legacy before and after |
| mmdep repo | new branch iter-03/worktree-grant: new mmdep/worktree_access.py; edits to isolation.py, doctor.py, tests/test_agent_account.py and backlog.md (inline Python edit scripts; ruff format); commit 19f10df |
| mmdep repo | new branch iter-03/feedback-type: edits to store.py and mcp_server.py; new tests/test_feedback.py; commit f457f71 |
| scratchpad | a SQLite backup copy of mmdep.db + an agents.yaml copy (scratch hub); dry-snap\snapshot.json and .md from the dev mmdep drive publish (its ledger entry went into the scratch copy); acltest and acl_debug.py (icacls experiments on scratchpad folders) |
| pytest temp folders | test hubs; icacls grants on temp worktrees; launches as mmdep-agent |
real mmdep_hub\workspaces |
live tests made and removed _live-test-* folders, now with per-task grants on their p\1 subfolder; none were left. The workspaces folder's own access list is unchanged |
| MMDEP hub (MCP) | raise_item #63 (manual), #64 (approve), #65 (approve) |
| runtime CLI (read only) | --version, doctor, projects, and a runtime-python import check, from %TEMP%, MMDEP_ACTOR=claude-code |
| dev CLI (read only on the real hub) | mmdep doctor with the new check, MMDEP_ACTOR=claude-code, from %TEMP% |
mmdep repo, main |
this status entry and docs/status/inbox.md (a status-only commit) |
2026-10-05 (merges #64 and #65, push; combined release #66; H1 design #67; MCP import finding #68)¶
All test runs were in the real checkout at a normal prompt (net session refused, so not elevated).
Read first: - #64 approved. Your answer accepts the trade-off of one agent-account run at a time, hub-wide. It adds a backlog [H] item, an agent account pool, and says the workspaces grant comes off only after the combined release. - #65 approved.
Step A:
1. #64:
- On iter-03/worktree-grant before the merge: 373 passed, 3 skipped; legacy 23.
- Backlog [H] "Agent account pool" added on the branch (1a74846).
- Merged as 65491da. After: 373 + 3 skipped; legacy 23.
2. #65:
- On iter-03/feedback-type before the merge: 366 passed, 2 skipped; legacy 23.
- Merged as 5a7c3d4. After: 381 passed, 3 skipped; legacy 23.
3. Pushed ab77f3b..5a7c3d4. ls-remote = 5a7c3d48. pyproject.toml is unchanged since 6feb2db.
Step B: combined release #66 (P1), supersedes #63. It covers:
- closing everything: the Get-Process check covers the runtime and the WindowsApps base Python it starts, and exiting Claude Code sessions;
- the database backup;
- installing 5a7c3d4 with dependencies;
- --version, an import check of the runtime's file path, and doctor (expected: 4 failure(s), 3 warning(s), the worktree-access FAIL among them), all from %TEMP%;
- the three drive lines, then drive publish / drive pull;
- icacls: look, /remove:g mmdep-agent, look again;
- doctor again (expected: OK worktree access, 3 failure(s), 3 warning(s));
- rollback.
Each check has its expected output, and the answer must paste the actual values. The expected doctor lines came from a dev-copy doctor run (see below).
My mistake, logged: to capture those lines, I ran the dev copy's mmdep doctor against the real hub with main at 5a7c3d4. Store() applies story F's migration, so the real mmdep.db gained items.source_project before the release. It's additive (the release does the same) and the old runtime ignores it. It's still a write to the hub's database by a non-runtime copy. It's in #66. From now on, the dev copy opens only scratch hubs. Doctor runs against the real hub use the runtime only.
Found (security), #68:
- .mcp.json starts the MCP server with the runtime's python.exe -m mmdep.mcp_server from the repo folder. -m puts that folder first on sys.path, so the server imported the dev checkout's mmdep.
- Verified: without -P, C:\projects\ai-sandbox\mmdep\mmdep\__init__.py; with -P, the runtime's site-packages.
- Fix on iter-03/mcp-runtime-import (0868de3): -P in the args, plus 2 tests. Full suite 383 passed, 3 skipped; legacy 23.
Step C: H1 design, #67 (approve, design only; no code).
- docs/research/h1-harness-design.md on iter-03/h1-harness (68a2eb4).
- Flags checked against claude --help (Claude Code 2.1.289): -p --output-format json --json-schema --session-id/--resume --model sonnet --restricted --tools/--allowedTools Read,Edit,Write,Glob,Grep --permission-mode acceptEdits --permission-prompts none --strict-mcp-config --max-budget-usd.
- The claude process runs as Atul. Only the Test Runner takes #64's lock, and that wait is measured.
- Caps: $0.50 per call, $10 for the spike, 20 calls a day (Atul's hub change), 3 questions per task.
- Synthetic projects h1-a and h1-b; ledger measures; a stop condition.
- Decisions asked: (a) B (MMDEP raises the specify item from structured output, recommended) or A (the agent calls raise_item itself); (b) the caps; (c) the model.
Waiting for Atul: #66 (P1 release), #68 (P1, the MCP import fix), #67 (H1 design), #63 (to close as superseded), #22.
Next, without Atul: after #66's answer, run the live gate test from the real checkout (test_live_account_cannot_write_another_tasks_worktree, which skips until the grant is gone), then readiness lines 2 and 6 from the runtime. Line 2 also needs a test env and a test_command on some project; H1's h1-a and h1-b would supply both.
Commands that wrote something (this turn):
| Where | What |
|---|---|
| mmdep repo | iter-03/worktree-grant: backlog.md (agent account pool), commit 1a74846 |
mmdep repo, main |
git merge --no-ff iter-03/worktree-grant → 65491da (#64); git merge --no-ff iter-03/feedback-type → 5a7c3d4 (#65); pytest -q and -q legacy before and after each |
| GitHub | git push github main (ab77f3b..5a7c3d4, then this status commit), stdin /dev/null; git ls-remote |
real hub mmdep.db |
unintended: the dev-copy mmdep doctor (main 5a7c3d4) applied ALTER TABLE items ADD COLUMN source_project (see above) |
| MMDEP hub (MCP) | raise_item #66 (manual, release), #67 (approve, H1 design), #68 (approve, -P) |
| mmdep repo | new branch iter-03/h1-harness: docs/research/h1-harness-design.md, commit 68a2eb4 |
| mmdep repo | new branch iter-03/mcp-runtime-import: .mcp.json, tests/test_mcp_config.py, commit 0868de3 |
| pytest temp folders | test hubs; icacls grants on temp worktrees; launches as mmdep-agent; the live tests' _live-test-* folders under the real workspaces (removed) |
| runtime / CLI (read only) | runtime mmdep --version and show 64/show 65; claude --version and --help; icacls (view only) on workspaces; process listing |
mmdep repo, main |
this status entry and docs/status/inbox.md (a status-only commit) |
2026-10-05, later (Step D: DR-002 recorded on a branch, approve #69; stage 1 setup #70)¶
- DR-002: Atul placed
docs/decisions/DR-002-agent-platforms-and-automation.md(75 lines, untracked) in the working tree. I read it in full and committed it unchanged oniter-03/dr-002(3ac7dd5, offmainatd4e91e7). The same commit adds: iteration-03.md: FB1, DR-002 stage 1, the sandbox forge bus (M, high, after H1), placed after H1; the dependency line adds H1 and GH → FB1;backlog.md: FB1 [H]; DR-002 follow-ups: [H] a TEA collector user per server (read-only, forced commands) and [H] the weekly triage plus TEA report as the first real use after stage 1;- spec §19: E1 marked resolved by DR-002.
- Docs only. Full suite 381 passed, 3 skipped; legacy 23.
- #69 (approve). Flagged there:
- ChatGPT event triggers don't cover GitLab;
- the connector's write ability is unknown;
- the turn limit is enforced by detection only (MMDEP is read-only on forges);
- DR-002 names Gemini 3.5 Flash while the hub uses 3.8-flash.
- #70 (manual, P3): Atul's setup:
- two private sandboxes named
mmdep-forge-sandbox, with the four labels; - the Copilot coding agent on the GitHub sandbox only;
- Codex review on the sandbox;
- the ChatGPT GitHub connector limited to the sandbox;
- the triage event task, with the drafted prompt (fixed comment format; issue text treated as data; never assign, open PRs or merge);
- read-only tokens (GitHub fine-grained Issues/PRs read-only on the sandbox; GitLab read_api from a Reporter-only account) stored with
cmdkey ... /pass(prompted, never on the command line).
Waiting for Atul: #66 (P1 release), #68 (P1, MCP -P), #67 (H1 design), #69 (DR-002), #70 (stage 1 setup, after #69), #63 (close as superseded), #22.
Commands that wrote something (this step):
| Where | What |
|---|---|
| mmdep repo | new branch iter-03/dr-002: git add of Atul's DR-002 file (unchanged); iteration-03.md, backlog.md and spec §19 edited by a scratchpad script (dr002_edits.py, one asserted replacement each); commit 3ac7dd5 |
| pytest | pytest -q and -q legacy on the branch (temp folders; live tests' _live-test-* folders under the real workspaces, removed) |
| MMDEP hub (MCP) | raise_item #69 (approve), #70 (manual) |
mmdep repo, main |
this status entry and docs/status/inbox.md (a status-only commit), then a push |
2026-10-05, evening (answers to #67 and #68; DR-003 and backlog on iter-03/dr-002, approve #71)¶
Answers read:
- #68 approved: "Merge after #66", plus a backlog [H] item (non-runtime copies refuse the real hub).
- #67 approved: option B (structured output; MMDEP raises the item; no hub write path for agents), the caps as proposed ($0.50 per call, $10 total, 20 calls a day, 3 questions per task), Sonnet. "Build after #66 is released."
- #66 (the release) is still open. So the #68 merge and the H1 build both wait for it, as Atul's answers say. Nothing was merged to main this turn except this status commit.
- The dev copy opens only scratch hubs from now on. It's also in the backlog as a code guard (#68).
On iter-03/dr-002 (76d8f81, on top of 3ac7dd5):
- Atul's docs/decisions/DR-003-controller-hosting.md, committed unchanged.
- Backlog, now under "DR-002 and DR-003 follow-ups": DOC-1; per-project views; enabling low-risk batch approval with Codex/Copilot as second-opinion reviewers (spec §9); the AWS MCP server for TEA only, on mmdep-ro; the Telegram (Botbiz) alert channel (titles only); browser-automation rules; the business-work projects Ops: renewals and Ops: partner payouts; non-runtime copies refuse the real hub (#68).
- DOC-1, per-project views and batch approval belonged to the DR-002 step and were missing from 3ac7dd5; they're added here.
- Full suite 381 passed, 3 skipped; legacy 23.
- #71 (approve) supersedes #69. #69 is left for Atul to close.
Waiting for Atul: #66 (P1 release; it unblocks the #68 merge and the H1 build), #71 (DR-002 + DR-003), #69 (close as superseded), #70 (stage 1 setup), #22.
Next, without Atul: nothing that doesn't depend on #66. After #66: merge #68 (tests before and after), push, then build H1 on iter-03/h1-harness.
Commands that wrote something (this step):
| Where | What |
|---|---|
| mmdep repo | iter-03/dr-002: git add of Atul's DR-003 file (unchanged); backlog.md edited by a scratchpad script (dr003_edits.py, asserted replacements); commit 76d8f81. The first two attempts failed in pre-commit (gitleaks not on PATH); it passed with the WinGet gitleaks folder on PATH |
| pytest | pytest -q and -q legacy on the branch (temp folders) |
| MMDEP hub (MCP) | raise_item #71 (approve) |
mmdep repo, main |
this status entry and docs/status/inbox.md (a status-only commit), then a push |
2026-10-06 (#66 confirmed; #68 and #71 merged, pushed; H1 built, approve #73; readiness from the runtime)¶
All test runs were in the real checkout at a normal prompt (net session: system error 5, so not elevated). Started from Atul's pasted plan.
Read first:
- #66 done: Atul installed 5a7c3d4, published Drive, and removed the workspaces grant (his pasted output). The answer's Get-Process lines show .Path where $_.Path was meant, so that filter matched nothing. Harmless here.
- #68 approved: "Merge after #66", plus a backlog [H] item (already in backlog.md through #71's branch).
- #67 approved: option B, the caps as proposed, Sonnet; build after #66.
- #71 approved. It asked for three things before the merge:
- spec 0.11 for E1;
- a backlog [L] item: measure DR-003's B2 trigger;
- a manual item for the DR-003 §6 snapshot clean-up.
- #69 rejected (superseded by #71). #63 done (superseded by #66).
Step A: #66 confirmed from the runtime, full path, from %TEMP%, MMDEP_ACTOR=claude-code:
- --version: 5a7c3d48a40f (runtime).
- Doctor:
- OK agent account required: scripts run as mmdep-agent; CLI agents opted in: none;
- OK elevation;
- OK worktree access: mmdep-agent has no grant on ...\workspaces;
- 3 failure(s), 4 warning(s). The failures are gemini, deepseek and qwen at cap 0 in role maps (#45, known). The warnings include "runtime vs main", because main has docs and config commits only.
- icacls on workspaces: only the inherited mmdep-agent:(I)(OI)(CI)(N) deny is left.
- The runtime's python with -P imports mmdep from site-packages, and ITEM_TYPES includes feedback.
Merges:
1. #68:
- On iter-03/mcp-runtime-import: 384 passed, 2 skipped; legacy 23.
- Merged as e2c7658. After: 384 + 2 skipped (17m24s this time, while a Windows shadow-copy job ran); legacy 23.
2. #71:
- On iter-03/dr-002, 31aa33d: spec 0.11 (version header and change-log row) and the backlog [L] "Measure DR-003's B2 trigger in the ledger".
- #72 (manual, P3) raised: the snapshot clean-up, marked as waiting on the legacy developer, with the two read-only aws checks (before and after) and the console steps.
- Before the merge:
- the first run: 1 failed, 381 passed, 2 skipped, in 6h19m (the machine slept during it); legacy 23. Which test failed is not known: I printed only the summary line.
- the rerun: 382 passed, 2 skipped (4m44s). Not reproduced; recorded as unexplained.
- Merged as cadeb73. After: 384 + 2 skipped; legacy 23.
3. Pushed 4189f8a..cadeb73. ls-remote = cadeb73a.
Restart note for Atul: the MCP server running during this session was started at 23:47 as python.exe -m mmdep.mcp_server, without -P, from before #68. .mcp.json is read only when Claude Code starts, so restart Claude Code (and Claude desktop, if it uses the same config) for the MCP server to use the runtime's code. No release is needed for #68 or #71: they change config and docs, not runtime code.
Step B: H1, on iter-03/h1-harness (rebased onto cadeb73: 9cd66cd, plus 196a4f0 stamping the design approved). Approve item #73.
- Built per #67. The flags were checked again against claude --help 2.1.289.
- What it adds:
- the session id is stored on the task before the launch;
- a question becomes a specify item raised by claude, and the task parks with step resume;
- the answer resumes the same session, agent and worktree;
- limits: 3 questions per session, then a review item; spike_budget_usd, then a manual item;
- ledger: harness.* entries and account.lock_wait.
- Found: total_cost_usd is cumulative over a session, so MMDEP logs each call's own cost as the difference.
- Live, pre-approval, using the dev copy on a scratch hub in the scratchpad only (the real hub was not opened):
- h1-a and h1-b ran in parallel. Both started at 01:09:12Z, and each asked one real question after 2 turns, for 0.0267 and 0.0266 USD.
- Two live resumes through the adapter kept the session.
- The waiting time on a person isn't measured: an agent can't answer items, so the inbox resume waits for Atul. #73 offers an optional scratch answer script (h1_answer.py, run by Atul as himself, pointing only at the scratch hub), or the real spike after release.
- Tests: 19 new + 1 opt-in live. Full suite 403 passed, 3 skipped; legacy 23.
- The formatter hook again removed an import (uuid) while it was briefly unused. The first test run caught it, before any commit.
Step C: readiness gate (spec §17.0), from the runtime (5a7c3d48a40f; the commands are listed below):
| # | Criterion | Met? | Evidence |
|---|---|---|---|
| 1 | Story 11 released in required mode | Yes | doctor OK agent account required: scripts run as mmdep-agent; OK elevation not elevated; hub agent_account: mode: required |
| 2 | Story 5 + test env + per-task worktree ACL released | No | Per-task grant: yes. mmdep.worktree_access is in the runtime; doctor shows OK worktree access; the workspaces grant is removed (#66). Test Runner code: in the runtime (mmdep.testrunner and mmdep.testenv import). Test env: no. mmdep_hub\test-envs doesn't exist, and the hub agents.yaml has no test_command, so doctor has no test env <project> line. H1's h1-a/h1-b (after #73) would be the first |
| 3 | Project registration: local origin, role map, test_command |
No | mmdep projects: mmdep, sandbox. mmdep_hub\repos: sandbox.git only |
| 4 | One task end to end on the new project | No | No new project |
| 5 | Working method written down | Partly | Unchanged: spec §17.0 line 5 and CLAUDE.md cover mmdep; accounting's PROJECT.md (S2) isn't written; H1 isn't proven yet (#73) |
| 6 | feedback item type |
Yes | The runtime's ITEM_TYPES: approve, confirm, feedback, input, manual, review, specify |
Waiting for Atul: #73 (H1), #72 (snapshot clean-up, after the legacy developer's answer), #70 (stage 1 setup), #22. Also: restart Claude Code (#68).
Next, without Atul: nothing that doesn't depend on #73. After #73:
- merge it (tests before and after) and push;
- one manual item covering the release, the hub agents.yaml lines for claude, and the h1-a/h1-b registration with test envs;
- then the spike (design §9).
Commands that wrote something (this turn):
| Where | What |
|---|---|
mmdep repo, main |
git merge --no-ff iter-03/mcp-runtime-import → e2c7658 (#68); git merge --no-ff iter-03/dr-002 → cadeb73 (#71); pytest -q and -q legacy before and after each (counts above) |
| mmdep repo | iter-03/dr-002: scratchpad worktree (removed afterwards); dr003_71.py (asserted replacements) for the spec 0.11 header and change log and the backlog [L]; commit 31aa33d (gitleaks put on PATH first) |
| GitHub | git push github main (4189f8a..cadeb73), then this status commit, stdin /dev/null; git ls-remote |
| MMDEP hub (MCP) | raise_item #72 (manual, P3), #73 (approve) |
| mmdep repo | iter-03/h1-harness: scratchpad worktree; git rebase onto e2c7658, then onto cadeb73; code, tests and notes; ruff; commits (now 9cd66cd, 196a4f0). The worktree was recreated, detached, for h1_answer.py |
| Claude CLI (live, as Atul, synthetic) | the scratchpad h1probe/ folder: one start and one resume (0.0288 + 0.0061 USD); MMDEP_LIVE_CLAUDE=1 for the live test (one start and one resume, in a pytest temp folder) |
| scratchpad (dev copy, scratch hub only) | h1_setup.py made h1run/ (two synthetic repos; h1-hub with agents.yaml); dev mmdep project add ×2 and task add ×2 (MMDEP_ACTOR=claude-code); two parallel mmdep run passes (MMDEP_ACTOR=mmdep): specify items #1 and #2 in the scratch hub, and worktrees under h1-hub\workspaces; h1_answer.py written for Atul |
| pytest temp folders | test hubs; icacls grants on temp worktrees; launches as mmdep-agent; the live tests' _live-test-* folders under the real workspaces (removed) |
| runtime CLI (read only) | --version, doctor, projects; a runtime-python import check; icacls (view) on workspaces; Select-String on the hub agents.yaml; net session |
mmdep repo, main |
this status entry and docs/status/inbox.md (a status-only commit) |
2026-10-06, later (Steps A–D complete; items #74, #75, #76 raised)¶
All four steps from Atul's pasted plan are done. #22 is closed (deferred per DR-002/DR-003), dropped from the waiting list.
Step A (done earlier this session): merged H1 (#73) as 97553c8. Full suite before: 403 passed, 3 skipped; legacy 23. After: 403 + 3 skipped; legacy 23. Pushed cadeb73..97553c8. The two scratch H1 questions (#1 and #2 in the scratch hub) were not answered; waiting time is measured in the first real run, per #73's approval text.
Step B: raised #74 (manual, P1): release main at 97553c8 (H1, MCP -P, spec 0.11 and DR-002/DR-003 docs). Close-everything check, db backup, install with dependencies, --version and doctor from %TEMP% non-elevated, expected output per check. Atul answers with actual values.
Step C: story M on iter-03/doctor-memory (34b28c7). Doctor warns when available physical memory is below 500 MB or 15% of total (ctypes GlobalMemoryStatusEx, no new dependency). Wake lock (SetThreadExecutionState, ctypes) held during the dispatcher's merge/work step; doctor reports the state. 11 new tests. Full suite: 414 passed, 3 skipped; legacy 23. Approve item #75. Backlog [L]: Task Scheduler "wake to run".
Step D: docs-only on iter-03/handoff-std (bd1c2e9). The amendment file committed unchanged; spec §4 references STD-AI-CONT-001; backlog "Agent handoff protocol (Atul, 6 Oct)" with 11 items, proposed order HO-1, CTX-1, HU-1, AG-1 first; TEA estate note with capacity/resilience risk and snapshot policy. Approve item #76.
Waiting for Atul: #74 (P1 release), #75 (story M), #76 (handoff docs), #70 (stage 1 setup). #22 closed.
Commands that wrote something (this turn):
| Where | What |
|---|---|
mmdep repo, main |
git merge --no-ff iter-03/h1-harness → 97553c8 (#73); pytest -q (403 + 3 skipped) and -q legacy (23) before and after |
| GitHub | git push github main (cadeb73..97553c8), stdin /dev/null; git ls-remote |
| mmdep repo | new branch iter-03/doctor-memory off main: mmdep/wakelock.py (new), mmdep/doctor.py (memory and wake lock checks), mmdep/dispatcher.py (keep_awake context), tests/test_doctor_memory.py (new, 11 tests), backlog.md; commit 34b28c7 |
| mmdep repo | iter-03/handoff-std (bd1c2e9): docs/decisions/gitlab-drafts/AI-Engineering-Continuity-and-Handover-v1.1-amendment.md (Atul's file, unchanged), docs/spec/000-orchestrator-design.md (§4 reference), backlog.md (agent handoff protocol + TEA estate note) |
| MMDEP hub (MCP) | raise_item #74 (manual, P1 release), #75 (approve, story M), #76 (approve, handoff docs) |
mmdep repo, main |
this status entry and docs/status/inbox.md (a status-only commit) |
2026-10-06, evening (#75 and #76 merged, pushed; release #77; HU-1 #78; AG-1 #79)¶
All test runs were in the real checkout at a normal prompt (net session: system error 5, so not elevated).
Read first: - #75 approved: "Merge to main, then fold into a single release with H1 (supersede #74)." - #76 approved: "Merge to main. Atul submits the amendment to the GitLab governance repo as an MR."
Step A: merges.
1. #75 (story M): on iter-03/doctor-memory before the merge: 414 passed, 3 skipped; legacy 23. Merged as 0dea785. After: 414 + 3 skipped; legacy 23.
2. #76 (handoff docs): on iter-03/handoff-std before the merge: 403 passed, 3 skipped; legacy 23. A backlog.md conflict (both branches added sections after line 102): resolved by keeping both — story M's "Task Scheduler" section first, then the handoff protocol and TEA estate sections. Merged as dfd8c78. After: 414 + 3 skipped; legacy 23.
3. Pushed 97553c8..dfd8c78. ls-remote = dfd8c787783d.
Step B: raised #77 (manual, P1): release main at dfd8c78, supersedes #74. Same format as #66: close-everything, db backup, install with dependencies, --version, import check (includes mmdep.wakelock), doctor from %TEMP% non-elevated. Expected doctor now includes the new memory and wake lock lines.
Step C: quick wins from the handoff backlog.
- HU-1 on
iter-03/hu1-placeholder-guard(f593458), approve #78. store.respond()rejects response text containing unfilled template placeholders (<commit shown>,<path>,<N>, etc.).- Regex:
<(?![-/!])[A-Za-z][A-Za-z0-9 _.,/'-]{0,60}>— catches angle-bracket-wrapped tokens, excludes HTML tags, stdin/stdout/stderr. - 22 new tests: 16 unit (regex edge cases) + 6 integration (respond with/without placeholders, error message).
- Full suite: 436 passed, 3 skipped; legacy 23.
-
Found: the ruff formatter removed the
reimport on the first edit because it was added before the usage site. Re-added after the usage was in place. -
AG-1 on
iter-03/ag1-agent-instructions(8d728e6), approve #79. - Source template:
docs/agents/agent-instructions.mdwith 6 blocks (role-and-scope, current-state, prompts-are-pointers, work-result-format, commit-trailers, windows-profile). - Generation script:
docs/agents/generate.pyreads the source, writesAGENTS.md,.github/copilot-instructions.md,GEMINI.md,docs/agents/project-instructions.txt. Supports--checkfor staleness detection. CLAUDE.mdlinks the source in a new "Agent instructions" section.- 8 new tests: source exists, targets exist, match the template, all blocks present, block count, block names, CLAUDE.md link, stale detection.
- Full suite: 422 passed, 3 skipped; legacy 23.
- Found and fixed: the first version's regex-based block extraction failed on Windows line endings. Rewrote with line-by-line parsing.
Waiting for Atul: #77 (P1 release), #78 (HU-1), #79 (AG-1), #74 (answer "superseded by #77"), #70 (stage 1 setup).
Commands that wrote something (this turn):
| Where | What |
|---|---|
mmdep repo, main |
git merge --no-ff iter-03/doctor-memory → 0dea785 (#75); git merge --no-ff iter-03/handoff-std → dfd8c78 (#76, conflict in backlog.md resolved); pytest -q and -q legacy before and after each (counts above) |
| GitHub | git push github main (97553c8..dfd8c78), stdin /dev/null; git ls-remote |
| mmdep repo | new branch iter-03/hu1-placeholder-guard off main: mmdep/store.py (placeholder regex and guard in respond), tests/test_placeholder_guard.py (new, 22 tests); commit f593458 |
| mmdep repo | new branch iter-03/ag1-agent-instructions off main: docs/agents/agent-instructions.md (source), docs/agents/generate.py (generator), AGENTS.md, .github/copilot-instructions.md, GEMINI.md, docs/agents/project-instructions.txt (generated), CLAUDE.md (link), tests/test_agent_instructions.py (new, 8 tests); commit 8d728e6 |
| MMDEP hub (MCP) | raise_item #77 (manual, P1 release, supersedes #74), #78 (approve, HU-1), #79 (approve, AG-1) |
mmdep repo, main |
this status entry and docs/status/inbox.md (a status-only commit) |
2026-10-07 (#78 and #79 merged with feedback applied, pushed; release #80; HO-1 design #81)¶
All test runs were in the real checkout at a normal prompt.
Read first: - #78 approved: "Before merging, confirm or add: text inside backticks is never treated as a placeholder, and the refusal message says so (wrap intentional angle-bracket text in backticks). Add a test for that. Merge to main." - #79 approved: "Use Role MMDEP-DEV and Seat SEAT-MAIN for this repo in the generated files. Next: the same template is reused for project repos as they are onboarded."
Step A: merges, with approval feedback applied first.
- #78 (HU-1, backticks safeguard):
- On
iter-03/hu1-placeholder-guard, commitbf07e2b:_BACKTICK_REstrips inline backticks and fenced code blocks before placeholder scanning; the error message now says "or wrap intentional angle-bracket text in backticks"; 6 new tests (4 unit: inline backtick ignored, fenced code block ignored, backtick only protects enclosed, mixed; 2 integration: respond with backtick-wrapped placeholder accepted, error message mentions backticks). Total: 28 placeholder guard tests. - Full suite on the branch: 442 passed, 3 skipped; legacy 23.
-
Merged as
7019802. After onmain: 439 passed, 3 skipped; legacy 23. (Count difference: 3 AG-1 tests not yet onmain.) -
#79 (AG-1, Role/Seat):
- On
iter-03/ag1-agent-instructions, commitbc89406: replaced generic<ROLE-ID>withMMDEP-DEVand<SEAT-ID>withSEAT-MAINin the source template and regenerated all 4 target files.--checkconfirmed all match. - 8 agent instruction tests pass.
-
Merged as
e412297. After onmain: 450 passed, 3 skipped; legacy 23. -
Pushed
dfd8c78..e412297to GitHub.
Step B: raised #80 (manual, P1): release main at e412297, supersedes #77. Same format as #66. Includes a note that the done answer itself will now be checked for unfilled template placeholders (HU-1). Expected doctor output includes the memory and wake-lock lines.
Step C: raised #81 (approve, HO-1 design only, no code). Five sections:
1. Where records live: forge issue templates (GitHub .github/ISSUE_TEMPLATE/ and GitLab .gitlab/issue_templates/), MMDEP items/ledger mirror, and the repository current-state record.
2. Required fields for Work Request, Work Result and Role Transfer.
3. How MMDEP links a forge issue to a task: DR-002 forge reader detects new issues; MMDEP creates a mirror item with forge: link; maps to existing or new task.
4. How the RESUMED/NEW banner (PR-1) is produced from task fields (session_id, item_id, branch).
5. How the 3-turn limit is counted: per-issue agent-action count since last human action; detection-only (MMDEP is read-only on forges).
Waiting for Atul: #80 (P1 release), #81 (HO-1 design), #77 (answer "superseded by #80"), #74 (answer "superseded by #77"), #70 (stage 1 setup).
Commands that wrote something (this turn):
| Where | What |
|---|---|
| mmdep repo | iter-03/hu1-placeholder-guard: mmdep/store.py (_BACKTICK_RE, error message), tests/test_placeholder_guard.py (6 new tests); commit bf07e2b |
| mmdep repo | iter-03/ag1-agent-instructions: docs/agents/agent-instructions.md (Role/Seat), 4 generated files; commit bc89406 |
mmdep repo, main |
git merge --no-ff iter-03/hu1-placeholder-guard → 7019802 (#78); git merge --no-ff iter-03/ag1-agent-instructions → e412297 (#79); pytest -q and -q legacy before and after each (counts above) |
| GitHub | git push github main (dfd8c78..e412297), stdin /dev/null |
| MMDEP hub (MCP) | raise_item #80 (manual, P1 release, supersedes #77), #81 (approve, HO-1 design) |
mmdep repo, main |
this status entry and docs/status/inbox.md (a status-only commit) |