feat(herdr): add per-project workspaces - #2505
Open
wonder-media wants to merge 7 commits into
Open
Conversation
… a journal exists
…ale section pointers
This was referenced Aug 17, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
The developer was shipping committed changes in the firstmate repository (documentation and sync updates around herdr project-space state and AGENTS inventory) through the no-mistakes validation pipeline, which gates changes with review, tests, lint, docs, push, PR, and CI. The pipeline had passed validation but failed at the push step twice with HTTP 403 because it pushed to the upstream repo kunchenguid/firstmate instead of the developer's fork. The developer diagnosed the root cause as an empty fork_url in the pipeline's repo registry, fixed it by setting fork_url to https://github.com/wonder-media/firstmate.git, and instructed the agent to retry the no-mistakes pipeline from the intact preserved head 5b673e2. Their explicit constraints were to rerun from that preserved, already-validated head (not re-submit an older local head) and to see the run through push, PR creation, and green CI.
What Changed
herdr-project-spacesconfig makes spawns from the same project directory land as tabs in a single workspace named after the project, while different projects (including same-basename directories elsewhere) get separate workspaces via path-hashed, durable exact-id binding records understate/.bin/fm-spawn.shwires the new project-workspace resolution into spawn, andbin/fm-config-inherit-lib.shplus the secondmate provisioning skill propagate theherdr-project-spacessetting to inherited/secondmate configs; the presentation-journal duplicate-launch recovery guard now runs whenever a journal exists, including under project grouping.tests/fm-backend-herdr.test.sh), real-herdr e2e presentation tests, and secondmate harness coverage for the new behavior, and documented the feature indocs/herdr-backend.md,docs/configuration.md,docs/architecture.md, and the AGENTS inventory.Risk Assessment
✅ Low: The feature is opt-in and default-off with exact-id-only adoption, warn-and-fall-back-flat handling on every failure path, single-sourced config inheritance, synced docs, and thorough unit/e2e coverage; the one shared behavior change (running the journal recovery guard whenever a journal exists) is a deliberate, tested durable fix that closes a duplicate-agent launch path.
Testing
Ran the three test files this change touches — the herdr fake-CLI unit suite (180 ok), the secondmate config-inheritance suite (42 ok), and the real-Herdr isolated-lab presentation e2e on herdr 0.7.5 (25 ok, 0 failures, default-session tripwire intact) — then manually drove the production project-workspace functions against a live isolated Herdr session and captured a transcript showing the end-user layout: same-project tasks grouped as tabs in one project-labeled workspace, separate projects and same-named projects in different directories kept in independent exact-id-bound workspaces, with durable binding records on disk. All passed. No screenshot artifact: Herdr lab sessions are headless and the safety contract permits only guarded CLI access, so JSON workspace/tab listings are the reviewer-visible surface.
Evidence: Live-Herdr project-space demo transcript (workspace/tab layout, bindings)
-- 5. What the user sees: herdr workspace list (labels are project names) -- [ {"workspace_id": "w1", "label": "mf-website"}, {"workspace_id": "w2", "label": "soul-site"}, {"workspace_id": "w3", "label": "mf-website"} ] -- 6. Tabs inside the mf-website project workspace (one tab per task) -- [ {"tab_id": "w1:t2", "label": "fm-task-checkout-fix"}, {"tab_id": "w1:t3", "label": "fm-task-seo-audit"} ] -- 7. Durable exact-id bindings persisted in state/ (path-hash keyed) -- # .herdr-project-space-mf-website-701de185cd3a56a5 -> workspace_id=w1 # .herdr-project-space-mf-website-ca87a1dddd2e2d44 -> workspace_id=w3 (same basename, different dir) # .herdr-project-space-soul-site-64e586b861f8e22c -> workspace_id=w2Evidence: Real-Herdr e2e transcript (25 ok, incl. new project-grouping checks)
Evidence: Unit-suite project-space pass lines
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
bin/backends/herdr.sh:1705- fm_backend_herdr_project_binding_snapshot assigns binding_version/binding_dir/binding_name/binding_session/binding_workspace via read without declaring them local, so they leak into the sourcing shell (fm-spawn.sh). No collision exists today, but a future variable with the same name in the 3000-line sourcing script would be silently clobbered. Add them to the function's local declaration.bin/backends/herdr.sh:1747- If a spawn fails between fresh project-workspace creation (binding already published atomically) and task-tab creation, the workspace's auto-seeded default tab is never pruned: later spawns reuse the binding with an empty seeded-tab id, and the prune contract is same-call-only. A stray empty tab persists in the project workspace. Cosmetic-only residue in an opt-in visual layout, matching existing failure-mode parity of the flat container path.✅ **Test** - passed
✅ No issues found.
bash tests/fm-backend-herdr.test.sh(fake-CLI unit suite, 180 ok incl. the 5 new project-space tests: opt-in parsing, reuse/separation, same-basename bindings, stale-binding refresh, no label adoption)bash tests/fm-secondmate-harness.test.sh(42 ok; new herdr-project-spaces config propagates to secondmate homes and mirrors absence)bash tests/fm-backend-herdr-presentation-e2e.test.sh(real herdr 0.7.5 isolated lab, 25 ok / 0 failures, incl. both new checks: fresh grouped spawns share one exact project workspace and skip the presentation projection; pending presentation journal still runs the duplicate-launch recovery guard under project grouping; default-session tripwire intact)Manual live-Herdr demo via the guarded fm-herdr-lab.sh helper driving production fm_backend_herdr_project_workspace_ensure/fm_backend_herdr_create_task: two mf-website tasks land as two tabs in one workspace labeled mf-website; soul-site gets a separate workspace; a same-basename project in another directory gets an independent workspace; durable exact-id binding records written under state/ (transcript saved as evidence)✅ **Document** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.