[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6DAE8HGEVCGQ8R8MFDKB91B":3,"skill-same:01M6DAE8HGEVCGQ8R8MFDKB91B":58},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":31,"linkOnly":32,"markdownContent":33,"markdownBody":34,"frontMatter":35,"refName":36,"commitSha":37,"skillDirectory":38,"forksCount":39,"repositoryHtmlUrl":40,"repositoryHomepageUrl":6,"ownerHtmlUrl":41,"ownerBlogUrl":42,"ownerDescription":6,"updatedAt":43,"install":44,"resources":57},"01M6DAE8HGEVCGQ8R8MFDKB91B","epoch",null,"Master orchestrator for full 12-phase workflow","dayvidpham\u002Fpasture","dayvidpham","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F22456875?v=4","01M6CHR0F6J2M4QEHFR85XRNSR","David Huu Pham","01M6DA7X1WB0MK7TTV46SRZK75",1,"MIT","https:\u002F\u002Fgithub.com\u002Fdayvidpham\u002Fpasture\u002Fblob\u002Fmain\u002Finternal\u002Ftarget\u002Fclaudecode\u002Fassets\u002Fpasture-skills\u002Fskills\u002Fepoch\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[5,24,25,26,27,28,29,30],"master orchestrator","workflow","orchestrator","master","full","for","phase","2026-11-10 10:12:30",false,"---\nname: epoch\ndescription: Master orchestrator for full 12-phase workflow\n---\n\n# Epoch Agent\n\n\u003C!-- BEGIN GENERATED FROM pasture schema -->\n\u003C!-- GENERATED FROM pasture v0.0.33 artifact v1 -->\n**Role:** `epoch` | **Phases owned:** p1-request, p2-elicit, p3-propose, p4-review, p5-plan-uat, p6-ratify, p7-handoff, p8-impl-plan, p9-worker-slices, p10-code-review, p11-impl-uat, p12-landing\n\n## Protocol Context (generated from schema.xml)\n\n### Owned Phases\n\n| Phase | Name | Domain | Transitions |\n|-------|------|--------|-------------|\n| `p1-request` | Request | user | → `p2-elicit` (classification confirmed, research and explore complete) |\n| `p2-elicit` | Elicit | user | → `p3-propose` (URD created with structured requirements) |\n| `p3-propose` | Propose | plan | → `p4-review` (proposal created) |\n| `p4-review` | Review | plan | → `p5-plan-uat` (all 3 reviewers vote ACCEPT); → `p3-propose` (any reviewer votes REVISE) |\n| `p5-plan-uat` | Plan UAT | user | → `p6-ratify` (user accepts plan); → `p3-propose` (user requests changes) |\n| `p6-ratify` | Ratify | plan | → `p7-handoff` (proposal ratified, IMPL_PLAN placeholder created) |\n| `p7-handoff` | Handoff | plan | → `p8-impl-plan` (handoff authored in the HANDOFF Pasture task body) |\n| `p8-impl-plan` | Impl Plan | impl | → `p9-worker-slices` (all slices created with leaf tasks, assigned, and dependency-chained) |\n| `p9-worker-slices` | Worker Slices | impl | → `p10-code-review` (all slices complete, quality gates pass) |\n| `p10-code-review` | Code Review | impl | → `p11-impl-uat` (all 3 reviewers ACCEPT, all BLOCKERs resolved); → `p9-worker-slices` (any reviewer votes REVISE) |\n| `p11-impl-uat` | Impl UAT | user | → `p12-landing` (user accepts implementation); → `p9-worker-slices` (user requests changes) |\n| `p12-landing` | Landing | impl | → `complete` (git push succeeds, all tasks closed or dependency-resolved) |\n\n### Commands\n\n| Command | Description | Phases |\n|---------|-------------|--------|\n| `pasture:epoch` | Master orchestrator for full 12-phase workflow | p1-request, p2-elicit, p3-propose, p4-review, p5-plan-uat, p6-ratify, p7-handoff, p8-impl-plan, p9-worker-slices, p10-code-review, p11-impl-uat, p12-landing |\n\n### General Constraints\n\n**[C-actionable-errors]**\n- Given: an error, exception, or user-facing message\n- When: creating or raising\n- Then: make it actionable: describe (1) what went wrong, (2) why it happened, (3) where it failed (file location, module, or function), (4) when it failed (step, operation, or timestamp), (5) what it means for the caller, and (6) how to fix it\n- Should not: raise generic or opaque error messages (e.g. 'invalid input', 'operation failed') that don't guide the user toward resolution\n\n**[C-audit-dep-chain]**\n- Given: any phase transition\n- When: creating new task\n- Then: chain dependency: \"${EPOCH_REC}\" block parent --by child\n- Should not: skip dependency chaining or invert direction\n\n_Example (correct)_\n\n```bash\n# Full dependency chain: work flows bottom-up, closure flows top-down\n\"${EPOCH_REC}\" block \"${REQUEST_ID_URI}\" --by \"${URE_ID_URI}\"\n\"${EPOCH_REC}\" block \"${URE_ID_URI}\" --by \"${PROPOSAL_ID_URI}\"\n\"${EPOCH_REC}\" block \"${PROPOSAL_ID_URI}\" --by \"${IMPL_PLAN_ID_URI}\"\n\"${EPOCH_REC}\" block \"${IMPL_PLAN_ID_URI}\" --by \"${SLICE_1_ID_URI}\"\n\"${EPOCH_REC}\" block \"${SLICE_1_ID_URI}\" --by \"${LEAF_TASK_A_ID_URI}\"\n```\n\n**[C-audit-never-delete]**\n- Given: any task or label\n- When: modifying\n- Then: add labels and comments only\n- Should not: delete or close tasks prematurely, remove labels\n\n**[C-clean-review-exit]**\n- Given: per-wave code review and end-of-epoch convergence\n- When: evaluating review results\n- Then: during a wave, only an open BLOCKER finding gates the wave; IMPORTANT and MINOR findings may stay open past a wave gate; after the last wave, convergence queries every open `pasture:finding` across all waves and targets 0\u002F0\u002F0 within the gate declaration's maxRounds; iterate review -> fix -> re-review up to the chosen review-effort budget; on budget exhaustion without reaching that target, SURFACE the outstanding findings to the user at a gate for a decision\n- Should not: require a zeroed round at a wave boundary; close a finding outside `C-finding-lifecycle`; proceed past the chosen budget without surfacing outstanding findings to the user; hardcode the budget; batch review across multiple slices\n\n**[C-dep-direction]**\n- Given: adding a Pasture dependency\n- When: determining direction\n- Then: parent blocked-by child: \"${EPOCH_REC}\" block \"${STAYS_OPEN_URI}\" --by \"${MUST_FINISH_FIRST_URI}\"\n- Should not: invert (child blocked-by parent)\n\n_Example (correct)_ — also illustrates: C-audit-dep-chain\n\n```bash\n\"${EPOCH_REC}\" block \"${REQUEST_ID_URI}\" --by \"${URE_ID_URI}\"\n```\n\n_Example (anti-pattern)_\n\n```bash\n\"${EPOCH_REC}\" block \"${URE_ID_URI}\" --by \"${REQUEST_ID_URI}\"\n```\n\n**[C-frontmatter-refs]**\n- Given: cross-task references (URD, request, etc.)\n- When: linking tasks\n- Then: use description frontmatter references: block\n- Should not: invent relationship commands or use blocking dependencies for reference documents\n\n**[C-handoff-skill-invocation]**\n- Given: an agent is launched for a new phase (especially p7 to p8 handoff)\n- When: composing the launch prompt\n- Then: prompt MUST start with Skill(\u002Fpasture:{role}) invocation directive so the agent loads its role instructions\n- Should not: launch agents without skill invocation — they skip role-critical procedures like ephemeral exploration and leaf task creation\n\n**[C-integration-points]**\n- Given: multiple vertical slices share types, interfaces, or data flows\n- When: decomposing IMPL_PLAN in Phase 8\n- Then: identify horizontal Layer Integration Points and document them in IMPL_PLAN; each integration point specifies: owning slice, consuming slices, shared contract, merge timing; include integration points in slice descriptions so workers know what to export and import\n- Should not: leave cross-slice dependencies implicit; assume workers will discover contracts on their own\n\n**[C-review-consensus]**\n- Given: review cycle (p4 or p10)\n- When: evaluating\n- Then: all 3 reviewers must ACCEPT before proceeding\n- Should not: proceed with any REVISE vote outstanding\n\n**[C-review-effort-budget]**\n- Given: the start of Phase 8 (IMPL_PLAN), like the Phase-1 research-depth gate\n- When: deciding how much review-and-fix effort to spend per wave\n- Then: request a configurable review-effort budget from the user — defaults: (1) three rounds, (2) one round, (3) zero rounds, (4) unlimited, (5) custom; the review->fix->re-review loop iterates up to the chosen budget; convergence runs at the end over open `pasture:finding` tasks across all waves and targets 0\u002F0\u002F0 within maxRounds; on budget exhaustion without convergence, surface the outstanding findings to the user for a decision\n- Should not: hardcode the review-cycle budget (e.g. an unconditional fixed cap baked into the prose instead of asked); proceed past the chosen budget without surfacing outstanding findings to the user; loop forever when a finite budget was chosen\n\n**[C-slice-review-before-close]**\n- Given: workers complete their implementation slices\n- When: slice implementation is done\n- Then: workers notify supervisor with \"${EPOCH_REC}\" note (not \"${EPOCH_REC}\" close); slices must be reviewed at least once by reviewers before closure; only the supervisor closes slices, after review passes\n- Should not: close slices immediately upon worker completion; allow workers to close their own slices\n\n**[C-supervisor-explore-ephemeral]**\n- Given: supervisor needs codebase exploration\n- When: starting Phase 8 (IMPL_PLAN)\n- Then: spawn ephemeral Explore subagents via Agent tool for scoped codebase queries; each subagent is short-lived and returns findings; no standing team overhead\n- Should not: explore the codebase directly as supervisor; maintain a standing explore team\n\n**[C-uat-feedback-disposition]**\n- Given: any UAT feedback item (Phase 5 or Phase 11) — flagged by the user OR a deferral proposed by the architect\u002Fsupervisor\n- When: recording each item\n- Then: assign every item an explicit, user-confirmed disposition of FIX-NOW, DEFER, or WON'T-DO; deferrals may be agent-proposed, but ALL deferred items — whoever proposed them — MUST be raised to the user at the next user gate (URE, Plan UAT, or Impl UAT) for confirmation; FIX-NOW items are resolved in the current wave, DEFER'd items are the SOLE source feeding the FOLLOWUP epic\n- Should not: leave a feedback item without a confirmed disposition; silently defer any item without raising it to the user at the next gate; route any review severity (BLOCKER\u002FIMPORTANT\u002FMINOR) into FOLLOWUP — only DEFER'd UAT items feed it\n\n### Handoffs\n\n_(No handoffs for this role)_\n\n### Startup Sequence\n\n_(No startup sequence defined for this role)_\n\n### Introduction\n\nYou are the master orchestrator for the full 12-phase epoch lifecycle. You delegate planning phases (1-7) to the architect and implementation phases (7-12) to the supervisor.\n\n### What You Own\n\nYou own the full 12-phase lifecycle from Request to Landing. You delegate phases 1-7 to the architect and phases 7-12 to the supervisor. The epoch role coordinates the complete workflow end-to-end and is the only role that spans all phases.\n\n### Inter-Agent Coordination\n\nAgents coordinate through tracker tasks and comments:\n\n| Action | Command |\n|--------|---------|\n| List blocked | `\"${EPOCH_REC}\" query --blocked` |\n| Add progress note | `\"${EPOCH_REC}\" note \"${TASK_ID_URI}\" --author pasture-system\u002F00000000-0000-0000-0000-000000000000 --body \"Progress: ...\"` |\n| List in-progress | `\"${EPOCH_REC}\" query --status=in_progress` |\n| Check task details | `\"${EPOCH_REC}\" show \"${TASK_ID_URI}\"` |\n| Update status | `\"${EPOCH_REC}\" status \"${TASK_ID_URI}\" --set in_progress` |\n\n**-> [Full workflow in PROCESS.md](..\u002Fprotocol\u002FPROCESS.md)** \u003C- All 12 Phases\n\n**[epoch-verbatim-capture]**\n- Given: user provides request\n- When: capturing\n- Then: store verbatim without paraphrasing in Phase 1 REQUEST task\n- Should not: summarize or interpret the user's words\n\n**[epoch-dep-chain]**\n- Given: any phase transition\n- When: creating new task\n- Then: add dependency to previous: \"${EPOCH_REC}\" block \"${PARENT_URI}\" --by \"${CHILD_URI}\"\n- Should not: skip dependency chaining\n\n**[epoch-audit-never-delete]**\n- Given: task completion\n- When: updating\n- Then: add comments and labels only\n- Should not: close or delete tasks prematurely\n\n**[epoch-consensus-required]**\n- Given: review cycle\n- When: any REVISE vote\n- Then: create PROPOSAL-N+1 and repeat review\n- Should not: proceed without full ACCEPT consensus from all 3 reviewers\n\n**[epoch-followup-trigger]**\n- Given: UAT (Phase 5 or 11) produces one or more user-DEFER'd items\n- When: finishing UAT\n- Then: Supervisor creates a follow-up epic (label pasture:epic-followup) from the user-DEFER'd UAT items only\n- Should not: create a follow-up epic from any review severity (BLOCKER\u002FIMPORTANT\u002FMINOR) — the FOLLOWUP epic is fed only by user-DEFER'd UAT items\n\n**[epoch-supervisor-not-idle]**\n- Given: a freshly spawned supervisor (Phase 8 IMPL_PLAN)\n- When: it dispatches Explore subagents and appears idle\n- Then: let it work — an apparently-idle supervisor is usually running Explore subagents to map the codebase\n- Should not: shut down or restart a supervisor that looks idle at the start of the IMPL_PLAN phase\n\n**[frag--review-clean-exit]**\n- Given: per-wave code review and end-of-epoch convergence\n- When: evaluating review results\n- Then: iterate review -> fix -> re-review up to the chosen review-effort budget; during a wave only an open BLOCKER finding gates progress, while IMPORTANT and MINOR findings stay open; after the last wave, converge over open `pasture:finding` tasks across all waves toward 0\u002F0\u002F0 within maxRounds; a finding closes only per `C-finding-lifecycle`; on budget exhaustion without convergence, SURFACE the outstanding findings to the user at a gate for a decision\n- Should not: hardcode the budget; proceed past the chosen budget without surfacing outstanding findings to the user; require a zeroed round at a wave boundary; close a finding outside `C-finding-lifecycle`; loop forever when a finite budget was chosen\n\n**[epoch-autonomous-progression]**\n- Given: non-user-gated phase completes\n- When: transitioning\n- Then: proceed autonomously; User gates: [request (p1 research depth); elicit (p2 requirements interview); plan-review (p5 plan UAT); impl-plan (p8 implementation effort and review budget); impl-uat (p11 implementation UAT)]\n- Should not: ask 'Should I proceed?' for autonomous phases; add user gates beyond the defined list\n\n**[epoch-uat-auto-ratify]**\n- Given: Phase 5 UAT ACCEPT\n- When: transitioning to Phase 6\n- Then: ratify automatically\n- Should not: ask user for ratification confirmation\n\n**[epoch-frontmatter-refs]**\n- Given: cross-task references\n- When: linking related tasks (e.g. URD to REQUEST)\n- Then: use description frontmatter references: block\n- Should not: use peer-reference commands\n\n## Task Recovery\n\nThe tracker backend is recorded once in .agents.local\u002Fpasture\u002FEPOCH.md; read that file instead of re-probing. If EPOCH.md is absent, stop and ask the orchestrator to run detection.\n\nRecover from live tracker state at session start and after compaction; never trust cached task rows. Read the `rec:` path from the header and use it as the one entry point: `\"${EPOCH_REC}\" \u003Cop>`. The operation set is closed: create, label, block, note, show, query, ready, status, close, whoami, signal.\n\nStore selection: the adapter reads PASTURE_DB_PATH when set, otherwise the header's `db:`; the probe wrote that value from the same precedence (XDG_DATA_HOME\u002Fpasture\u002Fpasture.db, HOME\u002F.local\u002Fshare\u002Fpasture\u002Fpasture.db, then .pasture\u002Fpasture.db). Use the same selected store for every command. If a recovery query fails, report its actual store path, failed operation, impact, and permission\u002Fschema\u002Fconfiguration repair; failure is not an empty work queue.\n\nThe adapter passes `--namespace` only when EPOCH.md records `namespace:`. That pin wins on both backends. Otherwise it omits the flag. Pasture then derives the namespace itself: PASTURE_NAMESPACE, else the git remote, then file:\u002F\u002F of the working directory. The ledger calls derive_namespace for the header and for every write that has no pin. That function is the one list of remote forms; do not restate it. With no origin, both backends use file:\u002F\u002F of the working directory, so invocations from different subdirectories use different namespaces. Queries without a pin follow that backend's derivation; pass `--all-namespaces` to opt out. When neither a pin nor PASTURE_NAMESPACE is set, pasture reads are unscoped and creation falls back to the git remote, then file:\u002F\u002F of the working directory. Inspect the returned full URI before choosing repository work. Separate reads are not an atomic snapshot.\n\nRun `\"${EPOCH_REC}\" ready`, `\"${EPOCH_REC}\" query --blocked`, and `\"${EPOCH_REC}\" query --status in_progress`. For each referenced active task, run `\"${EPOCH_REC}\" show \"$TASK_URI\"`, `\"${EPOCH_REC}\" show \"$TASK_URI\" --notes`, and `\"${EPOCH_REC}\" show \"$TASK_URI\" --history`. All *_URI variables in examples are inputs bound to full task URIs from an assignment, a verified tracker read, or the URI create printed; never use legacy short IDs. Resolve each variable before execution and quote it as one operand.\n\nCreate prints the new task URI directly. Persist it before any label or note: create-plus-label is non-atomic, so on failure retain the URI and retry only the failed label\u002Fnote operation, never create again. Only open, in_progress, and closed are statuses; blockage is represented by live dependencies and notes, not a blocked status.\n\nAn owner is recorded with `\"${EPOCH_REC}\" label \"$TASK_URI\" owner:\u003Crole>`. That is a claim, not an allocation: an unassigned task stays unassigned until the supervisor allocates it, and repeating an identical claim is not allocation. Never invent an owner.\n\nParent stays open and is blocked by child: `\"${EPOCH_REC}\" block \"$PARENT_URI\" --by \"$CHILD_URI\"`. Reference documents belong in description frontmatter under references:, never fabricated blockers. Workers report evidence and handoffs without closing their slices or leaves; the supervisor closes only after independent review and satisfied gates. Use git agent-commit for local commits. Never install, enable, or modify Git hooks, Git hook path configuration, or pre-commit integration without explicit user approval. Tracker writes are durable; Git history lands separately.\n\nIf the header records `backend: error`, stop before the first mutation: the store exists but cannot be used, and falling back could lose earlier epochs. A mid-epoch write failure retries 3 times. After the third failure, write the attempted record to .agents.local\u002Fpasture\u002Fpending\u002F\u003Ctimestamp>-\u003Cop>.md, keep the recorded backend, and surface the pending record to the user at the next gate. Never switch backends and never drop the record.\n\n## Task Attribution\n\nSelect the current registered author with \"${EPOCH_REC}\" whoami. It prints the author recorded in the header or PASTURE_AUTHOR, and on the pasture backend it confirms that actor with agents show. It does not list agents and it does not invent one. If neither source names an author, it stops: set author: in EPOCH.md or export PASTURE_AUTHOR. Never auto-register or guess an identity during recovery. Every note supplies --author explicitly; machine examples use the pre-registered pasture-system\u002F00000000-0000-0000-0000-000000000000 actor. If it is absent, stop and request registration from the operator rather than claiming registration is fixed. Preserve human intent verbatim and quote historical attribution as evidence, not as a forged new author.\n\n## Tracker Adapter\n\nTracker calls go through one entry point: `\"${EPOCH_REC}\" \u003Cop>`. The backend is chosen once, recorded in `.agents.local\u002Fpasture\u002FEPOCH.md`, and read from there; no skill body names a backend recipe.\n\nResolve the entry point from the header at the start of each shell session:\n\n~~~bash\nEPOCH_REC=$(sed -n 's\u002F^rec: \u002F\u002Fp' .agents.local\u002Fpasture\u002FEPOCH.md 2>\u002Fdev\u002Fnull | tail -1)\nif [ -z \"$EPOCH_REC\" ]; then\n  echo \"no epoch header at .agents.local\u002Fpasture\u002FEPOCH.md; run the epoch detection step\" >&2\n  exit 3\nfi\n~~~\n\n| Backend | Meaning | Action |\n|---|---|---|\n| `pasture` | binary is capable and the store is usable | forward each op through the adapter |\n| `ledger` | no usable binary or store | record in the ledger under `.agents.local\u002Fpasture\u002F`; `assign` is claim-only |\n| `error` | the store exists but cannot be used | diagnose and stop before the first mutation; do not fall back |\n\nThe operation set is closed. `relate` is `block --kind`; `assign` is `label ID owner:\u003Crole>`.\n\n| Op | Canonical call | Action |\n|---|---|---|\n| `create` | `\"${EPOCH_REC}\" create --kind K --title T --phase P [--priority N] [--label L]... [--ref K=V]... [--body TEXT|@-] -> ID` | create an immutable record; the body is verbatim; print the new task URI |\n| `label` | `\"${EPOCH_REC}\" label ID L   (assignment: label ID owner:\u003Crole>)` | add a label; never remove one |\n| `block` | `\"${EPOCH_REC}\" block PARENT --by CHILD [--kind blocked_by|derived_from|supersedes|discovered_from]` | add an edge; a blocked_by edge keeps the parent open until the child closes; a typed edge uses --kind |\n| `note` | `\"${EPOCH_REC}\" note ID --author A [--kind K] [--to ROLE] [--body TEXT|@-]` | append an attributed immutable note; --to makes it a message |\n| `show` | `\"${EPOCH_REC}\" show ID [--notes] [--history] [--format text|json]` | read one record, including description, phase, priority and refs; --notes lists notes. Pasture show returns createdAt and updatedAt; ledger timestamps stay on --history |\n| `query` | `\"${EPOCH_REC}\" query [--status S] [--label L] [--blocked] [--tree ID] [--all-namespaces] [--format text|json]` | enumerate records by status, label, blocked state, or dependency tree; --blocked rejects --status |\n| `ready` | `\"${EPOCH_REC}\" ready [--label L] [--all-namespaces] [--format text|json]` | list non-closed records with no non-closed blocker |\n| `status` | `\"${EPOCH_REC}\" status ID --set open|in_progress|closed` | change state (start work); the store keeps the only status field |\n| `close` | `\"${EPOCH_REC}\" close ID --reason R` | close a record; policy reserves this for the supervisor |\n| `whoami` | `\"${EPOCH_REC}\" whoami` | resolve the acting author; never invent one |\n| `signal` | `\"${EPOCH_REC}\" signal --kind K --phase P --skill S --entry E --quote Q` | record one user signal; pasture notes the feedback task, ledger appends one JSON object |\n\nEPOCH.md lives at .agents.local\u002Fpasture\u002FEPOCH.md (offer a gitignore entry; never require one).\n\n---\nepoch: \u003Cepoch id>\nbackend: pasture|ledger|error\nprobed_at: \u003CUTC timestamp>\nnamespace: \u003Cnamespace URI>        # optional pin; absent means the backend derives\ndb: \u003Cabsolute store path>         # pasture only\nauthor: \u003Cregistered actor id>     # pasture only; what whoami prints\nledger: \u003Cabsolute ledger path>    # ledger only\nrec: \u003Cabsolute adapter path>      # pasture and ledger\nfeedback: \u003Ctask URI>              # pasture only; the epoch feedback task for signals\nreviewer_tier: T2|T3              # optional; the phase-8 review-effort choice\ndegrades: assign=claim-only       # ledger only\n---\n\u003Cthe probe transcript and the user's one-line acknowledgement, verbatim>\n\nNo mid-epoch switching: if the recorded backend fails mid-epoch, stop and report. Switching is never a repair; abandon the epoch and start a new one with a fresh probe, so the header and the records never describe different backends.\n\nA mid-epoch write failure retries 3 times. After the third failure, write the attempted record to .agents.local\u002Fpasture\u002Fpending\u002F\u003Ctimestamp>-\u003Cop>.md, keep the recorded backend, and surface the pending record to the user at the next gate. Never switch backends and never drop the record.\n\nThe orchestrator runs the probe once. Subagents read EPOCH.md and never re-probe.\n\nWrite this probe to `.agents.local\u002Fpasture\u002Fprobe.sh` and run it before the first write:\n\n\u003C!-- pasture:asset probe -->\n~~~sh\n#!\u002Fbin\u002Fsh\n# derive_namespace is the one ledger normalisation. Forms: git@host:path,\n# ssh:\u002F\u002F, git:\u002F\u002F, http:\u002F\u002F, https:\u002F\u002F and user@host:path. An empty remote inside\n# a git work tree is file:\u002F\u002F of the physical working directory, matching\n# Pasture. PASTURE_NAMESPACE matches the value Pasture uses when\n# --namespace is omitted.\nderive_namespace() {\n  if [ -n \"${PASTURE_NAMESPACE:-}\" ]; then\n    printf '%s\\n' \"$PASTURE_NAMESPACE\"\n    return 0\n  fi\n  remote=$(git remote get-url origin 2>\u002Fdev\u002Fnull || true)\n  remote=$(printf '%s' \"$remote\" | tr -d '\\r\\n')\n  if [ -z \"$remote\" ]; then\n    dir=$(pwd -P 2>\u002Fdev\u002Fnull || pwd)\n    if [ -n \"${PWD:-}\" ] && [ \"$(cd -- \"$PWD\" 2>\u002Fdev\u002Fnull && pwd -P)\" = \"$dir\" ]; then\n      dir=$PWD\n    fi\n    printf 'file:\u002F\u002F%s\\n' \"$dir\"\n    return 0\n  fi\n  normalize_git_remote \"$remote\"\n}\n\nnormalize_git_remote() {\n  url=$1\n  tab=$(printf '\\t')\n  while :; do\n    case $url in\n      ' '*) url=${url# } ;;\n      \"$tab\"*) url=${url#\"$tab\"} ;;\n      *) break ;;\n    esac\n  done\n  while :; do\n    case $url in\n      *' ') url=${url% } ;;\n      *\"$tab\") url=${url%\"$tab\"} ;;\n      *) break ;;\n    esac\n  done\n  case \"$url\" in\n    ssh:\u002F\u002F*)\n      rest=${url#ssh:\u002F\u002F}\n      case \"$rest\" in\n        *@*) rest=${rest#*@} ;;\n      esac\n      url=\"https:\u002F\u002F$rest\"\n      ;;\n    git:\u002F\u002F*)\n      url=\"https:\u002F\u002F${url#git:\u002F\u002F}\"\n      ;;\n    http:\u002F\u002F*)\n      url=\"https:\u002F\u002F${url#http:\u002F\u002F}\"\n      ;;\n    https:\u002F\u002F*)\n      ;;\n    *@*:*)\n      rest=${url#*@}\n      host=${rest%%:*}\n      path=${rest#*:}\n      url=\"https:\u002F\u002F$host\u002F$path\"\n      ;;\n  esac\n  case \"$url\" in\n    *.git) url=${url%.git} ;;\n  esac\n  printf '%s\\n' \"$url\"\n}\n\n# Epoch tracker probe. Side-effect free. Prints KEY=VALUE lines and exits 0.\n# Order: capability (--help), store existence (test -f), usability (migrate --dry-run).\n# Verb list is generated from TrackerOps.\nset -u\n\nPASTURE_BIN=\"${PASTURE_BIN:-pasture}\"\nLEDGER_DIR=\"${PWD}\u002F.agents.local\u002Fpasture\u002Fledger\"\nREC_PATH=\"${PWD}\u002F.agents.local\u002Fpasture\u002Frec\"\n\nemit() {\n  printf 'BACKEND=%s\\n' \"$1\"\n  printf 'REASON=%s\\n' \"${2:-}\"\n  [ -n \"${3:-}\" ] && printf 'DB=%s\\n' \"$3\"\n  [ -n \"${4:-}\" ] && printf 'NS=%s\\n' \"$4\"\n  [ -n \"${5:-}\" ] && printf 'ACT=%s\\n' \"$5\"\n  [ -n \"${6:-}\" ] && printf 'VER=%s\\n' \"$6\"\n  printf 'LEDGER=%s\\n' \"$LEDGER_DIR\"\n  printf 'REC=%s\\n' \"$REC_PATH\"\n  printf 'PROBED_AT=%s\\n' \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"\n  exit 0\n}\n\n# Ledger header value. Same function every ledger write calls.\nemit_ledger() {\n  _ns=$(derive_namespace)\n  emit ledger \"$1\" \"${2:-}\" \"$_ns\" \"\" \"${3:-}\"\n}\n\n# 1. Capability. --help never opens a store.\nif ! command -v \"$PASTURE_BIN\" >\u002Fdev\u002Fnull 2>&1; then\n  emit_ledger not-installed\nfi\n\nVER=\"$(\"$PASTURE_BIN\" --version 2>\u002Fdev\u002Fnull | awk '{print $NF}')\"\n\n\"$PASTURE_BIN\" --help >\u002Fdev\u002Fnull 2>&1\nif [ \"$?\" -ne 0 ]; then\n  emit_ledger help-failed \"\" \"$VER\"\nfi\n\nfor VERB in \"task create\" \"task label\" \"task dep\" \\\n            \"task comment\" \"task comments\" \"task timeline\" \\\n            \"task show\" \"task blocked\" \"task list\" \\\n            \"task ready\" \"task update\" \"task close\" \\\n            \"task agents\"; do\n  # shellcheck disable=SC2086\n  \"$PASTURE_BIN\" $VERB --help >\u002Fdev\u002Fnull 2>&1\n  if [ \"$?\" -ne 0 ]; then\n    emit_ledger \"missing-verb:$VERB\" \"\" \"$VER\"\n  fi\ndone\n\n# 2. Store existence. Never open a missing store: opening creates it.\nif [ -n \"${PASTURE_DB_PATH:-}\" ]; then\n  DB=\"$PASTURE_DB_PATH\"\nelif [ -n \"${XDG_DATA_HOME:-}\" ]; then\n  DB=\"$XDG_DATA_HOME\u002Fpasture\u002Fpasture.db\"\nelif [ -n \"${HOME:-}\" ]; then\n  DB=\"$HOME\u002F.local\u002Fshare\u002Fpasture\u002Fpasture.db\"\nelse\n  DB=\".pasture\u002Fpasture.db\"\nfi\n\nif [ ! -f \"$DB\" ]; then\n  emit_ledger no-store \"$DB\" \"$VER\"\nfi\n\n# 3. Usability. --dry-run leaves the file's SHA-256 unchanged.\n\"$PASTURE_BIN\" --db \"$DB\" migrate --dry-run >\u002Fdev\u002Fnull 2>&1\nRC=$?\nif [ \"$RC\" -ne 0 ]; then\n  emit error \"store-exit-$RC\" \"$DB\" \"\" \"\" \"$VER\"\nfi\n\n# 4. Author. A missing author is a warning, not an unusable store.\n# Pasture: the probe does not derive a namespace. Writing one into\n# EPOCH.md would freeze it and override Pasture's own derivation.\n# Ledger: emit_ledger derives and prints NS because there is no Pasture store\n# to ask.\n\nACT=\"${PASTURE_AUTHOR:-pasture-system\u002F00000000-0000-0000-0000-000000000000}\"\n\"$PASTURE_BIN\" --db \"$DB\" task agents show \"$ACT\" --format json >\u002Fdev\u002Fnull 2>&1\nRC=$?\nif [ \"$RC\" -ne 0 ]; then\n  printf 'WARN=no-author\\n'\n  printf 'probe: warning: author %s is not registered in %s\\n  why: task agents show exited %s, and a missing author is not an unusable store\\n  where: epoch tracker probe, author check\\n  fix: register the actor, or export PASTURE_AUTHOR to a registered id; the epoch still records backend pasture\\n' \"$ACT\" \"$DB\" \"$RC\" >&2\nfi\n\nemit pasture \"\" \"$DB\" \"\" \"$ACT\" \"$VER\"\n~~~\n\u003C!-- \u002Fpasture:asset probe -->\n\nDetection limits, by step:\n\n- pasture --help: detects that the binary is on PATH and that each required verb has a help page. It cannot detect version compatibility, store health, permissions, schema, namespace or author. A help page can exist on a binary whose runtime is broken.\n- test -f: detects a missing store file and stops the probe before anything opens it. It cannot detect a directory, an unreadable or corrupt database, an out-of-date schema, a held lock, or a different store selected by configuration. Never replace it with a read: opening a missing store creates it.\n- migrate --dry-run: detects that the store opens and that its migrations can be planned; it leaves the file's SHA-256 unchanged. It still opens the store and may take locks. It cannot detect concurrent writers, a defect that only a real migration touches, an unregistered author, or a namespace mismatch. A non-zero result is the only error backend: the store exists but cannot be used.\n- Namespace: for pasture, the probe does not derive a namespace and does not write one into the header. Writing one would freeze Pasture's own derivation (PASTURE_NAMESPACE, else the git origin, else file:\u002F\u002F of the working directory). For ledger, there is no Pasture store to ask, so the probe derives and prints NS from derive_namespace, and every unpinned ledger write uses that value. A missing remote is not an unusable store. It cannot detect a remote other than origin.\n- Author: a missing or unregistered author is a warning (WARN=no-author), not an error backend. It cannot detect an author registered in another namespace, nor prove a later write will be allowed.\n- The probe cannot detect a store chosen by configuration outside the env\u002Fdefault precedence, and it does not prove that any later write will succeed.\n\nWrite this adapter to the path the header records in `rec:` and make it executable; every tracker call goes through it:\n\n\u003C!-- pasture:asset rec -->\n~~~sh\n#!\u002Fbin\u002Fsh\n# derive_namespace is the one ledger normalisation. Forms: git@host:path,\n# ssh:\u002F\u002F, git:\u002F\u002F, http:\u002F\u002F, https:\u002F\u002F and user@host:path. An empty remote inside\n# a git work tree is file:\u002F\u002F of the physical working directory, matching\n# Pasture. PASTURE_NAMESPACE matches the value Pasture uses when\n# --namespace is omitted.\nderive_namespace() {\n  if [ -n \"${PASTURE_NAMESPACE:-}\" ]; then\n    printf '%s\\n' \"$PASTURE_NAMESPACE\"\n    return 0\n  fi\n  remote=$(git remote get-url origin 2>\u002Fdev\u002Fnull || true)\n  remote=$(printf '%s' \"$remote\" | tr -d '\\r\\n')\n  if [ -z \"$remote\" ]; then\n    dir=$(pwd -P 2>\u002Fdev\u002Fnull || pwd)\n    if [ -n \"${PWD:-}\" ] && [ \"$(cd -- \"$PWD\" 2>\u002Fdev\u002Fnull && pwd -P)\" = \"$dir\" ]; then\n      dir=$PWD\n    fi\n    printf 'file:\u002F\u002F%s\\n' \"$dir\"\n    return 0\n  fi\n  normalize_git_remote \"$remote\"\n}\n\nnormalize_git_remote() {\n  url=$1\n  tab=$(printf '\\t')\n  while :; do\n    case $url in\n      ' '*) url=${url# } ;;\n      \"$tab\"*) url=${url#\"$tab\"} ;;\n      *) break ;;\n    esac\n  done\n  while :; do\n    case $url in\n      *' ') url=${url% } ;;\n      *\"$tab\") url=${url%\"$tab\"} ;;\n      *) break ;;\n    esac\n  done\n  case \"$url\" in\n    ssh:\u002F\u002F*)\n      rest=${url#ssh:\u002F\u002F}\n      case \"$rest\" in\n        *@*) rest=${rest#*@} ;;\n      esac\n      url=\"https:\u002F\u002F$rest\"\n      ;;\n    git:\u002F\u002F*)\n      url=\"https:\u002F\u002F${url#git:\u002F\u002F}\"\n      ;;\n    http:\u002F\u002F*)\n      url=\"https:\u002F\u002F${url#http:\u002F\u002F}\"\n      ;;\n    https:\u002F\u002F*)\n      ;;\n    *@*:*)\n      rest=${url#*@}\n      host=${rest%%:*}\n      path=${rest#*:}\n      url=\"https:\u002F\u002F$host\u002F$path\"\n      ;;\n  esac\n  case \"$url\" in\n    *.git) url=${url%.git} ;;\n  esac\n  printf '%s\\n' \"$url\"\n}\n\n# Epoch tracker adapter (rec). Generated from TrackerOps. Do not hand-edit.\n# Backend: the one recorded in EPOCH_FILE. Never re-probes, never switches.\nset -u\n\nEPOCH_FILE=\"${EPOCH_FILE:-.agents.local\u002Fpasture\u002FEPOCH.md}\"\nPASTURE_BIN=\"${PASTURE_BIN:-pasture}\"\n\nfail() {\n  printf 'rec: %s\\n  why: %s\\n' \"$1\" \"$2\" >&2\n  if [ -n \"${4:-}\" ]; then\n    printf '  where: %s\\n' \"$4\" >&2\n  fi\n  printf '  fix: %s\\n' \"$3\" >&2\n  exit 3\n}\n\n[ -f \"$EPOCH_FILE\" ] || fail \"no epoch header at $EPOCH_FILE\" \\\n  \"the backend is chosen once and recorded before the first write; EPOCH_FILE is relative to the working directory\" \\\n  \"run from the repository root, or set EPOCH_FILE to the absolute header path, then run the Starting an Epoch detection step\"\n\nfield() {\n  sed -n \"s\u002F^$1: \u002F\u002Fp\" \"$EPOCH_FILE\" 2>\u002Fdev\u002Fnull | tail -1\n}\n\nBACKEND=\"$(field backend)\"\nDB=\"${PASTURE_DB_PATH:-$(field db)}\"\n# --namespace is passed only when EPOCH.md records one. Otherwise pasture\n# derives (env, else git remote, else file:\u002F\u002F of cwd). Ledger calls the same\n# derivation function the probe printed, unless the header pins a namespace.\nHEADER_NS=\"$(field namespace)\"\nif [ \"$BACKEND\" = ledger ] && [ -z \"$HEADER_NS\" ]; then\n  NS=$(derive_namespace)\nelse\n  NS=\"$HEADER_NS\"\nfi\nAUTHOR=\"${PASTURE_AUTHOR:-$(field author)}\"\nLEDGER=\"$(field ledger)\"\nif [ -z \"$LEDGER\" ]; then\n  LEDGER=\".agents.local\u002Fpasture\u002Fledger\"\nfi\n\ncase \"$BACKEND\" in\n  pasture) : ;;\n  ledger) : ;;\n  error)\n    fail \"the recorded backend is error\" \\\n      \"the store exists but cannot be used, and falling back could lose earlier epochs\" \\\n      \"repair the store named in $EPOCH_FILE, or start a new epoch with a working backend\" ;;\n  \"\")\n    fail \"no backend recorded in $EPOCH_FILE\" \\\n      \"the probe has not written the header yet\" \\\n      \"run the Starting an Epoch detection step\" ;;\n  *)\n    fail \"unknown backend '$BACKEND' in $EPOCH_FILE\" \\\n      \"the backend set is closed to pasture, ledger and error\" \\\n      \"rewrite the backend line from the probe output\" ;;\nesac\n\nOP=\"${1:-}\"\n[ \"$#\" -gt 0 ] && shift\n\nallns=0\nblocked=0\nrefs=\nlabels=\nbody=\nkind=task\ntitle=\nphase=\npriority=\nlabel=\nstatus=\ntree=\nfmt=json\nmode=show\nreason=\nnote_body=\nnote_kind=\nnote_to=\nparent=\nchild=\nedge_kind=blocked_by\nid=\n\npb() {\n  if [ -n \"$DB\" ]; then\n    \"$PASTURE_BIN\" --db \"$DB\" \"$@\"\n  else\n    \"$PASTURE_BIN\" \"$@\"\n  fi\n}\n\nneed_id() {\n  [ -n \"${1:-}\" ] || fail \"$OP needs a task ID\" \"the first operand was empty\" \"pass the full task URI as the first operand\"\n}\n\n# json_escape and row_encode are total for every byte except NUL, which a\n# shell argument cannot carry. Each is the inverse of its reader (take, and\n# row_decode). A trailing newline is kept: the decoder is not read with a\n# bare command substitution. C0 controls other than tab, CR and LF are\n# \\u00XX, because a raw control byte makes show --format json invalid JSON.\njson_escape() {\n  ROW_IN=$1 awk 'BEGIN {\n    s = ENVIRON[\"ROW_IN\"]\n    out = \"\"\n    for (i = 0; i \u003C 32; i++) ctrl[sprintf(\"%c\", i)] = i\n    for (i = 1; i \u003C= length(s); i++) {\n      c = substr(s, i, 1)\n      if (c == \"\\\\\") out = out \"\\\\\\\\\"\n      else if (c == \"\\\"\") out = out \"\\\\\\\"\"\n      else if (c == \"\\t\") out = out \"\\\\t\"\n      else if (c == \"\\r\") out = out \"\\\\r\"\n      else if (c == \"\\n\") out = out \"\\\\n\"\n      else if (c in ctrl) out = out sprintf(\"\\\\u00%02x\", ctrl[c])\n      else out = out c\n    }\n    printf \"%s\", out\n  }'\n}\n\n# text_field keeps one show --format text record to one line per key. A\n# description that contains a newline would otherwise span lines, and the\n# next line would look like a new key.\ntext_field() {\n  ROW_IN=$1 awk 'BEGIN {\n    s = ENVIRON[\"ROW_IN\"]\n    out = \"\"\n    for (i = 1; i \u003C= length(s); i++) {\n      c = substr(s, i, 1)\n      if (c == \"\\\\\") out = out \"\\\\\\\\\"\n      else if (c == \"\\n\") out = out \"\\\\n\"\n      else if (c == \"\\r\") out = out \"\\\\r\"\n      else out = out c\n    }\n    printf \"%s\", out\n  }'\n}\n\nrow_encode() {\n  ROW_IN=$1 awk 'BEGIN {\n    s = ENVIRON[\"ROW_IN\"]\n    out = \"\"\n    for (i = 1; i \u003C= length(s); i++) {\n      c = substr(s, i, 1)\n      if (c == \"\\\\\") out = out \"\\\\\\\\\"\n      else if (c == \"\\n\") out = out \"\\\\n\"\n      else if (c == \"\\r\") out = out \"\\\\r\"\n      else if (c == \"\\t\") out = out \"\\\\t\"\n      else if (c == \" \") out = out \"\\\\s\"\n      else if (c == \"\\037\") out = out \"\\\\037\"\n      else out = out c\n    }\n    printf \"%s\", out\n  }'\n}\n\nrow_decode() {\n  ROW_IN=$1 awk 'BEGIN {\n    s = ENVIRON[\"ROW_IN\"]\n    esc = 0\n    out = \"\"\n    i = 1\n    n = length(s)\n    while (i \u003C= n) {\n      c = substr(s, i, 1)\n      if (esc) {\n        if (c == \"n\") out = out \"\\n\"\n        else if (c == \"r\") out = out \"\\r\"\n        else if (c == \"t\") out = out \"\\t\"\n        else if (c == \"s\") out = out \" \"\n        else if (c == \"0\" && substr(s, i, 3) == \"037\") { out = out \"\\037\"; i += 2 }\n        else if (c == \"\\\\\") out = out \"\\\\\"\n        else out = out c\n        esc = 0\n      } else if (c == \"\\\\\") esc = 1\n      else out = out c\n      i++\n    }\n    if (esc) out = out \"\\\\\"\n    printf \"%s\", out\n  }'\n}\n\n# Sets decoded. A bare $(row_decode) would eat a trailing newline.\nrow_decode_set() {\n  _raw=$(row_decode \"$1\"; printf '\\003')\n  _mark=$(printf '\\003')\n  decoded=${_raw%\"$_mark\"}\n}\n\nlabel_push() {\n  enc=$(row_encode \"$1\")\n  if [ -z \"$labels\" ]; then\n    labels=$enc\n  else\n    labels=\"$labels $enc\"\n  fi\n}\n\nfold_refs() {\n  [ -n \"$refs\" ] || return 0\n  block=\"references:\"\n  old=$IFS\n  IFS='\n'\n  for pair in $refs; do\n    key=${pair%%=*}\n    val=${pair#*=}\n    block=\"$block\n  $key: $val\"\n  done\n  IFS=$old\n  if [ -z \"$body\" ]; then\n    body=\"---\n$block\n---\"\n  else\n    body=\"---\n$block\n---\n$body\"\n  fi\n}\n\nmint_id() {\n  if [ -r \u002Fproc\u002Fsys\u002Fkernel\u002Frandom\u002Fuuid ]; then\n    printf 'ledger\u002F%s\\n' \"$(cat \u002Fproc\u002Fsys\u002Fkernel\u002Frandom\u002Fuuid)\"\n    return 0\n  fi\n  printf 'ledger\u002F%s-%s\\n' \"$(date -u +%Y%m%dT%H%M%SZ)\" \"$$\"\n}\n\nledger_append() {\n  rec_id=\"$1\"\n  rec_line=\"$2\"\n  mkdir -p \"$LEDGER\" || fail \"cannot create ledger directory $LEDGER\" \"the parent is not writable\" \"choose a writable ledger: path in $EPOCH_FILE\"\n  safe=$(printf '%s' \"$rec_id\" | sed -e 's\u002F%\u002F%25\u002Fg' -e 's#\u002F#%2F#g' -e 's\u002F \u002F%20\u002Fg' -e 's\u002F:\u002F%3A\u002Fg')\n  file=\"$LEDGER\u002F$safe.jsonl\"\n  lock=\"$LEDGER\u002F.lock\"\n  n=0\n  while ! mkdir \"$lock\" 2>\u002Fdev\u002Fnull; do\n    n=$((n + 1))\n    if [ \"$n\" -gt 100 ]; then\n      fail \"ledger lock held at $lock\" \"another rec process did not release it\" \"remove $lock only if no rec process is running, then retry\"\n    fi\n    sleep 0.1\n  done\n  if [ ! -f \"$LEDGER\u002F.gitignore-offered\" ]; then\n    printf 'rec: ledger records are under %s\\n  offer: add that directory to .gitignore if the records should stay uncommitted\\n  note: a gitignore entry is offered, never required\\n' \"$LEDGER\" >&2\n    : > \"$LEDGER\u002F.gitignore-offered\" || true\n  fi\n  printf '%s\\n' \"$rec_line\" >> \"$file\" || {\n    rmdir \"$lock\" 2>\u002Fdev\u002Fnull || true\n    fail \"ledger append failed for $file\" \"the ledger directory was not writable\" \"make $LEDGER writable and retry the same operation\"\n  }\n  rmdir \"$lock\" 2>\u002Fdev\u002Fnull || true\n}\n\nledger_line() {\n  _f_op=\n  _f_id=\n  _f_namespace=\n  _f_title=\n  _f_kind=\n  _f_phase=\n  _f_status=\n  _f_priority=\n  _f_label=\n  _f_parent=\n  _f_child=\n  _f_edge_kind=\n  _f_author=\n  _f_note_kind=\n  _f_to=\n  _f_reason=\n  _f_ref=\n  _f_body=\n  _f_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)\n  while [ \"$#\" -gt 1 ]; do\n    case \"$1\" in\n      op) _f_op=\"$2\"; shift 2 ;;\n      id) _f_id=\"$2\"; shift 2 ;;\n      namespace) _f_namespace=\"$2\"; shift 2 ;;\n      title) _f_title=\"$2\"; shift 2 ;;\n      kind) _f_kind=\"$2\"; shift 2 ;;\n      phase) _f_phase=\"$2\"; shift 2 ;;\n      status) _f_status=\"$2\"; shift 2 ;;\n      priority) _f_priority=\"$2\"; shift 2 ;;\n      label) _f_label=\"$2\"; shift 2 ;;\n      parent) _f_parent=\"$2\"; shift 2 ;;\n      child) _f_child=\"$2\"; shift 2 ;;\n      edge_kind) _f_edge_kind=\"$2\"; shift 2 ;;\n      author) _f_author=\"$2\"; shift 2 ;;\n      note_kind) _f_note_kind=\"$2\"; shift 2 ;;\n      to) _f_to=\"$2\"; shift 2 ;;\n      reason) _f_reason=\"$2\"; shift 2 ;;\n      ref) _f_ref=\"$2\"; shift 2 ;;\n      body) _f_body=\"$2\"; shift 2 ;;\n      *) fail \"ledger_line rejected field '$1'\" \"the ledger field set is closed\" \"use a field from the stable ledger order\" ;;\n    esac\n  done\n  printf '{'\n  printf '\"op\":\"%s\"' \"$(json_escape \"$_f_op\")\"\n  printf ',\"id\":\"%s\"' \"$(json_escape \"$_f_id\")\"\n  printf ',\"at\":\"%s\"' \"$(json_escape \"$_f_at\")\"\n  printf ',\"namespace\":\"%s\"' \"$(json_escape \"$_f_namespace\")\"\n  printf ',\"title\":\"%s\"' \"$(json_escape \"$_f_title\")\"\n  printf ',\"kind\":\"%s\"' \"$(json_escape \"$_f_kind\")\"\n  printf ',\"phase\":\"%s\"' \"$(json_escape \"$_f_phase\")\"\n  printf ',\"status\":\"%s\"' \"$(json_escape \"$_f_status\")\"\n  printf ',\"priority\":\"%s\"' \"$(json_escape \"$_f_priority\")\"\n  printf ',\"label\":\"%s\"' \"$(json_escape \"$_f_label\")\"\n  printf ',\"parent\":\"%s\"' \"$(json_escape \"$_f_parent\")\"\n  printf ',\"child\":\"%s\"' \"$(json_escape \"$_f_child\")\"\n  printf ',\"edge_kind\":\"%s\"' \"$(json_escape \"$_f_edge_kind\")\"\n  printf ',\"author\":\"%s\"' \"$(json_escape \"$_f_author\")\"\n  printf ',\"note_kind\":\"%s\"' \"$(json_escape \"$_f_note_kind\")\"\n  printf ',\"to\":\"%s\"' \"$(json_escape \"$_f_to\")\"\n  printf ',\"reason\":\"%s\"' \"$(json_escape \"$_f_reason\")\"\n  printf ',\"ref\":\"%s\"' \"$(json_escape \"$_f_ref\")\"\n  printf ',\"body\":\"%s\"' \"$(json_escape \"$_f_body\")\"\n  printf '}'\n}\n\nsignal_json() {\n  printf '{\"kind\":\"%s\",\"phase\":\"%s\",\"skill\":\"%s\",\"entry\":\"%s\",\"quote\":\"%s\"}' \\\n    \"$(json_escape \"$kind\")\" \\\n    \"$(json_escape \"$phase\")\" \\\n    \"$(json_escape \"$skill\")\" \\\n    \"$(json_escape \"$entry\")\" \\\n    \"$(json_escape \"$quote\")\"\n}\n\n\nledger_signal() {\n  dir=$(dirname \"$EPOCH_FILE\")\n  file=\"$dir\u002Fsignals.jsonl\"\n  mkdir -p \"$dir\" || fail \"cannot create signal directory $dir\" \"the parent of EPOCH.md is not writable\" \"choose a writable EPOCH_FILE directory and retry the same signal\"\n  printf '%s\\n' \"$signal_line\" >> \"$file\" || fail \"signal append failed for $file\" \"the signal file was not writable\" \"make $dir writable and retry the same signal\"\n}\n\nledger_create() {\n  id=$(mint_id)\n  # Empty priority matches the pasture create flag default so show agrees.\n  [ -n \"$priority\" ] || priority=medium\n  ledger_append \"$id\" \"$(ledger_line op create id \"$id\" namespace \"$NS\" title \"$title\" kind \"$kind\" phase \"$phase\" status open priority \"$priority\" author \"$AUTHOR\" ref \"$refs\" body \"$body\")\"\n  printf '%s\\n' \"$id\"\n  set -f\n  for _enc in $labels; do\n    [ -n \"$_enc\" ] || continue\n    row_decode_set \"$_enc\"\n    label=$decoded\n    ledger_append \"$id\" \"$(ledger_line op label id \"$id\" namespace \"$NS\" label \"$label\" author \"$AUTHOR\")\"\n  done\n  set +f\n}\n\nledger_label() {\n  set -f\n  for _enc in $labels; do\n    [ -n \"$_enc\" ] || continue\n    row_decode_set \"$_enc\"\n    label=$decoded\n    ledger_append \"$id\" \"$(ledger_line op label id \"$id\" namespace \"$NS\" label \"$label\" author \"$AUTHOR\")\"\n  done\n  set +f\n}\n\nledger_block() {\n  ledger_append \"$parent\" \"$(ledger_line op block id \"$parent\" namespace \"$NS\" parent \"$parent\" child \"$child\" edge_kind \"$edge_kind\" author \"$AUTHOR\")\"\n}\n\nledger_note() {\n  ledger_append \"$id\" \"$(ledger_line op note id \"$id\" namespace \"$NS\" author \"$AUTHOR\" note_kind \"$note_kind\" to \"$note_to\" body \"$note_body\")\"\n}\n\nledger_status() {\n  ledger_append \"$id\" \"$(ledger_line op status id \"$id\" namespace \"$NS\" status \"$status\" author \"$AUTHOR\")\"\n}\n\nledger_close() {\n  ledger_append \"$id\" \"$(ledger_line op close id \"$id\" namespace \"$NS\" status closed reason \"$reason\" author \"$AUTHOR\")\"\n}\n\nledger_whoami() {\n  printf '%s\\n' \"$AUTHOR\"\n}\n\nledger_files() {\n  [ -d \"$LEDGER\" ] || return 0\n  find \"$LEDGER\" -name '*.jsonl' -type f | sort\n}\n\nledger_rows() {\n  files=$(ledger_files)\n  [ -n \"$files\" ] || return 0\n  printf '%s\\n' \"$files\" | while IFS= read -r f; do\n    cat \"$f\"\n  done | awk '\nfunction take(line, pos,   i, c, out, esc, hex, v, ok, h, d) {\n  out = \"\"\n  esc = 0\n  for (i = pos + 1; i \u003C= length(line); i++) {\n    c = substr(line, i, 1)\n    if (esc) {\n      if (c == \"n\") out = out \"\\n\"\n      else if (c == \"t\") out = out \"\\t\"\n      else if (c == \"r\") out = out \"\\r\"\n      else if (c == \"u\" && i + 4 \u003C= length(line)) {\n        hex = substr(line, i + 1, 4)\n        v = 0\n        ok = 1\n        for (h = 1; h \u003C= 4; h++) {\n          d = index(\"0123456789abcdef\", tolower(substr(hex, h, 1)))\n          if (d == 0) ok = 0\n          else v = v * 16 + d - 1\n        }\n        if (ok) { out = out sprintf(\"%c\", v); i += 4 }\n        else out = out c\n      }\n      else out = out c\n      esc = 0\n    } else if (c == \"\\\\\") esc = 1\n    else if (c == \"\\\"\") { got = out; got_i = i + 1; return }\n    else out = out c\n  }\n  got = out\n  got_i = length(line) + 1\n}\nfunction parse(line,   i, n, c) {\n  delete f\n  i = 1\n  n = length(line)\n  while (i \u003C= n) {\n    c = substr(line, i, 1)\n    if (c == \"\\\"\") {\n      take(line, i)\n      key = got\n      i = got_i\n      while (i \u003C= n && substr(line, i, 1) != \"\\\"\") i++\n      if (i > n) return\n      take(line, i)\n      f[key] = got\n      i = got_i\n    } else i++\n  }\n}\nfunction add_label(id, lab,   cur, i, n, p) {\n  if (lab == \"\") return\n  cur = labels[id]\n  n = split(cur, p, \",\")\n  for (i = 1; i \u003C= n; i++) if (p[i] == lab) return\n  if (cur == \"\") labels[id] = lab\n  else labels[id] = cur \",\" lab\n}\nfunction add_edge(id, kind, child,   cur) {\n  if (child == \"\") return\n  cur = edges[id]\n  item = kind \">\" child\n  if (cur == \"\") edges[id] = item\n  else edges[id] = cur \";\" item\n}\nfunction row_encode(s,   i, c, out) {\n  out = \"\"\n  if (s == \"\") return \"\"\n  for (i = 1; i \u003C= length(s); i++) {\n    c = substr(s, i, 1)\n    if (c == \"\\\\\") out = out \"\\\\\\\\\"\n    else if (c == \"\\n\") out = out \"\\\\n\"\n    else if (c == \"\\r\") out = out \"\\\\r\"\n    else if (c == \"\\t\") out = out \"\\\\t\"\n    else if (c == \" \") out = out \"\\\\s\"\n    else if (c == \"\\037\") out = out \"\\\\037\"\n    else out = out c\n  }\n  return out\n}\n{\n  parse($0)\n  id = f[\"id\"]\n  if (id == \"\") next\n  seen[id] = 1\n  if (f[\"op\"] == \"create\") {\n    title[id] = f[\"title\"]\n    ns[id] = f[\"namespace\"]\n    body[id] = f[\"body\"]\n    phase[id] = f[\"phase\"]\n    priority[id] = f[\"priority\"]\n    ref[id] = f[\"ref\"]\n    if (st[id] == \"\") st[id] = \"open\"\n  }\n  if (f[\"namespace\"] != \"\" && ns[id] == \"\") ns[id] = f[\"namespace\"]\n  if (f[\"op\"] == \"status\" || f[\"op\"] == \"close\" || f[\"op\"] == \"create\") {\n    if (f[\"status\"] != \"\") st[id] = f[\"status\"]\n  }\n  add_label(id, f[\"label\"])\n  if (f[\"op\"] == \"block\") add_edge(id, f[\"edge_kind\"], f[\"child\"])\n  if (f[\"op\"] == \"note\") notes[id]++\n}\nEND {\n  for (id in seen) {\n    blocked = 0\n    n = split(edges[id], e, \";\")\n    for (i = 1; i \u003C= n; i++) {\n      split(e[i], p, \">\")\n      if (p[1] != \"blocked_by\") continue\n      child = p[2]\n      cst = st[child]\n      if (cst != \"closed\") blocked = 1\n    }\n    ready = 0\n    if (st[id] != \"closed\" && blocked == 0) ready = 1\n    printf \"%s\\037%s\\037%s\\037%s\\037%s\\037%s\\037%d\\037%d\\037%d\\037%s\\037%s\\037%s\\037%s\\n\", id, row_encode(ns[id]), row_encode(title[id]), st[id], row_encode(labels[id]), row_encode(edges[id]), notes[id]+0, ready, blocked, row_encode(phase[id]), row_encode(priority[id]), row_encode(ref[id]), row_encode(body[id])\n  }\n}\n'\n}\n\nledger_split() {\n  row=\"$1\"\n  old=$IFS\n  set -f\n  IFS=$(printf '\\037')\n  # shellcheck disable=SC2086\n  set -- $row\n  set +f\n  IFS=$old\n  rid=${1:-}\n  row_decode_set \"${2:-}\"; rns=$decoded\n  row_decode_set \"${3:-}\"; rtitle=$decoded\n  rstatus=${4:-}\n  row_decode_set \"${5:-}\"; rlabels=$decoded\n  row_decode_set \"${6:-}\"; redges=$decoded\n  rcomments=${7:-}; rready=${8:-}; rblocked=${9:-}\n  row_decode_set \"${10:-}\"; rphase=$decoded\n  row_decode_set \"${11:-}\"; rpriority=$decoded\n  row_decode_set \"${12:-}\"; rref=$decoded\n  row_decode_set \"${13:-}\"; rbody=$decoded\n}\n\nledger_match() {\n  if [ \"$allns\" != 1 ] && [ -n \"$NS\" ] && [ \"$rns\" != \"$NS\" ]; then\n    return 1\n  fi\n  if [ -n \"$status\" ] && [ \"$rstatus\" != \"$status\" ]; then\n    return 1\n  fi\n  if [ -n \"$label\" ]; then\n    case \",$rlabels,\" in\n      *\",$label,\"*) : ;;\n      *) return 1 ;;\n    esac\n  fi\n  if [ \"${want_blocked:-0}\" = 1 ] && [ \"$rblocked\" != 1 ]; then\n    return 1\n  fi\n  if [ \"${want_ready:-0}\" = 1 ] && [ \"$rready\" != 1 ]; then\n    return 1\n  fi\n  return 0\n}\n\nledger_emit() {\n  want_blocked=${1:-0}\n  want_ready=${2:-0}\n  rows=$(ledger_rows | sort)\n  if [ \"$fmt\" = text ]; then\n    printf '%s\\n' \"$rows\" | while IFS= read -r row; do\n      [ -n \"$row\" ] || continue\n      ledger_split \"$row\"\n      ledger_match || continue\n      printf '%s\\t%s\\t%s\\n' \"$rid\" \"$rtitle\" \"$rstatus\"\n    done\n    return 0\n  fi\n  printf '['\n  first=1\n  printf '%s\\n' \"$rows\" | while IFS= read -r row; do\n    [ -n \"$row\" ] || continue\n    ledger_split \"$row\"\n    ledger_match || continue\n    if [ \"$first\" = 1 ]; then first=0; else printf ','; fi\n    printf '{\"id\":\"%s\",\"title\":\"%s\",\"status\":\"%s\",\"namespace\":\"%s\",\"labels\":\"%s\",\"edges\":\"%s\",\"comments\":%s}' \\\n      \"$(json_escape \"$rid\")\" \"$(json_escape \"$rtitle\")\" \"$(json_escape \"$rstatus\")\" \\\n      \"$(json_escape \"$rns\")\" \"$(json_escape \"$rlabels\")\" \"$(json_escape \"$redges\")\" \\\n      \"$rcomments\"\n  done\n  printf ']\\n'\n}\n\nledger_show() {\n  rows=$(ledger_rows)\n  found=0\n  # notes and history replace the record, matching pasture's separate commands.\n  if [ \"$mode\" = notes ] || [ \"$mode\" = history ]; then\n    hit=$(printf '%s\\n' \"$rows\" | while IFS= read -r row; do\n      [ -n \"$row\" ] || continue\n      ledger_split \"$row\"\n      [ \"$rid\" = \"$id\" ] && printf yes\n    done)\n    [ -n \"$hit\" ] || fail \"no ledger record $id\" \"no line names that id under $LEDGER\" \"create the record first, or pass the id create printed\"\n    safe=$(printf '%s' \"$id\" | sed -e 's\u002F%\u002F%25\u002Fg' -e 's#\u002F#%2F#g' -e 's\u002F \u002F%20\u002Fg' -e 's\u002F:\u002F%3A\u002Fg')\n    file=\"$LEDGER\u002F$safe.jsonl\"\n    if [ \"$mode\" = history ]; then\n      cat \"$file\"\n      return 0\n    fi\n    awk '\nfunction take(line, pos,   i, c, out, esc, hex, v, ok, h, d) {\n  out = \"\"; esc = 0\n  for (i = pos + 1; i \u003C= length(line); i++) {\n    c = substr(line, i, 1)\n    if (esc) {\n      if (c == \"n\") out = out \"\\n\"\n      else if (c == \"t\") out = out \"\\t\"\n      else if (c == \"r\") out = out \"\\r\"\n      else if (c == \"u\" && i + 4 \u003C= length(line)) {\n        hex = substr(line, i + 1, 4)\n        v = 0\n        ok = 1\n        for (h = 1; h \u003C= 4; h++) {\n          d = index(\"0123456789abcdef\", tolower(substr(hex, h, 1)))\n          if (d == 0) ok = 0\n          else v = v * 16 + d - 1\n        }\n        if (ok) { out = out sprintf(\"%c\", v); i += 4 }\n        else out = out c\n      }\n      else out = out c\n      esc = 0\n    } else if (c == \"\\\\\") esc = 1\n    else if (c == \"\\\"\") { got = out; got_i = i + 1; return }\n    else out = out c\n  }\n}\n{\n  i = 1\n  while (i \u003C= length($0)) {\n    if (substr($0, i, 1) == \"\\\"\") {\n      take($0, i); key = got; i = got_i\n      while (i \u003C= length($0) && substr($0, i, 1) != \"\\\"\") i++\n      take($0, i); f[key] = got; i = got_i\n    } else i++\n  }\n  if (f[\"op\"] == \"note\") print f[\"body\"]\n  delete f\n}\n' \"$file\"\n    return 0\n  fi\n  printf '%s\\n' \"$rows\" | while IFS= read -r row; do\n    [ -n \"$row\" ] || continue\n    ledger_split \"$row\"\n    [ \"$rid\" = \"$id\" ] || continue\n    found=1\n    if [ \"$fmt\" = text ]; then\n      printf 'id: %s\\ntitle: %s\\nstatus: %s\\nnamespace: %s\\nlabels: %s\\nedges: %s\\ncomments: %s\\nphase: %s\\npriority: %s\\nref: %s\\ndescription: %s\\n' \\\n        \"$(text_field \"$rid\")\" \"$(text_field \"$rtitle\")\" \"$rstatus\" \"$(text_field \"$rns\")\" \"$(text_field \"$rlabels\")\" \"$(text_field \"$redges\")\" \"$rcomments\" \\\n        \"$(text_field \"$rphase\")\" \"$(text_field \"$rpriority\")\" \"$(text_field \"$rref\")\" \"$(text_field \"$rbody\")\"\n    else\n      printf '{\"id\":\"%s\",\"title\":\"%s\",\"status\":\"%s\",\"namespace\":\"%s\",\"labels\":\"%s\",\"edges\":\"%s\",\"comments\":%s,\"phase\":\"%s\",\"priority\":\"%s\",\"ref\":\"%s\",\"description\":\"%s\"}\\n' \\\n        \"$(json_escape \"$rid\")\" \"$(json_escape \"$rtitle\")\" \"$(json_escape \"$rstatus\")\" \\\n        \"$(json_escape \"$rns\")\" \"$(json_escape \"$rlabels\")\" \"$(json_escape \"$redges\")\" \\\n        \"$rcomments\" \\\n        \"$(json_escape \"$rphase\")\" \"$(json_escape \"$rpriority\")\" \"$(json_escape \"$rref\")\" \"$(json_escape \"$rbody\")\"\n    fi\n  done\n  printf '%s\\n' \"$rows\" | while IFS= read -r row; do\n    [ -n \"$row\" ] || continue\n    ledger_split \"$row\"\n    [ \"$rid\" = \"$id\" ] && found=1\n  done\n  # The pipeline above cannot export found. Re-scan plainly.\n  hit=$(printf '%s\\n' \"$rows\" | while IFS= read -r row; do\n    [ -n \"$row\" ] || continue\n    ledger_split \"$row\"\n    [ \"$rid\" = \"$id\" ] && printf yes\n  done)\n  [ -n \"$hit\" ] || fail \"no ledger record $id\" \"no line names that id under $LEDGER\" \"create the record first, or pass the id create printed\"\n}\n\nledger_query() {\n  if [ -n \"$tree\" ]; then\n    ledger_tree \"$tree\"\n    return 0\n  fi\n  ledger_emit \"$blocked\" 0\n}\n\nledger_ready() {\n  ledger_emit 0 1\n}\n\nledger_tree() {\n  root=\"$1\"\n  rows=$(ledger_rows)\n  printf '%s\\n' \"$rows\" | while IFS= read -r row; do\n    [ -n \"$row\" ] || continue\n    ledger_split \"$row\"\n    [ \"$rid\" = \"$root\" ] || continue\n    printf '%s\\t%s\\n' \"$rid\" \"$redges\"\n  done\n}\ncase \"$OP\" in\n  # dispatch:create\n  create)\n    kind=task\n    title=\n    phase=\n    priority=\n    labels=\n    refs=\n    body=\n    while [ \"$#\" -gt 0 ]; do\n      case \"$1\" in\n        --kind) kind=\"$2\"; shift 2 ;;\n        --kind=*) kind=\"${1#*=}\"; shift ;;\n        --title) title=\"$2\"; shift 2 ;;\n        --title=*) title=\"${1#*=}\"; shift ;;\n        --phase) phase=\"$2\"; shift 2 ;;\n        --phase=*) phase=\"${1#*=}\"; shift ;;\n        --priority) priority=\"$2\"; shift 2 ;;\n        --priority=*) priority=\"${1#*=}\"; shift ;;\n        --label) label_push \"$2\"; shift 2 ;;\n        --label=*) label_push \"${1#*=}\"; shift ;;\n        --ref)\n          if [ -z \"$refs\" ]; then refs=\"$2\"; else refs=\"$refs\n$2\"; fi\n          shift 2 ;;\n        --ref=*)\n          _val=\"${1#*=}\"\n          if [ -z \"$refs\" ]; then refs=\"$_val\"; else refs=\"$refs\n$_val\"; fi\n          shift ;;\n        --body) body=\"$2\"; shift 2 ;;\n        --body=*) body=\"${1#*=}\"; shift ;;\n        *) fail \"create rejected argument '$1'\" \"the argument is not in the create contract\" \"use --kind, --title, --phase, --priority, --label, --ref, --body\" ;;\n      esac\n    done\n    [ \"$body\" = \"@-\" ] && body=\"$(cat)\"\n    fold_refs\n    if [ \"$BACKEND\" = ledger ]; then\n      ledger_create\n      exit $?\n    fi\n      set -- task create \"$title\" --type \"$kind\" --phase \"$phase\"\n      [ -n \"$priority\" ] && set -- \"$@\" --priority \"$priority\"\n      [ -n \"$body\" ] && set -- \"$@\" --description \"$body\"\n      if [ \"$allns\" != 1 ] && [ -n \"$NS\" ]; then set -- \"$@\" --namespace \"$NS\"; fi\n      out=\"$(pb \"$@\" --format text)\" || exit $?\n      id=\"$(printf '%s\\n' \"$out\" | sed -n 's\u002F^ID: *\u002F\u002Fp' | head -1)\"\n      [ -n \"$id\" ] || fail \"create returned no task ID\" \"the backend text output had no ID line\" \"check the store named in $EPOCH_FILE and the backend help page\"\n      printf '%s\\n' \"$id\"\n      set -f\n      for _enc in $labels; do\n        [ -n \"$_enc\" ] || continue\n        row_decode_set \"$_enc\"\n        label=$decoded\n        set -- task label add \"$id\" \"$label\"\n        pb \"$@\" >\u002Fdev\u002Fnull || exit $?\n      done\n      set +f\n    ;;\n  # dispatch:label\n  label)\n    id=\"${1:-}\"\n    [ \"$#\" -gt 0 ] && shift\n    need_id \"$id\"\n    labels=\n    while [ \"$#\" -gt 0 ]; do\n      case \"$1\" in\n        --*) fail \"label rejected argument '$1'\" \"the argument is not in the label contract\" \"pass the label as a positional after the task URI\" ;;\n        *) label_push \"$1\"; shift ;;\n      esac\n    done\n    [ -n \"$labels\" ] || fail \"label needs at least one label\" \"no label operand was given\" \"pass the label after the task URI\"\n    if [ \"$BACKEND\" = ledger ]; then\n      ledger_label\n      exit $?\n    fi\n      set -f\n      for _enc in $labels; do\n        [ -n \"$_enc\" ] || continue\n        row_decode_set \"$_enc\"\n        label=$decoded\n        set -- task label add \"$id\" \"$label\"\n        pb \"$@\" || exit $?\n      done\n      set +f\n    ;;\n  # dispatch:block\n  block)\n    child=\n    edge_kind=blocked_by\n    parent=\"${1:-}\"\n    [ \"$#\" -gt 0 ] && shift\n    need_id \"$parent\"\n    while [ \"$#\" -gt 0 ]; do\n      case \"$1\" in\n        --by) child=\"$2\"; shift 2 ;;\n        --by=*) child=\"${1#*=}\"; shift ;;\n        --kind) edge_kind=\"$2\"; shift 2 ;;\n        --kind=*) edge_kind=\"${1#*=}\"; shift ;;\n        *) fail \"block rejected argument '$1'\" \"the argument is not in the block contract\" \"use --by, --kind\" ;;\n      esac\n    done\n    [ -n \"$child\" ] || fail \"block needs --by CHILD\" \"the child operand was empty\" \"pass block PARENT --by CHILD\"\n    if [ \"$BACKEND\" = ledger ]; then\n      ledger_block\n      exit $?\n    fi\n      if [ \"$edge_kind\" = blocked_by ]; then\n        set -- task dep add \"$parent\" --blocked-by \"$child\"\n        pb \"$@\" || exit $?\n      else\n        set -- task dep add \"$parent\" --target \"$child\" --kind \"$edge_kind\"\n        pb \"$@\" || exit $?\n      fi\n    ;;\n  # dispatch:note\n  note)\n    note_body=\n    note_kind=\n    note_to=\n    fmt=json\n    id=\"${1:-}\"\n    [ \"$#\" -gt 0 ] && shift\n    need_id \"$id\"\n    while [ \"$#\" -gt 0 ]; do\n      case \"$1\" in\n        --author) AUTHOR=\"$2\"; shift 2 ;;\n        --author=*) AUTHOR=\"${1#*=}\"; shift ;;\n        --body) note_body=\"$2\"; shift 2 ;;\n        --body=*) note_body=\"${1#*=}\"; shift ;;\n        --kind) note_kind=\"$2\"; shift 2 ;;\n        --kind=*) note_kind=\"${1#*=}\"; shift ;;\n        --to) note_to=\"$2\"; shift 2 ;;\n        --to=*) note_to=\"${1#*=}\"; shift ;;\n        --format) fmt=\"$2\"; shift 2 ;;\n        --format=*) fmt=\"${1#*=}\"; shift ;;\n        *) fail \"note rejected argument '$1'\" \"the argument is not in the note contract\" \"use --author, --body, --kind, --to, --format\" ;;\n      esac\n    done\n    [ \"$note_body\" = \"@-\" ] && note_body=\"$(cat)\"\n    [ -n \"$AUTHOR\" ] || fail \"note needs --author\" \"the tracker never invents an author\" \"pass the registered actor id\"\n    [ -n \"$note_kind\" ] && note_body=\"[$note_kind] $note_body\"\n    [ -n \"$note_to\" ] && note_body=\"to:$note_to $note_body\"\n    if [ \"$BACKEND\" = ledger ]; then\n      ledger_note\n      exit $?\n    fi\n      set -- task comment add \"$id\" --author \"$AUTHOR\" -- \"$note_body\"\n      pb \"$@\" || exit $?\n    ;;\n  # dispatch:show\n  show)\n    mode=show\n    mode=show\n    fmt=json\n    id=\"${1:-}\"\n    [ \"$#\" -gt 0 ] && shift\n    need_id \"$id\"\n    while [ \"$#\" -gt 0 ]; do\n      case \"$1\" in\n        --notes) mode=notes; shift ;;\n        --history) mode=history; shift ;;\n        --format) fmt=\"$2\"; shift 2 ;;\n        --format=*) fmt=\"${1#*=}\"; shift ;;\n        *) fail \"show rejected argument '$1'\" \"the argument is not in the show contract\" \"use --notes, --history, --format\" ;;\n      esac\n    done\n    if [ \"$BACKEND\" = ledger ]; then\n      ledger_show\n      exit $?\n    fi\n      if [ \"$mode\" = notes ]; then\n        set -- task comments \"$id\" --format \"$fmt\"\n        pb \"$@\" || exit $?\n      elif [ \"$mode\" = history ]; then\n        set -- task timeline \"$id\"\n        pb \"$@\" || exit $?\n      else\n        set -- task show \"$id\" --format \"$fmt\"\n        pb \"$@\" || exit $?\n      fi\n    ;;\n  # dispatch:query\n  query)\n    status=\n    label=\n    tree=\n    fmt=json\n    blocked=0\n    allns=0\n    while [ \"$#\" -gt 0 ]; do\n      case \"$1\" in\n        --status) status=\"$2\"; shift 2 ;;\n        --status=*) status=\"${1#*=}\"; shift ;;\n        --label) label=\"$2\"; shift 2 ;;\n        --label=*) label=\"${1#*=}\"; shift ;;\n        --tree) tree=\"$2\"; shift 2 ;;\n        --tree=*) tree=\"${1#*=}\"; shift ;;\n        --format) fmt=\"$2\"; shift 2 ;;\n        --format=*) fmt=\"${1#*=}\"; shift ;;\n        --blocked) blocked=1; shift ;;\n        --all-namespaces) allns=1; shift ;;\n        *) fail \"query rejected argument '$1'\" \"the argument is not in the query contract\" \"use --status, --label, --tree, --format, --blocked, --all-namespaces\" ;;\n      esac\n    done\n    [ \"$blocked\" != 1 ] || [ -z \"$status\" ] || fail \"query rejected --blocked with --status\" \"the blocked read has no --status flag, so forwarding it exits 1\" \"drop --status, or run query --status without --blocked\"\n    if [ \"$BACKEND\" = ledger ]; then\n      ledger_query\n      exit $?\n    fi\n      if [ -n \"$tree\" ]; then\n        set -- task dep tree \"$tree\"\n        pb \"$@\" || exit $?\n        exit $?\n      elif [ \"$blocked\" = 1 ]; then\n        set -- task blocked\n        [ -n \"$label\" ] && set -- \"$@\" --label \"$label\"\n        if [ \"$allns\" != 1 ] && [ -n \"$NS\" ]; then set -- \"$@\" --namespace \"$NS\"; fi\n        [ \"$allns\" = 1 ] && set -- \"$@\" --all-namespaces\n        set -- \"$@\" --format \"$fmt\"\n        pb \"$@\" || exit $?\n      else\n        set -- task list\n        [ -n \"$status\" ] && set -- \"$@\" --status \"$status\"\n        [ -n \"$label\" ] && set -- \"$@\" --label \"$label\"\n        if [ \"$allns\" != 1 ] && [ -n \"$NS\" ]; then set -- \"$@\" --namespace \"$NS\"; fi\n        [ \"$allns\" = 1 ] && set -- \"$@\" --all-namespaces\n        set -- \"$@\" --format \"$fmt\"\n        pb \"$@\" || exit $?\n      fi\n    ;;\n  # dispatch:ready\n  ready)\n    label=\n    fmt=json\n    allns=0\n    while [ \"$#\" -gt 0 ]; do\n      case \"$1\" in\n        --label) label=\"$2\"; shift 2 ;;\n        --label=*) label=\"${1#*=}\"; shift ;;\n        --format) fmt=\"$2\"; shift 2 ;;\n        --format=*) fmt=\"${1#*=}\"; shift ;;\n        --all-namespaces) allns=1; shift ;;\n        *) fail \"ready rejected argument '$1'\" \"the argument is not in the ready contract\" \"use --label, --format, --all-namespaces\" ;;\n      esac\n    done\n    if [ \"$BACKEND\" = ledger ]; then\n      ledger_ready\n      exit $?\n    fi\n      set -- task ready\n      [ -n \"$label\" ] && set -- \"$@\" --label \"$label\"\n      if [ \"$allns\" != 1 ] && [ -n \"$NS\" ]; then set -- \"$@\" --namespace \"$NS\"; fi\n      [ \"$allns\" = 1 ] && set -- \"$@\" --all-namespaces\n      set -- \"$@\" --format \"$fmt\"\n      pb \"$@\" || exit $?\n    ;;\n  # dispatch:status\n  status)\n    status=\n    id=\"${1:-}\"\n    [ \"$#\" -gt 0 ] && shift\n    need_id \"$id\"\n    while [ \"$#\" -gt 0 ]; do\n      case \"$1\" in\n        --set) status=\"$2\"; shift 2 ;;\n        --set=*) status=\"${1#*=}\"; shift ;;\n        *) fail \"status rejected argument '$1'\" \"the argument is not in the status contract\" \"use --set\" ;;\n      esac\n    done\n    [ -n \"$status\" ] || fail \"status needs --set\" \"no target status was given\" \"pass --set open, in_progress or closed\"\n    if [ \"$BACKEND\" = ledger ]; then\n      ledger_status\n      exit $?\n    fi\n      set -- task update \"$id\" --status \"$status\"\n      pb \"$@\" || exit $?\n    ;;\n  # dispatch:close\n  close)\n    reason=\n    id=\"${1:-}\"\n    [ \"$#\" -gt 0 ] && shift\n    need_id \"$id\"\n    while [ \"$#\" -gt 0 ]; do\n      case \"$1\" in\n        --reason) reason=\"$2\"; shift 2 ;;\n        --reason=*) reason=\"${1#*=}\"; shift ;;\n        *) fail \"close rejected argument '$1'\" \"the argument is not in the close contract\" \"use --reason\" ;;\n      esac\n    done\n    if [ \"$BACKEND\" = ledger ]; then\n      ledger_close\n      exit $?\n    fi\n      set -- task close \"$id\"\n      [ -n \"$reason\" ] && set -- \"$@\" --reason \"$reason\"\n      pb \"$@\" || exit $?\n    ;;\n  # dispatch:whoami\n  whoami)\n    [ -n \"$AUTHOR\" ] || fail \"whoami has no recorded author\" \\\n      \"neither the header author: line nor PASTURE_AUTHOR names an actor\" \\\n      \"set author: in $EPOCH_FILE or export PASTURE_AUTHOR to a registered actor id\"\n    if [ \"$BACKEND\" = ledger ]; then\n      ledger_whoami\n      exit $?\n    fi\n      set -- task agents show \"$AUTHOR\"\n      pb \"$@\" >\u002Fdev\u002Fnull || exit $?\n      printf '%s\\n' \"$AUTHOR\"\n    ;;\n  # dispatch:signal\n  signal)\n    kind=\n    phase=\n    skill=\n    entry=\n    quote=\n    while [ \"$#\" -gt 0 ]; do\n      case \"$1\" in\n        --kind) kind=\"$2\"; shift 2 ;;\n        --kind=*) kind=\"${1#*=}\"; shift ;;\n        --phase) phase=\"$2\"; shift 2 ;;\n        --phase=*) phase=\"${1#*=}\"; shift ;;\n        --skill) skill=\"$2\"; shift 2 ;;\n        --skill=*) skill=\"${1#*=}\"; shift ;;\n        --entry) entry=\"$2\"; shift 2 ;;\n        --entry=*) entry=\"${1#*=}\"; shift ;;\n        --quote) quote=\"$2\"; shift 2 ;;\n        --quote=*) quote=\"${1#*=}\"; shift ;;\n        *) fail \"signal rejected argument '$1'\" \"the argument is not in the signal contract\" \"use --kind, --phase, --skill, --entry, --quote\" ;;\n      esac\n    done\n    [ -n \"$kind\" ] || fail \"signal needs --kind\" \"the kind operand was empty\" \"pass --kind with one registered signal kind\"\n    [ -n \"$phase\" ] || fail \"signal needs --phase\" \"the phase operand was empty\" \"pass --phase with one registered phase name\"\n    [ -n \"$skill\" ] || fail \"signal needs --skill\" \"the skill operand was empty\" \"pass --skill with the skill directory name\"\n    [ -n \"$entry\" ] || fail \"signal needs --entry\" \"the entry operand was empty\" \"pass --entry with the record that holds the interaction\"\n    [ -n \"$quote\" ] || fail \"signal needs --quote\" \"the quote operand was empty\" \"pass --quote with a short verbatim fragment\"\n    case \"$kind\" in\n      frustration|positive-signal|satisfaction|confusion|correction) ;;\n      *) fail \"signal rejected kind '$kind'\" \"the kind is not in the closed set, so a later count would treat an unregistered label as data\" \"pass --kind with one of: frustration, positive-signal, satisfaction, confusion, correction\" \"signal operation in the tracker adapter, before the backend call\" ;;\n    esac\n    case \"$phase\" in\n      request|elicit|propose|review|plan-review|ratify|handoff|impl-plan|worker-slices|code-review|impl-uat|landing) ;;\n      *) fail \"signal rejected phase '$phase'\" \"the phase is not in the registered vocabulary, so the record would not join the phase it names\" \"pass --phase with one of: request, elicit, propose, review, plan-review, ratify, handoff, impl-plan, worker-slices, code-review, impl-uat, landing\" \"signal operation in the tracker adapter, before the backend call\" ;;\n    esac\n    signal_line=$(signal_json)\n    if [ \"$BACKEND\" != ledger ]; then\n      FEEDBACK_ID=$(field feedback)\n      [ -n \"$FEEDBACK_ID\" ] || fail \"signal has no feedback task\" \"EPOCH.md has no feedback: line, so a note would run with an empty id and record nothing\" \"the lookup rule is in Signal Capture; do not create a task from the signal operation\"\n      [ -n \"$AUTHOR\" ] || fail \"signal needs a recorded author\" \"the header author: line and PASTURE_AUTHOR are both empty\" \"set author: in $EPOCH_FILE or export PASTURE_AUTHOR to a registered actor id\"\n    fi\n    if [ \"$BACKEND\" = ledger ]; then\n      ledger_signal\n      exit $?\n    fi\n      set -- task comment add \"$FEEDBACK_ID\" --author \"$AUTHOR\" -- \"$signal_line\"\n      pb \"$@\" || exit $?\n    ;;\n  *)\n    fail \"unknown operation '$OP'\" \"the operation set is closed\" \\\n      \"use one of create, label, block, note, show, query, ready, status, close, whoami, signal; run the probe if the header is missing\"\n    ;;\nesac\n~~~\n\u003C!-- \u002Fpasture:asset rec -->\n\n\n## Signal Capture\n\nRecord the user's friction or approval when it appears, so a later project-level skill can distill it. Capture is a side effect of work already happening: it never prompts the user and never blocks a phase.\n\n### Closed kind list\n\nThe kind is a closed list. Use exactly one of:\n\n- `frustration`: the user shows annoyance, repeats a complaint, or rejects the interaction itself.\n- `positive-signal`: the user endorses a direction or a result before a gate asks.\n- `satisfaction`: the user accepts a gate and says the result is what they wanted.\n- `confusion`: the user asks what a term means, misreads a question, or answers a different question.\n- `correction`: the user corrects a fact, a requirement, or an earlier answer.\n\n### One feedback task per epoch\n\nOn the pasture backend, the epoch creates one feedback task at epoch start and writes `feedback: \u003Ctask URI>` into `.agents.local\u002Fpasture\u002FEPOCH.md`. A second run reads the `feedback:` line and does not create another task. On the ledger backend, create nothing and record nothing: there is no feedback task and no `feedback:` line. No other skill creates a feedback task. If the pasture line is absent, the signal operation fails and records nothing; do not create a task to fill the gap.\n\n### Line format and recording\n\nWrite one JSON object per signal. The fields, in this order, are kind, phase, skill, entry and quote.\n\n~~~\n{\"kind\":\"\u003Ckind>\",\"phase\":\"\u003Cphase>\",\"skill\":\"\u003Cskill>\",\"entry\":\"\u003Centry-ref>\",\"quote\":\"\u003Cshort verbatim quote>\"}\n~~~\n\n- `kind`: one of the registered kinds above, exactly.\n- `phase`: one of request, elicit, propose, review, plan-review, ratify, handoff, impl-plan, worker-slices, code-review, impl-uat, landing.\n- `skill`: the skill that recorded it, named exactly as the skill directory.\n- `entry`: the record that holds the interaction, such as a task URI plus the question or section.\n- `quote`: a short verbatim fragment, max 20 words. Never paraphrase or tidy it.\n\nRecord the signal with one call. Do not name a backend command in this section.\n\n~~~bash\n\"${EPOCH_REC}\" signal --kind confusion --phase elicit --skill user-elicit --entry '\u003Ctask URI>#end-vision' --quote 'what does arm mean here'\n~~~\n\nThe adapter records that JSON object. An empty feedback id fails in the adapter and records nothing. Write the call in the same turn as the work. Do not ask permission to record. Do not wait for the write before you continue the phase.\n\nIf the write fails, follow the tracker write-failure rule: retry, keep the recorded backend, and surface the pending line at the next gate. A failed record never blocks the phase and never prompts the user.\n\n## Core Principles\n\n1. **AUDIT TRAIL PRESERVATION** — Never delete or destroy information, labels, or tasks\n2. **DEPENDENCY CHAINING** — Each task blocks its predecessor: `\"${EPOCH_REC}\" block \"${PARENT_URI}\" --by \"${CHILD_URI}\"`\n3. **USER ENGAGEMENT** — URE and UAT at multiple checkpoints\n4. **CONSENSUS REQUIRED** — All 3 reviewers must ACCEPT before proceeding\n5. **EAGER SEVERITY TREE** — Code reviews (Phase 10) always create 3 severity groups (BLOCKER, IMPORTANT, MINOR); empty groups closed immediately. Only an open BLOCKER gates a review wave; open findings converge at the end\n6. **FOLLOW-UP EPIC** — Fed ONLY by user-DEFER'd UAT items (Phase 5\u002F11), never by any review severity; the Supervisor creates it from those DEFER'd items\n7. **RIDE THE WAVE** — Phases 8-10 form one continuous cycle: Explore subagents (P8), workers implement (P9), ephemeral reviewers review (P10), iterating review→fix→re-review up to the chosen review-effort budget; only an open BLOCKER gates a wave, and after the last wave convergence queries every open pasture:finding toward 0\u002F0\u002F0 within maxRounds; on exhaustion, surface outstanding findings to the user at a gate; workers persist throughout\n\n## The 12-Phase Workflow\n\n```\nPhase 1:  pasture:p1-user       -> REQUEST (classify, research, explore)\n            s1_1-classify -> s1_2-research || s1_3-explore\nPhase 2:  pasture:p2-user       -> ELICIT (URE survey) + URD (single source of truth)\n            s2_1-elicit -> s2_2-urd\nPhase 3:  pasture:p3-plan       -> PROPOSAL-N (architect proposes)\nPhase 4:  pasture:p4-plan       -> REVIEW (3 parallel reviewers, ACCEPT\u002FREVISE)\nPhase 5:  pasture:p5-user       -> Plan UAT (user acceptance test)\nPhase 6:  pasture:p6-plan       -> Ratification (supersede old proposals)\nPhase 7:  pasture:p7-plan       -> Handoff (architect -> supervisor)\nPhase 8:  implementation plan -> IMPL_PLAN (supervisor decomposes into slices; Explore subagents)\nPhase 9:  worker slices       -> one slice per worker (parallel workers; Ride the Wave — workers persist for review)\nPhase 10: code review         -> reviewers review every slice in the wave; only an open BLOCKER gates a wave, then convergence at the end, else surface to the user\nPhase 11: pasture:p11-user    -> Implementation UAT\nPhase 12: landing             -> commit, push, hand off\n```\n\n### Phase 1 Expanded: REQUEST\n\nPhase 1 has 3 sub-steps:\n\n| Sub-step | Label | Description | Parallel? |\n|----------|-------|-------------|-----------|\n| s1_1-classify | `pasture:p1-user:s1_1-classify` | Capture and classify request along 4 axes (scope, complexity, risk, domain novelty) | Sequential (first) |\n| s1_2-research | `pasture:p1-user:s1_2-research` | Find domain standards, prior art | Parallel with s1_3 |\n| s1_3-explore | `pasture:p1-user:s1_3-explore` | Codebase exploration for integration points | Parallel with s1_2 |\n\nAfter classification, user confirms research depth. Then s1_2 and s1_3 run in parallel.\n\n## Starting an Epoch\n\n**Step 0: detect the tracker backend before the first write.**\n\nWrite the probe and the adapter by running the installer below against this skill file. It writes `.agents.local\u002Fpasture\u002Fprobe.sh` and `.agents.local\u002Fpasture\u002Frec` from the shipped fences in the Tracker Adapter section. Set `SKILL_FILE` to this file, run the installer, then the probe. Write `EPOCH.md` from the probe output before any task is created. The orchestrator runs this probe once; subagents read the header and never re-probe. A missing git origin is not an error backend. For pasture, the probe does not derive a namespace: record `namespace:` only to pin one, otherwise the adapter omits `--namespace` and Pasture derives it. For ledger, the probe does derive a namespace: it prints NS from derive_namespace, and every unpinned ledger write calls that same function. Copy NS to `namespace:` only to override it. A missing or unregistered author is a warning (`WARN=no-author`), not an error backend.\n\n```bash\n# pasture:install-tracker\ninstall_tracker_assets() {\n  skill=$1\n  dest=${2:-.agents.local\u002Fpasture}\n  if [ -z \"$skill\" ] || [ ! -f \"$skill\" ]; then\n    printf 'install: no skill file\\n  why: install_tracker_assets needs the rendered skill path\\n  where: Starting an Epoch install step\\n  fix: set SKILL_FILE to this skill and pass it as the first argument\\n' >&2\n    return 1\n  fi\n  mkdir -p \"$dest\" || return 1\n  awk -v dest=\"$dest\" '\n    $0 == \"\u003C!-- pasture:asset probe -->\" { kind = \"probe\"; next }\n    $0 == \"\u003C!-- pasture:asset rec -->\" { kind = \"rec\"; next }\n    $0 ~ \u002F^\u003C!-- \\\u002Fpasture:asset \u002F { kind = \"\"; grab = 0; next }\n    kind != \"\" && $0 == \"~~~sh\" { grab = 1; next }\n    kind != \"\" && grab == 1 && $0 == \"~~~\" { grab = 0; next }\n    grab == 1 && kind == \"probe\" { print > (dest \"\u002Fprobe.sh\"); next }\n    grab == 1 && kind == \"rec\" { print > (dest \"\u002Frec\"); next }\n  ' \"$skill\"\n  if [ ! -s \"$dest\u002Fprobe.sh\" ] || [ ! -s \"$dest\u002Frec\" ]; then\n    printf 'install: the skill did not emit probe.sh and rec\\n  why: the Tracker Adapter fences were missing or empty\\n  where: %s\\n  fix: regenerate the skill so it contains pasture:asset probe and pasture:asset rec\\n' \"$skill\" >&2\n    return 1\n  fi\n  chmod +x \"$dest\u002Fprobe.sh\" \"$dest\u002Frec\"\n}\n# \u002Fpasture:install-tracker\ninstall_tracker_assets \"$SKILL_FILE\" .agents.local\u002Fpasture\nsh .agents.local\u002Fpasture\u002Fprobe.sh\n# Then write .agents.local\u002Fpasture\u002FEPOCH.md from the keys named below. Do not\n# copy a derived NS into namespace: unless you mean to pin it. In every shell\n# session resolve the entry point:\nEPOCH_REC=$(sed -n 's\u002F^rec: \u002F\u002Fp' .agents.local\u002Fpasture\u002FEPOCH.md | tail -1)\n```\n\nIf the probe prints `BACKEND=error`, stop and report before the first mutation: the store exists but cannot be used. If it prints `BACKEND=ledger`, record `backend:`, `ledger:`, `rec:` and `probed_at:` from the output and continue; the adapter writes one JSONL line per mutation under that ledger and derives the namespace with the same function that printed NS. Offer a gitignore entry; never require one. If it prints `BACKEND=pasture`, record `backend:`, `db:`, `author:`, `rec:` and `probed_at:` from the output. Record `namespace:` only to pin one; the adapter then passes `--namespace`, and that pin wins over Pasture's derivation.\n\n**Step 0b: create the epoch feedback task once, and only on the pasture backend.** This is the only create. On the pasture backend, create the task once and record `feedback:`. On the ledger backend, create nothing and record nothing. The lookup rule, including who may create the task, is in Signal Capture.\n\n```bash\nBACKEND=$(sed -n 's\u002F^backend: \u002F\u002Fp' .agents.local\u002Fpasture\u002FEPOCH.md | tail -1)\nif [ \"$BACKEND\" = pasture ]; then\n  FEEDBACK_ID_URI=$(sed -n 's\u002F^feedback: \u002F\u002Fp' .agents.local\u002Fpasture\u002FEPOCH.md | tail -1)\n  if [ -z \"$FEEDBACK_ID_URI\" ]; then\n    FEEDBACK_ID_URI=$(\"${EPOCH_REC}\" create --kind task --title \"FEEDBACK: epoch signals\" --phase unscoped)\n    printf 'feedback: %s\\n' \"$FEEDBACK_ID_URI\" >> .agents.local\u002Fpasture\u002FEPOCH.md\n  fi\nfi\n```\n\n**Option 1: Manual Task Creation**\n```bash\n# Phase 1: Capture user request\nREQUEST_ID_URI=$(\"${EPOCH_REC}\" create --kind task --title \"REQUEST: {{feature}}\" --phase request \\\n  --body \"{{verbatim user request}}\")\n\"${EPOCH_REC}\" label \"$REQUEST_ID_URI\" pasture:p1-user:s1_1-classify\n\"${EPOCH_REC}\" note \"$REQUEST_ID_URI\" --author pasture-system\u002F00000000-0000-0000-0000-000000000000 --body \"Requested role (not an assignment): architect\"\n\n# Then proceed through phases manually\n```\n\nUse the explicit task graph; no molecule subsystem is required.\n\n## Phase Transitions\n\nEach phase creates a task and chains dependencies. Cross-references use description frontmatter instead of peer-reference commands.\n\n```bash\n# After Phase 1 creates \"${REQUEST_ID_URI}\"\n\"${EPOCH_REC}\" block \"${REQUEST_ID_URI}\" --by \"${ELICIT_ID_URI}\"    # REQUEST blocked by ELICIT\n\n# After Phase 2 creates \"${ELICIT_ID_URI}\" and URD\n\"${EPOCH_REC}\" block \"${ELICIT_ID_URI}\" --by \"${PROPOSAL_ID_URI}\"   # ELICIT blocked by PROPOSAL\n# URD linked via frontmatter in its description:\n#   references:\n#     request: \"${REQUEST_ID_URI}\"\n#     elicit: \"${ELICIT_ID_URI}\"\n\n# After Phase 5 (UAT) and Phase 6 (ratify), update URD\n\"${EPOCH_REC}\" note \"${URD_ID_URI}\" --author pasture-system\u002F00000000-0000-0000-0000-000000000000 --body \"UAT results: {{summary}}\"\n\"${EPOCH_REC}\" note \"${URD_ID_URI}\" --author pasture-system\u002F00000000-0000-0000-0000-000000000000 --body \"Ratified: scope confirmed as {{summary}}\"\n```\n\n## Follow-up Epic\n\n**Trigger:** UAT (Phase 5 or 11) produces one or more **user-DEFER'd items**.\nThe FOLLOWUP epic is fed ONLY by those DEFER'd UAT items — **never** by any review severity (review findings converge at the end; close rule: C-finding-lifecycle).\n**Owner:** Supervisor creates the follow-up epic.\n\n```bash\nFOLLOWUP_EPIC_ID_URI=$(\"${EPOCH_REC}\" create --kind epic --title \"FOLLOWUP: User-deferred improvements from UAT\" --phase unscoped --priority 3 \\\n  --body \"---\nreferences:\n  request: \"${REQUEST_ID_URI}\"\n  uat: \"${UAT_ID_URI}\"\n---\nAggregated user-DEFER'd items from UAT (Phase 5\u002F11).\")\n\"${EPOCH_REC}\" label \"$FOLLOWUP_EPIC_ID_URI\" pasture:epic-followup\n```\n\n### Follow-up lifecycle (same protocol, FOLLOWUP_* prefix)\n\nThe follow-up epic runs the same protocol phases with FOLLOWUP_* prefixed task types:\n\n```\nFOLLOWUP → FOLLOWUP_URE → FOLLOWUP_URD → FOLLOWUP_PROPOSAL-1 → FOLLOWUP_IMPL_PLAN → the follow-up slice\n```\n\n- **FOLLOWUP_URE**: Scoping URE with user to determine which DEFER'd items to address\n- **FOLLOWUP_URD**: Requirements doc for follow-up scope (references original URD)\n- **FOLLOWUP_PROPOSAL-{N}**: Proposal accounting for original URD + FOLLOWUP_URD + the DEFER'd items\n- **FOLLOWUP_IMPL_PLAN**: Supervisor decomposes follow-up into slices\n- **the follow-up slice**: Each slice implements the DEFER'd-item work decomposed into leaf tasks\n\nSee `\u002Fpasture:supervisor` and `\u002Fpasture:impl-review` for full creation commands.\n\n## EAGER Severity Tree (Phase 10)\n\nCode reviews ALWAYS create ONE severity trio (3 group tasks) per wave, per axis, per round, even if empty:\n\n```bash\n# Create all 3 severity groups immediately (EAGER, not lazy)\nBLOCKER_ID_URI=$(\"${EPOCH_REC}\" create --kind task --title \"BLOCKER\" --phase code_review \\\n   --body \"Severity group for this review round; close only if empty\")\n\"${EPOCH_REC}\" label \"$BLOCKER_ID_URI\" pasture:severity:blocker\nIMPORTANT_ID_URI=$(\"${EPOCH_REC}\" create --kind task --title \"IMPORTANT\" --phase code_review \\\n   --body \"Severity group for this review round; close only if empty\")\n\"${EPOCH_REC}\" label \"$IMPORTANT_ID_URI\" pasture:severity:important\nMINOR_ID_URI=$(\"${EPOCH_REC}\" create --kind task --title \"MINOR\" --phase code_review \\\n   --body \"Severity group for this review round; close only if empty\")\n\"${EPOCH_REC}\" label \"$MINOR_ID_URI\" pasture:severity:minor\n\n# Empty groups are closed immediately\n\"${EPOCH_REC}\" close \"${IMPORTANT_ID_URI}\"\n\"${EPOCH_REC}\" close \"${MINOR_ID_URI}\"\n```\n\n**Dual-parent BLOCKER:** Bind BLOCKER_FINDING_ID_URI from the finding task's create output before running this fence. BLOCKER findings block both the severity group AND the slice:\n```bash\n\"${EPOCH_REC}\" block \"${BLOCKER_ID_URI}\" --by \"${BLOCKER_FINDING_ID_URI}\"\n\"${EPOCH_REC}\" block \"${SLICE_ID_URI}\" --by \"${BLOCKER_FINDING_ID_URI}\"\n```\n\nSee [reviewer-review-code](..\u002Freviewer-review-code\u002FSKILL.md) for severity definitions.\n\n## Tracking Progress\n\n```bash\n# View dependency chain\n\"${EPOCH_REC}\" query --tree \"${LATEST_TASK_ID_URI}\"\n\n# Check blocked work\n\"${EPOCH_REC}\" query --blocked\n\n# See all epoch tasks by phase\n\"${EPOCH_REC}\" query --label=\"pasture:p1-user:s1_1-classify\"    # REQUEST tasks\n\"${EPOCH_REC}\" query --label=\"pasture:p2-user:s2_1-elicit\"      # ELICIT tasks\n\"${EPOCH_REC}\" query --label=\"pasture:p3-plan:s3-propose\"        # PROPOSAL tasks\n\"${EPOCH_REC}\" query --phase worker_slices          # Implementation slices\n```\n\n## Skills to Invoke\n\nEach phase transition MUST include an explicit `Skill(...)` invocation directive. When launching agents for a phase, the prompt MUST tell the agent to call the corresponding skill as its first action.\n\n| Phase | Skill | Invocation Directive |\n|-------|-------|---------------------|\n| 1 (REQUEST: classify, research, explore) | `\u002Fpasture:user-request` | `Skill(\u002Fpasture:user-request)` |\n| 2 (ELICIT + URD) | `\u002Fpasture:user-elicit` | `Skill(\u002Fpasture:user-elicit)` |\n| 3-6 (PROPOSAL, REVIEW, UAT, RATIFY) | `\u002Fpasture:architect` | `Skill(\u002Fpasture:architect)` |\n| 5, 11 (UAT) | `\u002Fpasture:user-uat` | `Skill(\u002Fpasture:user-uat)` |\n| 7 (HANDOFF) | `\u002Fpasture:architect-handoff` | Architect calls `Skill(\u002Fpasture:architect-handoff)` after ratification |\n| 8-10 (IMPL_PLAN, SLICES, CODE REVIEW) | `\u002Fpasture:supervisor` | Supervisor prompt MUST start with `Skill(\u002Fpasture:supervisor)` |\n| 12 (LANDING) | Manual git commit and push | N\u002FA |\n\n**CRITICAL — interviewing phases:** The interviewing phases MUST explicitly invoke their skill. Do **not** improvise interview questions:\n- **Phase 2 (URE):** invoke `Skill(\u002Fpasture:user-elicit)` — skipping it produces low-quality elicitation.\n- **Phases 5 & 11 (UAT):** invoke `Skill(\u002Fpasture:user-uat)` — it drives the FIX-NOW, DEFER, or WON'T-DO disposition and demonstrative examples.\n\n**CRITICAL:** When the architect hands off to the supervisor (Phase 7 → 8), the supervisor launch prompt MUST:\n1. Start with `Skill(\u002Fpasture:supervisor)` — without this, the supervisor skips role-critical procedures\n2. Include all Pasture task IDs (REQUEST, URD, RATIFIED PROPOSAL, HANDOFF)\n3. Include the HANDOFF Pasture task ID — the handoff is authored in that task body (no filesystem path)\n\n## Never Delete Policy\n\n**DO:** Add labels, add comments, update status\n**DON'T:** Close tasks prematurely, delete tasks, remove labels\n\n```bash\n# Correct: Add ratify label\n\"${EPOCH_REC}\" label \"${PROPOSAL_ID_URI}\" pasture:p6-plan:s6-ratify\n\"${EPOCH_REC}\" note \"${PROPOSAL_ID_URI}\" --author pasture-system\u002F00000000-0000-0000-0000-000000000000 --body \"RATIFIED: All reviewers ACCEPT\"\n\n# Wrong: Don't close\n# \"${EPOCH_REC}\" close \"${PROPOSAL_ID_URI}\"  # NEVER DO THIS\n```\n\u003C!-- END GENERATED FROM pasture schema -->\n","# Epoch Agent\n\n\u003C!-- BEGIN GENERATED FROM pasture schema -->\n\u003C!-- GENERATED FROM pasture v0.0.33 artifact v1 -->\n**Role:** `epoch` | **Phases owned:** p1-request, p2-elicit, p3-propose, p4-review, p5-plan-uat, p6-ratify, p7-handoff, p8-impl-plan, p9-worker-slices, p10-code-review, p11-impl-uat, p12-landing\n\n## Protocol Context (generated from schema.xml)\n\n### Owned Phases\n\n| Phase | Name | Domain | Transitions |\n|-------|------|--------|-------------|\n| `p1-request` | Request | user | → `p2-elicit` (classification confirmed, research and explore complete) |\n| `p2-elicit` | Elicit | user | → `p3-propose` (URD created with structured requirements) |\n| `p3-propose` | Propose | plan | → `p4-review` (proposal created) |\n| `p4-review` | Review | plan | → `p5-plan-uat` (all 3 reviewers vote ACCEPT); → `p3-propose` (any reviewer votes REVISE) |\n| `p5-plan-uat` | Plan UAT | user | → `p6-ratify` (user accepts plan); → `p3-propose` (user requests changes) |\n| `p6-ratify` | Ratify | plan | → `p7-handoff` (proposal ratified, IMPL_PLAN placeholder created) |\n| `p7-handoff` | Handoff | plan | → `p8-impl-plan` (handoff authored in the HANDOFF Pasture task body) |\n| `p8-impl-plan` | Impl Plan | impl | → `p9-worker-slices` (all slices created with leaf tasks, assigned, and dependency-chained) |\n| `p9-worker-slices` | Worker Slices | impl | → `p10-code-review` (all slices complete, quality gates pass) |\n| `p10-code-review` | Code Review | impl | → `p11-impl-uat` (all 3 reviewers ACCEPT, all BLOCKERs resolved); → `p9-worker-slices` (any reviewer votes REVISE) |\n| `p11-impl-uat` | Impl UAT | user | → `p12-landing` (user accepts implementation); → `p9-worker-slices` (user requests changes) |\n| `p12-landing` | Landing | impl | → `complete` (git push succeeds, all tasks closed or dependency-resolved) |\n\n### Commands\n\n| Command | Description | Phases |\n|---------|-------------|--------|\n| `pasture:epoch` | Master orchestrator for full 12-phase workflow | p1-request, p2-elicit, p3-propose, p4-review, p5-plan-uat, p6-ratify, p7-handoff, p8-impl-plan, p9-worker-slices, p10-code-review, p11-impl-uat, p12-landing |\n\n### General Constraints\n\n**[C-actionable-errors]**\n- Given: an error, exception, or user-facing message\n- When: creating or raising\n- Then: make it actionable: describe (1) what went wrong, (2) why it happened, (3) where it failed (file location, module, or function), (4) when it failed (step, operation, or timestamp), (5) what it means for the caller, and (6) how to fix it\n- Should not: raise generic or opaque error messages (e.g. 'invalid input', 'operation failed') that don't guide the user toward resolution\n\n**[C-audit-dep-chain]**\n- Given: any phase transition\n- When: creating new task\n- Then: chain dependency: \"${EPOCH_REC}\" block parent --by child\n- Should not: skip dependency chaining or invert direction\n\n_Example (correct)_\n\n```bash\n# Full dependency chain: work flows bottom-up, closure flows top-down\n\"${EPOCH_REC}\" block \"${REQUEST_ID_URI}\" --by \"${URE_ID_URI}\"\n\"${EPOCH_REC}\" block \"${URE_ID_URI}\" --by \"${PROPOSAL_ID_URI}\"\n\"${EPOCH_REC}\" block \"${PROPOSAL_ID_URI}\" --by \"${IMPL_PLAN_ID_URI}\"\n\"${EPOCH_REC}\" block \"${IMPL_PLAN_ID_URI}\" --by \"${SLICE_1_ID_URI}\"\n\"${EPOCH_REC}\" block \"${SLICE_1_ID_URI}\" --by \"${LEAF_TASK_A_ID_URI}\"\n```\n\n**[C-audit-never-delete]**\n- Given: any task or label\n- When: modifying\n- Then: add labels and comments only\n- Should not: delete or close tasks prematurely, remove labels\n\n**[C-clean-review-exit]**\n- Given: per-wave code review and end-of-epoch convergence\n- When: evaluating review results\n- Then: during a wave, only an open BLOCKER finding gates the wave; IMPORTANT and MINOR findings may stay open past a wave gate; after the last wave, convergence queries every open `pasture:finding` across all waves and targets 0\u002F0\u002F0 within the gate declaration's maxRounds; iterate review -> fix -> re-review up to the chosen review-effort budget; on budget exhaustion without reaching that target, SURFACE the outstanding findings to the user at a gate for a decision\n- Should not: require a zeroed round at a wave boundary; close a finding outside `C-finding-lifecycle`; proceed past the chosen budget without surfacing outstanding findings to the user; hardcode the budget; batch review across multiple slices\n\n**[C-dep-direction]**\n- Given: adding a Pasture dependency\n- When: determining direction\n- Then: parent blocked-by child: \"${EPOCH_REC}\" block \"${STAYS_OPEN_URI}\" --by \"${MUST_FINISH_FIRST_URI}\"\n- Should not: invert (child blocked-by parent)\n\n_Example (correct)_ — also illustrates: C-audit-dep-chain\n\n```bash\n\"${EPOCH_REC}\" block \"${REQUEST_ID_URI}\" --by \"${URE_ID_URI}\"\n```\n\n_Example (anti-pattern)_\n\n```bash\n\"${EPOCH_REC}\" block \"${URE_ID_URI}\" --by \"${REQUEST_ID_URI}\"\n```\n\n**[C-frontmatter-refs]**\n- Given: cross-task references (URD, request, etc.)\n- When: linking tasks\n- Then: use description frontmatter references: block\n- Should not: invent relationship commands or use blocking dependencies for reference documents\n\n**[C-handoff-skill-invocation]**\n- Given: an agent is launched for a new phase (especially p7 to p8 handoff)\n- When: composing the launch prompt\n- Then: prompt MUST start with Skill(\u002Fpasture:{role}) invocation directive so the agent loads its role instructions\n- Should not: launch agents without skill invocation — they skip role-critical procedures like ephemeral exploration and leaf task creation\n\n**[C-integration-points]**\n- Given: multiple vertical slices share types, interfaces, or data flows\n- When: decomposing IMPL_PLAN in Phase 8\n- Then: identify horizontal Layer Integration Points and document them in IMPL_PLAN; each integration point specifies: owning slice, consuming slices, shared contract, merge timing; include integration points in slice descriptions so workers know what to export and import\n- Should not: leave cross-slice dependencies implicit; assume workers will discover contracts on their own\n\n**[C-review-consensus]**\n- Given: review cycle (p4 or p10)\n- When: evaluating\n- Then: all 3 reviewers must ACCEPT before proceeding\n- Should not: proceed with any REVISE vote outstanding\n\n**[C-review-effort-budget]**\n- Given: the start of Phase 8 (IMPL_PLAN), like the Phase-1 research-depth gate\n- When: deciding how much review-and-fix effort to spend per wave\n- Then: request a configurable review-effort budget from the user — defaults: (1) three rounds, (2) one round, (3) zero rounds, (4) unlimited, (5) custom; the review->fix->re-review loop iterates up to the chosen budget; convergence runs at the end over open `pasture:finding` tasks across all waves and targets 0\u002F0\u002F0 within maxRounds; on budget exhaustion without convergence, surface the outstanding findings to the user for a decision\n- Should not: hardcode the review-cycle budget (e.g. an unconditional fixed cap baked into the prose instead of asked); proceed past the chosen budget without surfacing outstanding findings to the user; loop forever when a finite budget was chosen\n\n**[C-slice-review-before-close]**\n- Given: workers complete their implementation slices\n- When: slice implementation is done\n- Then: workers notify supervisor with \"${EPOCH_REC}\" note (not \"${EPOCH_REC}\" close); slices must be reviewed at least once by reviewers before closure; only the supervisor closes slices, after review passes\n- Should not: close slices immediately upon worker completion; allow workers to close their own slices\n\n**[C-supervisor-explore-ephemeral]**\n- Given: supervisor needs codebase exploration\n- When: starting Phase 8 (IMPL_PLAN)\n- Then: spawn ephemeral Explore subagents via Agent tool for scoped codebase queries; each subagent is short-lived and returns findings; no standing team overhead\n- Should not: explore the codebase directly as supervisor; maintain a standing explore team\n\n**[C-uat-feedback-disposition]**\n- Given: any UAT feedback item (Phase 5 or Phase 11) — flagged by the user OR a deferral proposed by the architect\u002Fsupervisor\n- When: recording each item\n- Then: assign every item an explicit, user-confirmed disposition of FIX-NOW, DEFER, or WON'T-DO; deferrals may be agent-proposed, but ALL deferred items — whoever proposed them — MUST be raised to the user at the next user gate (URE, Plan UAT, or Impl UAT) for confirmation; FIX-NOW items are resolved in the current wave, DEFER'd items are the SOLE source feeding the FOLLOWUP epic\n- Should not: leave a feedback item without a confirmed disposition; silently defer any item without raising it to the user at the next gate; route any review severity (BLOCKER\u002FIMPORTANT\u002FMINOR) into FOLLOWUP — only DEFER'd UAT items feed it\n\n### Handoffs\n\n_(No handoffs for this role)_\n\n### Startup Sequence\n\n_(No startup sequence defined for this role)_\n\n### Introduction\n\nYou are the master orchestrator for the full 12-phase epoch lifecycle. You delegate planning phases (1-7) to the architect and implementation phases (7-12) to the supervisor.\n\n### What You Own\n\nYou own the full 12-phase lifecycle from Request to Landing. You delegate phases 1-7 to the architect and phases 7-12 to the supervisor. The epoch role coordinates the complete workflow end-to-end and is the only role that spans all phases.\n\n### Inter-Agent Coordination\n\nAgents coordinate through tracker tasks and comments:\n\n| Action | Command |\n|--------|---------|\n| List blocked | `\"${EPOCH_REC}\" query --blocked` |\n| Add progress note | `\"${EPOCH_REC}\" note \"${TASK_ID_URI}\" --author pasture-system\u002F00000000-0000-0000-0000-000000000000 --body \"Progress: ...\"` |\n| List in-progress | `\"${EPOCH_REC}\" query --status=in_progress` |\n| Check task details | `\"${EPOCH_REC}\" show \"${TASK_ID_URI}\"` |\n| Update status | `\"${EPOCH_REC}\" status \"${TASK_ID_URI}\" --set in_progress` |\n\n**-> [Full workflow in PROCESS.md](..\u002Fprotocol\u002FPROCESS.md)** \u003C- All 12 Phases\n\n**[epoch-verbatim-capture]**\n- Given: user provides request\n- When: capturing\n- Then: store verbatim without paraphrasing in Phase 1 REQUEST task\n- Should not: summarize or interpret the user's words\n\n**[epoch-dep-chain]**\n- Given: any phase transition\n- When: creating new task\n- Then: add dependency to previous: \"${EPOCH_REC}\" block \"${PARENT_URI}\" --by \"${CHILD_URI}\"\n- Should not: skip dependency chaining\n\n**[epoch-audit-never-delete]**\n- Given: task completion\n- When: updating\n- Then: add comments and labels only\n- Should not: close or delete tasks prematurely\n\n**[epoch-consensus-required]**\n- Given: review cycle\n- When: any REVISE vote\n- Then: create PROPOSAL-N+1 and repeat review\n- Should not: proceed without full ACCEPT consensus from all 3 reviewers\n\n**[epoch-followup-trigger]**\n- Given: UAT (Phase 5 or 11) produces one or more user-DEFER'd items\n- When: finishing UAT\n- Then: Supervisor creates a follow-up epic (label pasture:epic-followup) from the user-DEFER'd UAT items only\n- Should not: create a follow-up epic from any review severity (BLOCKER\u002FIMPORTANT\u002FMINOR) — the FOLLOWUP epic is fed only by user-DEFER'd UAT items\n\n**[epoch-supervisor-not-idle]**\n- Given: a freshly spawned supervisor (Phase 8 IMPL_PLAN)\n- When: it dispatches Explore subagents and appears idle\n- Then: let it work — an apparently-idle supervisor is usually running Explore subagents to map the codebase\n- Should not: shut down or restart a supervisor that looks idle at the start of the IMPL_PLAN phase\n\n**[frag--review-clean-exit]**\n- Given: per-wave code review and end-of-epoch convergence\n- When: evaluating review results\n- Then: iterate review -> fix -> re-review up to the chosen review-effort budget; during a wave only an open BLOCKER finding gates progress, while IMPORTANT and MINOR findings stay open; after the last wave, converge over open `pasture:finding` tasks across all waves toward 0\u002F0\u002F0 within maxRounds; a finding closes only per `C-finding-lifecycle`; on budget exhaustion without convergence, SURFACE the outstanding findings to the user at a gate for a decision\n- Should not: hardcode the budget; proceed past the chosen budget without surfacing outstanding findings to the user; require a zeroed round at a wave boundary; close a finding outside `C-finding-lifecycle`; loop forever when a finite budget was chosen\n\n**[epoch-autonomous-progression]**\n- Given: non-user-gated phase completes\n- When: transitioning\n- Then: proceed autonomously; User gates: [request (p1 research depth); elicit (p2 requirements interview); plan-review (p5 plan UAT); impl-plan (p8 implementation effort and review budget); impl-uat (p11 implementation UAT)]\n- Should not: ask 'Should I proceed?' for autonomous phases; add user gates beyond the defined list\n\n**[epoch-uat-auto-ratify]**\n- Given: Phase 5 UAT ACCEPT\n- When: transitioning to Phase 6\n- Then: ratify automatically\n- Should not: ask user for ratification confirmation\n\n**[epoch-frontmatter-refs]**\n- Given: cross-task references\n- When: linking related tasks (e.g. URD to REQUEST)\n- Then: use description frontmatter references: block\n- Should not: use peer-reference commands\n\n## Task Recovery\n\nThe tracker backend is recorded once in .agents.local\u002Fpasture\u002FEPOCH.md; read that file instead of re-probing. If EPOCH.md is absent, stop and ask the orchestrator to run detection.\n\nRecover from live tracker state at session start and after compaction; never trust cached task rows. Read the `rec:` path from the header and use it as the one entry point: `\"${EPOCH_REC}\" \u003Cop>`. The operation set is closed: create, label, block, note, show, query, ready, status, close, whoami, signal.\n\nStore selection: the adapter reads PASTURE_DB_PATH when set, otherwise the header's `db:`; the probe wrote that value from the same precedence (XDG_DATA_HOME\u002Fpasture\u002Fpasture.db, HOME\u002F.local\u002Fshare\u002Fpasture\u002Fpasture.db, then .pasture\u002Fpasture.db). Use the same selected store for every command. If a recovery query fails, report its actual store path, failed operation, impact, and permission\u002Fschema\u002Fconfiguration repair; failure is not an empty work queue.\n\nThe adapter passes `--namespace` only when EPOCH.md records `namespace:`. That pin wins on both backends. Otherwise it omits the flag. Pasture then derives the namespace itself: PASTURE_NAMESPACE, else the git remote, then file:\u002F\u002F of the working directory. The ledger calls derive_namespace for the header and for every write that has no pin. That function is the one list of remote forms; do not restate it. With no origin, both backends use file:\u002F\u002F of the working directory, so invocations from different subdirectories use different namespaces. Queries without a pin follow that backend's derivation; pass `--all-namespaces` to opt out. When neither a pin nor PASTURE_NAMESPACE is set, pasture reads are unscoped and creation falls back to the git remote, then file:\u002F\u002F of the working directory. Inspect the returned full URI before choosing repository work. Separate reads are not an atomic snapshot.\n\nRun `\"${EPOCH_REC}\" ready`, `\"${EPOCH_REC}\" query --blocked`, and `\"${EPOCH_REC}\" query --status in_progress`. For each referenced active task, run `\"${EPOCH_REC}\" show \"$TASK_URI\"`, `\"${EPOCH_REC}\" show \"$TASK_URI\" --notes`, and `\"${EPOCH_REC}\" show \"$TASK_URI\" --history`. All *_URI variables in examples are inputs bound to full task URIs from an assignment, a verified tracker read, or the URI create printed; never use legacy short IDs. Resolve each variable before execution and quote it as one operand.\n\nCreate prints the new task URI directly. Persist it before any label or note: create-plus-label is non-atomic, so on failure retain the URI and retry only the failed label\u002Fnote operation, never create again. Only open, in_progress, and closed are statuses; blockage is represented by live dependencies and notes, not a blocked status.\n\nAn owner is recorded with `\"${EPOCH_REC}\" label \"$TASK_URI\" owner:\u003Crole>`. That is a claim, not an allocation: an unassigned task stays unassigned until the supervisor allocates it, and repeating an identical claim is not allocation. Never invent an owner.\n\nParent stays open and is blocked by child: `\"${EPOCH_REC}\" block \"$PARENT_URI\" --by \"$CHILD_URI\"`. Reference documents belong in description frontmatter under references:, never fabricated blockers. Workers report evidence and handoffs without closing their slices or leaves; the supervisor closes only after independent review and satisfied gates. Use git agent-commit for local commits. Never install, enable, or modify Git hooks, Git hook path configuration, or pre-commit integration without explicit user approval. Tracker writes are durable; Git history lands separately.\n\nIf the header records `backend: error`, stop before the first mutation: the store exists but cannot be used, and falling back could lose earlier epochs. A mid-epoch write failure retries 3 times. After the third failure, write the attempted record to .agents.local\u002Fpasture\u002Fpending\u002F\u003Ctimestamp>-\u003Cop>.md, keep the recorded backend, and surface the pending record to the user at the next gate. Never switch backends and never drop the record.\n\n## Task Attribution\n\nSelect the current registered author with \"${EPOCH_REC}\" whoami. It prints the author recorded in the header or PASTURE_AUTHOR, and on the pasture backend it confirms that actor with agents show. It does not list agents and it does not invent one. If neither source names an author, it stops: set author: in EPOCH.md or export PASTURE_AUTHOR. Never auto-register or guess an identity during recovery. Every note supplies --author explicitly; machine examples use the pre-registered pasture-system\u002F00000000-0000-0000-0000-000000000000 actor. If it is absent, stop and request registration from the operator rather than claiming registration is fixed. Preserve human intent verbatim and quote historical attribution as evidence, not as a forged new author.\n\n## Tracker Adapter\n\nTracker calls go through one entry point: `\"${EPOCH_REC}\" \u003Cop>`. The backend is chosen once, recorded in `.agents.local\u002Fpasture\u002FEPOCH.md`, and read from there; no skill body names a backend recipe.\n\nResolve the entry point from the header at the start of each shell session:\n\n~~~bash\nEPOCH_REC=$(sed -n 's\u002F^rec: \u002F\u002Fp' .agents.local\u002Fpasture\u002FEPOCH.md 2>\u002Fdev\u002Fnull | tail -1)\nif [ -z \"$EPOCH_REC\" ]; then\n  echo \"no epoch header at .agents.local\u002Fpasture\u002FEPOCH.md; run the epoch detection step\" >&2\n  exit 3\nfi\n~~~\n\n| Backend | Meaning | Action |\n|---|---|---|\n| `pasture` | binary is capable and the store is usable | forward each op through the adapter |\n| `ledger` | no usable binary or store | record in the ledger under `.agents.local\u002Fpasture\u002F`; `assign` is claim-only |\n| `error` | the store exists but cannot be used | diagnose and stop before the first mutation; do not fall back |\n\nThe operation set is closed. `relate` is `block --kind`; `assign` is `label ID owner:\u003Crole>`.\n\n| Op | Canonical call | Action |\n|---|---|---|\n| `create` | `\"${EPOCH_REC}\" create --kind K --title T --phase P [--priority N] [--label L]... [--ref K=V]... [--body TEXT|@-] -> ID` | create an immutable record; the body is verbatim; print the new task URI |\n| `label` | `\"${EPOCH_REC}\" label ID L   (assignment: label ID owner:\u003Crole>)` | add a label; never remove one |\n| `block` | `\"${EPOCH_REC}\" block PARENT --by CHILD [--kind blocked_by|derived_from|supersedes|discovered_from]` | add an edge; a blocked_by edge keeps the parent open until the child closes; a typed edge uses --kind |\n| `note` | `\"${EPOCH_REC}\" note ID --author A [--kind K] [--to ROLE] [--body TEXT|@-]` | append an attributed immutable note; --to makes it a message |\n| `show` | `\"${EPOCH_REC}\" show ID [--notes] [--history] [--format text|json]` | read one record, including description, phase, priority and refs; --notes lists notes. Pasture show returns createdAt and updatedAt; ledger timestamps stay on --history |\n| `query` | `\"${EPOCH_REC}\" query [--status S] [--label L] [--blocked] [--tree ID] [--all-namespaces] [--format text|json]` | enumerate records by status, label, blocked state, or dependency tree; --blocked rejects --status |\n| `ready` | `\"${EPOCH_REC}\" ready [--label L] [--all-namespaces] [--format text|json]` | list non-closed records with no non-closed blocker |\n| `status` | `\"${EPOCH_REC}\" status ID --set open|in_progress|closed` | change state (start work); the store keeps the only status field |\n| `close` | `\"${EPOCH_REC}\" close ID --reason R` | close a record; policy reserves this for the supervisor |\n| `whoami` | `\"${EPOCH_REC}\" whoami` | resolve the acting author; never invent one |\n| `signal` | `\"${EPOCH_REC}\" signal --kind K --phase P --skill S --entry E --quote Q` | record one user signal; pasture notes the feedback task, ledger appends one JSON object |\n\nEPOCH.md lives at .agents.local\u002Fpasture\u002FEPOCH.md (offer a gitignore entry; never require one).\n\n---\nepoch: \u003Cepoch id>\nbackend: pasture|ledger|error\nprobed_at: \u003CUTC timestamp>\nnamespace: \u003Cnamespace URI>        # optional pin; absent means the backend derives\ndb: \u003Cabsolute store path>         # pasture only\nauthor: \u003Cregistered actor id>     # pasture only; what whoami prints\nledger: \u003Cabsolute ledger path>    # ledger only\nrec: \u003Cabsolute adapter path>      # pasture and ledger\nfeedback: \u003Ctask URI>              # pasture only; the epoch feedback task for signals\nreviewer_tier: T2|T3              # optional; the phase-8 review-effort choice\ndegrades: assign=claim-only       # ledger only\n---\n\u003Cthe probe transcript and the user's one-line acknowledgement, verbatim>\n\nNo mid-epoch switching: if the recorded backend fails mid-epoch, stop and report. Switching is never a repair; abandon the epoch and start a new one with a fresh probe, so the header and the records never describe different backends.\n\nA mid-epoch write failure retries 3 times. After the third failure, write the attempted record to .agents.local\u002Fpasture\u002Fpending\u002F\u003Ctimestamp>-\u003Cop>.md, keep the recorded backend, and surface the pending record to the user at the next gate. Never switch backends and never drop the record.\n\nThe orchestrator runs the probe once. Subagents read EPOCH.md and never re-probe.\n\nWrite this probe to `.agents.local\u002Fpasture\u002Fprobe.sh` and run it before the first write:\n\n\u003C!-- pasture:asset probe -->\n~~~sh\n#!\u002Fbin\u002Fsh\n# derive_namespace is the one ledger normalisation. Forms: git@host:path,\n# ssh:\u002F\u002F, git:\u002F\u002F, http:\u002F\u002F, https:\u002F\u002F and user@host:path. An empty remote inside\n# a git work tree is file:\u002F\u002F of the physical working directory, matching\n# Pasture. PASTURE_NAMESPACE matches the value Pasture uses when\n# --namespace is omitted.\nderive_namespace() {\n  if [ -n \"${PASTURE_NAMESPACE:-}\" ]; then\n    printf '%s\\n' \"$PASTURE_NAMESPACE\"\n    return 0\n  fi\n  remote=$(git remote get-url origin 2>\u002Fdev\u002Fnull || true)\n  remote=$(printf '%s' \"$remote\" | tr -d '\\r\\n')\n  if [ -z \"$remote\" ]; then\n    dir=$(pwd -P 2>\u002Fdev\u002Fnull || pwd)\n    if [ -n \"${PWD:-}\" ] && [ \"$(cd -- \"$PWD\" 2>\u002Fdev\u002Fnull && pwd -P)\" = \"$dir\" ]; then\n      dir=$PWD\n    fi\n    printf 'file:\u002F\u002F%s\\n' \"$dir\"\n    return 0\n  fi\n  normalize_git_remote \"$remote\"\n}\n\nnormalize_git_remote() {\n  url=$1\n  tab=$(printf '\\t')\n  while :; do\n    case $url in\n      ' '*) url=${url# } ;;\n      \"$tab\"*) url=${url#\"$tab\"} ;;\n      *) break ;;\n    esac\n  done\n  while :; do\n    case $url in\n      *' ') url=${url% } ;;\n      *\"$tab\") url=${url%\"$tab\"} ;;\n      *) break ;;\n    esac\n  done\n  case \"$url\" in\n    ssh:\u002F\u002F*)\n      rest=${url#ssh:\u002F\u002F}\n      case \"$rest\" in\n        *@*) rest=${rest#*@} ;;\n      esac\n      url=\"https:\u002F\u002F$rest\"\n      ;;\n    git:\u002F\u002F*)\n      url=\"https:\u002F\u002F${url#git:\u002F\u002F}\"\n      ;;\n    http:\u002F\u002F*)\n      url=\"https:\u002F\u002F${url#http:\u002F\u002F}\"\n      ;;\n    https:\u002F\u002F*)\n      ;;\n    *@*:*)\n      rest=${url#*@}\n      host=${rest%%:*}\n      path=${rest#*:}\n      url=\"https:\u002F\u002F$host\u002F$path\"\n      ;;\n  esac\n  case \"$url\" in\n    *.git) url=${url%.git} ;;\n  esac\n  printf '%s\\n' \"$url\"\n}\n\n# Epoch tracker probe. Side-effect free. Prints KEY=VALUE lines and exits 0.\n# Order: capability (--help), store existence (test -f), usability (migrate --dry-run).\n# Verb list is generated from TrackerOps.\nset -u\n\nPASTURE_BIN=\"${PASTURE_BIN:-pasture}\"\nLEDGER_DIR=\"${PWD}\u002F.agents.local\u002Fpasture\u002Fledger\"\nREC_PATH=\"${PWD}\u002F.agents.local\u002Fpasture\u002Frec\"\n\nemit() {\n  printf 'BACKEND=%s\\n' \"$1\"\n  printf 'REASON=%s\\n' \"${2:-}\"\n  [ -n \"${3:-}\" ] && printf 'DB=%s\\n' \"$3\"\n  [ -n \"${4:-}\" ] && printf 'NS=%s\\n' \"$4\"\n  [ -n \"${5:-}\" ] && printf 'ACT=%s\\n' \"$5\"\n  [ -n \"${6:-}\" ] && printf 'VER=%s\\n' \"$6\"\n  printf 'LEDGER=%s\\n' \"$LEDGER_DIR\"\n  printf 'REC=%s\\n' \"$REC_PATH\"\n  printf 'PROBED_AT=%s\\n' \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"\n  exit 0\n}\n\n# Ledger header value. Same function every ledger write calls.\nemit_ledger() {\n  _ns=$(derive_namespace)\n  emit ledger \"$1\" \"${2:-}\" \"$_ns\" \"\" \"${3:-}\"\n}\n\n# 1. Capability. --help never opens a store.\nif ! command -v \"$PASTURE_BIN\" >\u002Fdev\u002Fnull 2>&1; then\n  emit_ledger not-installed\nfi\n\nVER=\"$(\"$PASTURE_BIN\" --version 2>\u002Fdev\u002Fnull | awk '{print $NF}')\"\n\n\"$PASTURE_BIN\" --help >\u002Fdev\u002Fnull 2>&1\nif [ \"$?\" -ne 0 ]; then\n  emit_ledger help-failed \"\" \"$VER\"\nfi\n\nfor VERB in \"task create\" \"task label\" \"task dep\" \\\n            \"task comment\" \"task comments\" \"task timeline\" \\\n            \"task show\" \"task blocked\" \"task list\" \\\n            \"task ready\" \"task update\" \"task close\" \\\n            \"task agents\"; do\n  # shellcheck disable=SC2086\n  \"$PASTURE_BIN\" $VERB --help >\u002Fdev\u002Fnull 2>&1\n  if [ \"$?\" -ne 0 ]; then\n    emit_ledger \"missing-verb:$VERB\" \"\" \"$VER\"\n  fi\ndone\n\n# 2. Store existence. Never open a missing store: opening creates it.\nif [ -n \"${PASTURE_DB_PATH:-}\" ]; then\n  DB=\"$PASTURE_DB_PATH\"\nelif [ -n \"${XDG_DATA_HOME:-}\" ]; then\n  DB=\"$XDG_DATA_HOME\u002Fpasture\u002Fpasture.db\"\nelif [ -n \"${HOME:-}\" ]; then\n  DB=\"$HOME\u002F.local\u002Fshare\u002Fpasture\u002Fpasture.db\"\nelse\n  DB=\".pasture\u002Fpasture.db\"\nfi\n\nif [ ! -f \"$DB\" ]; then\n  emit_ledger no-store \"$DB\" \"$VER\"\nfi\n\n# 3. Usability. --dry-run leaves the file's SHA-256 unchanged.\n\"$PASTURE_BIN\" --db \"$DB\" migrate --dry-run >\u002Fdev\u002Fnull 2>&1\nRC=$?\nif [ \"$RC\" -ne 0 ]; then\n  emit error \"store-exit-$RC\" \"$DB\" \"\" \"\" \"$VER\"\nfi\n\n# 4. Author. A missing author is a warning, not an unusable store.\n# Pasture: the probe does not derive a namespace. Writing one into\n# EPOCH.md would freeze it and override Pasture's own derivation.\n# Ledger: emit_ledger derives and prints NS because there is no Pasture store\n# to ask.\n\nACT=\"${PASTURE_AUTHOR:-pasture-system\u002F00000000-0000-0000-0000-000000000000}\"\n\"$PASTURE_BIN\" --db \"$DB\" task agents show \"$ACT\" --format json >\u002Fdev\u002Fnull 2>&1\nRC=$?\nif [ \"$RC\" -ne 0 ]; then\n  printf 'WARN=no-author\\n'\n  printf 'probe: warning: author %s is not registered in %s\\n  why: task agents show exited %s, and a missing author is not an unusable store\\n  where: epoch tracker probe, author check\\n  fix: register the actor, or export PASTURE_AUTHOR to a registered id; the epoch still records backend pasture\\n' \"$ACT\" \"$DB\" \"$RC\" >&2\nfi\n\nemit pasture \"\" \"$DB\" \"\" \"$ACT\" \"$VER\"\n~~~\n\u003C!-- \u002Fpasture:asset probe -->\n\nDetection limits, by step:\n\n- pasture --help: detects that the binary is on PATH and that each required verb has a help page. It cannot detect version compatibility, store health, permissions, schema, namespace or author. A help page can exist on a binary whose runtime is broken.\n- test -f: detects a missing store file and stops the probe before anything opens it. It cannot detect a directory, an unreadable or corrupt database, an out-of-date schema, a held lock, or a different store selected by configuration. Never replace it with a read: opening a missing store creates it.\n- migrate --dry-run: detects that the store opens and that its migrations can be planned; it leaves the file's SHA-256 unchanged. It still opens the store and may take locks. It cannot detect concurrent writers, a defect that only a real migration touches, an unregistered author, or a namespace mismatch. A non-zero result is the only error backend: the store exists but cannot be used.\n- Namespace: for pasture, the probe does not derive a namespace and does not write one into the header. Writing one would freeze Pasture's own derivation (PASTURE_NAMESPACE, else the git origin, else file:\u002F\u002F of the working directory). For ledger, there is no Pasture store to ask, so the probe derives and prints NS from derive_namespace, and every unpinned ledger write uses that value. A missing remote is not an unusable store. It cannot detect a remote other than origin.\n- Author: a missing or unregistered author is a warning (WARN=no-author), not an error backend. It cannot detect an author registered in another namespace, nor prove a later write will be allowed.\n- The probe cannot detect a store chosen by configuration outside the env\u002Fdefault precedence, and it does not prove that any later write will succeed.\n\nWrite this adapter to the path the header records in `rec:` and make it executable; every tracker call goes through it:\n\n\u003C!-- pasture:asset rec -->\n~~~sh\n#!\u002Fbin\u002Fsh\n# derive_namespace is the one ledger normalisation. Forms: git@host:path,\n# ssh:\u002F\u002F, git:\u002F\u002F, http:\u002F\u002F, https:\u002F\u002F and user@host:path. An empty remote inside\n# a git work tree is file:\u002F\u002F of the physical working directory, matching\n# Pasture. PASTURE_NAMESPACE matches the value Pasture uses when\n# --namespace is omitted.\nderive_namespace() {\n  if [ -n \"${PASTURE_NAMESPACE:-}\" ]; then\n    printf '%s\\n' \"$PASTURE_NAMESPACE\"\n    return 0\n  fi\n  remote=$(git remote get-url origin 2>\u002Fdev\u002Fnull || true)\n  remote=$(printf '%s' \"$remote\" | tr -d '\\r\\n')\n  if [ -z \"$remote\" ]; then\n    dir=$(pwd -P 2>\u002Fdev\u002Fnull || pwd)\n    if [ -n \"${PWD:-}\" ] && [ \"$(cd -- \"$PWD\" 2>\u002Fdev\u002Fnull && pwd -P)\" = \"$dir\" ]; then\n      dir=$PWD\n    fi\n    printf 'file:\u002F\u002F%s\\n' \"$dir\"\n    return 0\n  fi\n  normalize_git_remote \"$remote\"\n}\n\nnormalize_git_remote() {\n  url=$1\n  tab=$(printf '\\t')\n  while :; do\n    case $url in\n      ' '*) url=${url# } ;;\n      \"$tab\"*) url=${url#\"$tab\"} ;;\n      *) break ;;\n    esac\n  done\n  while :; do\n    case $url in\n      *' ') url=${url% } ;;\n      *\"$tab\") url=${url%\"$tab\"} ;;\n      *) break ;;\n    esac\n  done\n  case \"$url\" in\n    ssh:\u002F\u002F*)\n      rest=${url#ssh:\u002F\u002F}\n      case \"$rest\" in\n        *@*) rest=${rest#*@} ;;\n      esac\n      url=\"https:\u002F\u002F$rest\"\n      ;;\n    git:\u002F\u002F*)\n      url=\"https:\u002F\u002F${url#git:\u002F\u002F}\"\n      ;;\n    http:\u002F\u002F*)\n      url=\"https:\u002F\u002F${url#http:\u002F\u002F}\"\n      ;;\n    https:\u002F\u002F*)\n      ;;\n    *@*:*)\n      rest=${url#*@}\n      host=${rest%%:*}\n      path=${rest#*:}\n      url=\"https:\u002F\u002F$host\u002F$path\"\n      ;;\n  esac\n  case \"$url\" in\n    *.git) url=${url%.git} ;;\n  esac\n  printf '%s\\n' \"$url\"\n}\n\n# Epoch tracker adapter (rec). Generated from TrackerOps. Do not hand-edit.\n# Backend: the one recorded in EPOCH_FILE. Never re-probes, never switches.\nset -u\n\nEPOCH_FILE=\"${EPOCH_FILE:-.agents.local\u002Fpasture\u002FEPOCH.md}\"\nPASTURE_BIN=\"${PASTURE_BIN:-pasture}\"\n\nfail() {\n  printf 'rec: %s\\n  why: %s\\n' \"$1\" \"$2\" >&2\n  if [ -n \"${4:-}\" ]; then\n    printf '  where: %s\\n' \"$4\" >&2\n  fi\n  printf '  fix: %s\\n' \"$3\" >&2\n  exit 3\n}\n\n[ -f \"$EPOCH_FILE\" ] || fail \"no epoch header at $EPOCH_FILE\" \\\n  \"the backend is chosen once and recorded before the first write; EPOCH_FILE is relative to the working directory\" \\\n  \"run from the repository root, or set EPOCH_FILE to the absolute header path, then run the Starting an Epoch detection step\"\n\nfield() {\n  sed -n \"s\u002F^$1: \u002F\u002Fp\" \"$EPOCH_FILE\" 2>\u002Fdev\u002Fnull | tail -1\n}\n\nBACKEND=\"$(field backend)\"\nDB=\"${PASTURE_DB_PATH:-$(field db)}\"\n# --namespace is passed only when EPOCH.md records one. Otherwise pasture\n# derives (env, else git remote, else file:\u002F\u002F of cwd). Ledger calls the same\n# derivation function the probe printed, unless the header pins a namespace.\nHEADER_NS=\"$(field namespace)\"\nif [ \"$BACKEND\" = ledger ] && [ -z \"$HEADER_NS\" ]; then\n  NS=$(derive_namespace)\nelse\n  NS=\"$HEADER_NS\"\nfi\nAUTHOR=\"${PASTURE_AUTHOR:-$(field author)}\"\nLEDGER=\"$(field ledger)\"\nif [ -z \"$LEDGER\" ]; then\n  LEDGER=\".agents.local\u002Fpasture\u002Fledger\"\nfi\n\ncase \"$BACKEND\" in\n  pasture) : ;;\n  ledger) : ;;\n  error)\n    fail \"the recorded backend is error\" \\\n      \"the store exists but cannot be used, and falling back could lose earlier epochs\" \\\n      \"repair the store named in $EPOCH_FILE, or start a new epoch with a working backend\" ;;\n  \"\")\n    fail \"no backend recorded in $EPOCH_FILE\" \\\n      \"the probe has not written the header yet\" \\\n      \"run the Starting an Epoch detection step\" ;;\n  *)\n    fail \"unknown backend '$BACKEND' in $EPOCH_FILE\" \\\n      \"the backend set is closed to pasture, ledger and error\" \\\n      \"rewrite the backend line from the probe output\" ;;\nesac\n\nOP=\"${1:-}\"\n[ \"$#\" -gt 0 ] && shift\n\nallns=0\nblocked=0\nrefs=\nlabels=\nbody=\nkind=task\ntitle=\nphase=\npriority=\nlabel=\nstatus=\ntree=\nfmt=json\nmode=show\nreason=\nnote_body=\nnote_kind=\nnote_to=\nparent=\nchild=\nedge_kind=blocked_by\nid=\n\npb() {\n  if [ -n \"$DB\" ]; then\n    \"$PASTURE_BIN\" --db \"$DB\" \"$@\"\n  else\n    \"$PASTURE_BIN\" \"$@\"\n  fi\n}\n\nneed_id() {\n  [ -n \"${1:-}\" ] || fail \"$OP needs a task ID\" \"the first operand was empty\" \"pass the full task URI as the first operand\"\n}\n\n# json_escape and row_encode are total for every byte except NUL, which a\n# shell argument cannot carry. Each is the inverse of its reader (take, and\n# row_decode). A trailing newline is kept: the decoder is not read with a\n# bare command substitution. C0 controls other than tab, CR and LF are\n# \\u00XX, because a raw control byte makes show --format json invalid JSON.\njson_escape() {\n  ROW_IN=$1 awk 'BEGIN {\n    s = ENVIRON[\"ROW_IN\"]\n    out = \"\"\n    for (i = 0; i \u003C 32; i++) ctrl[sprintf(\"%c\", i)] = i\n    for (i = 1; i \u003C= length(s); i++) {\n      c = substr(s, i, 1)\n      if (c == \"\\\\\") out = out \"\\\\\\\\\"\n      else if (c == \"\\\"\") out = out \"\\\\\\\"\"\n      else if (c == \"\\t\") out = out \"\\\\t\"\n      else if (c == \"\\r\") out = out \"\\\\r\"\n      else if (c == \"\\n\") out = out \"\\\\n\"\n      else if (c in ctrl) out = out sprintf(\"\\\\u00%02x\", ctrl[c])\n      else out = out c\n    }\n    printf \"%s\", out\n  }'\n}\n\n# text_field keeps one show --format text record to one line per key. A\n# description that contains a newline would otherwise span lines, and the\n# next line would look like a new key.\ntext_field() {\n  ROW_IN=$1 awk 'BEGIN {\n    s = ENVIRON[\"ROW_IN\"]\n    out = \"\"\n    for (i = 1; i \u003C= length(s); i++) {\n      c = substr(s, i, 1)\n      if (c == \"\\\\\") out = out \"\\\\\\\\\"\n      else if (c == \"\\n\") out = out \"\\\\n\"\n      else if (c == \"\\r\") out = out \"\\\\r\"\n      else out = out c\n    }\n    printf \"%s\", out\n  }'\n}\n\nrow_encode() {\n  ROW_IN=$1 awk 'BEGIN {\n    s = ENVIRON[\"ROW_IN\"]\n    out = \"\"\n    for (i = 1; i \u003C= length(s); i++) {\n      c = substr(s, i, 1)\n      if (c == \"\\\\\") out = out \"\\\\\\\\\"\n      else if (c == \"\\n\") out = out \"\\\\n\"\n      else if (c == \"\\r\") out = out \"\\\\r\"\n      else if (c == \"\\t\") out = out \"\\\\t\"\n      else if (c == \" \") out = out \"\\\\s\"\n      else if (c == \"\\037\") out = out \"\\\\037\"\n      else out = out c\n    }\n    printf \"%s\", out\n  }'\n}\n\nrow_decode() {\n  ROW_IN=$1 awk 'BEGIN {\n    s = ENVIRON[\"ROW_IN\"]\n    esc = 0\n    out = \"\"\n    i = 1\n    n = length(s)\n    while (i \u003C= n) {\n      c = substr(s, i, 1)\n      if (esc) {\n        if (c == \"n\") out = out \"\\n\"\n        else if (c == \"r\") out = out \"\\r\"\n        else if (c == \"t\") out = out \"\\t\"\n        else if (c == \"s\") out = out \" \"\n        else if (c == \"0\" && substr(s, i, 3) == \"037\") { out = out \"\\037\"; i += 2 }\n        else if (c == \"\\\\\") out = out \"\\\\\"\n        else out = out c\n        esc = 0\n      } else if (c == \"\\\\\") esc = 1\n      else out = out c\n      i++\n    }\n    if (esc) out = out \"\\\\\"\n    printf \"%s\", out\n  }'\n}\n\n# Sets decoded. A bare $(row_decode) would eat a trailing newline.\nrow_decode_set() {\n  _raw=$(row_decode \"$1\"; printf '\\003')\n  _mark=$(printf '\\003')\n  decoded=${_raw%\"$_mark\"}\n}\n\nlabel_push() {\n  enc=$(row_encode \"$1\")\n  if [ -z \"$labels\" ]; then\n    labels=$enc\n  else\n    labels=\"$labels $enc\"\n  fi\n}\n\nfold_refs() {\n  [ -n \"$refs\" ] || return 0\n  block=\"references:\"\n  old=$IFS\n  IFS='\n'\n  for pair in $refs; do\n    key=${pair%%=*}\n    val=${pair#*=}\n    block=\"$block\n  $key: $val\"\n  done\n  IFS=$old\n  if [ -z \"$body\" ]; then\n    body=\"---\n$block\n---\"\n  else\n    body=\"---\n$block\n---\n$body\"\n  fi\n}\n\nmint_id() {\n  if [ -r \u002Fproc\u002Fsys\u002Fkernel\u002Frandom\u002Fuuid ]; then\n    printf 'ledger\u002F%s\\n' \"$(cat \u002Fproc\u002Fsys\u002Fkernel\u002Frandom\u002Fuuid)\"\n    return 0\n  fi\n  printf 'ledger\u002F%s-%s\\n' \"$(date -u +%Y%m%dT%H%M%SZ)\" \"$$\"\n}\n\nledger_append() {\n  rec_id=\"$1\"\n  rec_line=\"$2\"\n  mkdir -p \"$LEDGER\" || fail \"cannot create ledger directory $LEDGER\" \"the parent is not writable\" \"choose a writable ledger: path in $EPOCH_FILE\"\n  safe=$(printf '%s' \"$rec_id\" | sed -e 's\u002F%\u002F%25\u002Fg' -e 's#\u002F#%2F#g' -e 's\u002F \u002F%20\u002Fg' -e 's\u002F:\u002F%3A\u002Fg')\n  file=\"$LEDGER\u002F$safe.jsonl\"\n  lock=\"$LEDGER\u002F.lock\"\n  n=0\n  while ! mkdir \"$lock\" 2>\u002Fdev\u002Fnull; do\n    n=$((n + 1))\n    if [ \"$n\" -gt 100 ]; then\n      fail \"ledger lock held at $lock\" \"another rec process did not release it\" \"remove $lock only if no rec process is running, then retry\"\n    fi\n    sleep 0.1\n  done\n  if [ ! -f \"$LEDGER\u002F.gitignore-offered\" ]; then\n    printf 'rec: ledger records are under %s\\n  offer: add that directory to .gitignore if the records should stay uncommitted\\n  note: a gitignore entry is offered, never required\\n' \"$LEDGER\" >&2\n    : > \"$LEDGER\u002F.gitignore-offered\" || true\n  fi\n  printf '%s\\n' \"$rec_line\" >> \"$file\" || {\n    rmdir \"$lock\" 2>\u002Fdev\u002Fnull || true\n    fail \"ledger append failed for $file\" \"the ledger directory was not writable\" \"make $LEDGER writable and retry the same operation\"\n  }\n  rmdir \"$lock\" 2>\u002Fdev\u002Fnull || true\n}\n\nledger_line() {\n  _f_op=\n  _f_id=\n  _f_namespace=\n  _f_title=\n  _f_kind=\n  _f_phase=\n  _f_status=\n  _f_priority=\n  _f_label=\n  _f_parent=\n  _f_child=\n  _f_edge_kind=\n  _f_author=\n  _f_note_kind=\n  _f_to=\n  _f_reason=\n  _f_ref=\n  _f_body=\n  _f_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)\n  while [ \"$#\" -gt 1 ]; do\n    case \"$1\" in\n      op) _f_op=\"$2\"; shift 2 ;;\n      id) _f_id=\"$2\"; shift 2 ;;\n      namespace) _f_namespace=\"$2\"; shift 2 ;;\n      title) _f_title=\"$2\"; shift 2 ;;\n      kind) _f_kind=\"$2\"; shift 2 ;;\n      phase) _f_phase=\"$2\"; shift 2 ;;\n      status) _f_status=\"$2\"; shift 2 ;;\n      priority) _f_priority=\"$2\"; shift 2 ;;\n      label) _f_label=\"$2\"; shift 2 ;;\n      parent) _f_parent=\"$2\"; shift 2 ;;\n      child) _f_child=\"$2\"; shift 2 ;;\n      edge_kind) _f_edge_kind=\"$2\"; shift 2 ;;\n      author) _f_author=\"$2\"; shift 2 ;;\n      note_kind) _f_note_kind=\"$2\"; shift 2 ;;\n      to) _f_to=\"$2\"; shift 2 ;;\n      reason) _f_reason=\"$2\"; shift 2 ;;\n      ref) _f_ref=\"$2\"; shift 2 ;;\n      body) _f_body=\"$2\"; shift 2 ;;\n      *) fail \"ledger_line rejected field '$1'\" \"the ledger field set is closed\" \"use a field from the stable ledger order\" ;;\n    esac\n  done\n  printf '{'\n  printf '\"op\":\"%s\"' \"$(json_escape \"$_f_op\")\"\n  printf ',\"id\":\"%s\"' \"$(json_escape \"$_f_id\")\"\n  printf ',\"at\":\"%s\"' \"$(json_escape \"$_f_at\")\"\n  printf ',\"namespace\":\"%s\"' \"$(json_escape \"$_f_namespace\")\"\n  printf ',\"title\":\"%s\"' \"$(json_escape \"$_f_title\")\"\n  printf ',\"kind\":\"%s\"' \"$(json_escape \"$_f_kind\")\"\n  printf ',\"phase\":\"%s\"' \"$(json_escape \"$_f_phase\")\"\n  printf ',\"status\":\"%s\"' \"$(json_escape \"$_f_status\")\"\n  printf ',\"priority\":\"%s\"' \"$(json_escape \"$_f_priority\")\"\n  printf ',\"label\":\"%s\"' \"$(json_escape \"$_f_label\")\"\n  printf ',\"parent\":\"%s\"' \"$(json_escape \"$_f_parent\")\"\n  printf ',\"child\":\"%s\"' \"$(json_escape \"$_f_child\")\"\n  printf ',\"edge_kind\":\"%s\"' \"$(json_escape \"$_f_edge_kind\")\"\n  printf ',\"author\":\"%s\"' \"$(json_escape \"$_f_author\")\"\n  printf ',\"note_kind\":\"%s\"' \"$(json_escape \"$_f_note_kind\")\"\n  printf ',\"to\":\"%s\"' \"$(json_escape \"$_f_to\")\"\n  printf ',\"reason\":\"%s\"' \"$(json_escape \"$_f_reason\")\"\n  printf ',\"ref\":\"%s\"' \"$(json_escape \"$_f_ref\")\"\n  printf ',\"body\":\"%s\"' \"$(json_escape \"$_f_body\")\"\n  printf '}'\n}\n\nsignal_json() {\n  printf '{\"kind\":\"%s\",\"phase\":\"%s\",\"skill\":\"%s\",\"entry\":\"%s\",\"quote\":\"%s\"}' \\\n    \"$(json_escape \"$kind\")\" \\\n    \"$(json_escape \"$phase\")\" \\\n    \"$(json_escape \"$skill\")\" \\\n    \"$(json_escape \"$entry\")\" \\\n    \"$(json_escape \"$quote\")\"\n}\n\n\nledger_signal() {\n  dir=$(dirname \"$EPOCH_FILE\")\n  file=\"$dir\u002Fsignals.jsonl\"\n  mkdir -p \"$dir\" || fail \"cannot create signal directory $dir\" \"the parent of EPOCH.md is not writable\" \"choose a writable EPOCH_FILE directory and retry the same signal\"\n  printf '%s\\n' \"$signal_line\" >> \"$file\" || fail \"signal append failed for $file\" \"the signal file was not writable\" \"make $dir writable and retry the same signal\"\n}\n\nledger_create() {\n  id=$(mint_id)\n  # Empty priority matches the pasture create flag default so show agrees.\n  [ -n \"$priority\" ] || priority=medium\n  ledger_append \"$id\" \"$(ledger_line op create id \"$id\" namespace \"$NS\" title \"$title\" kind \"$kind\" phase \"$phase\" status open priority \"$priority\" author \"$AUTHOR\" ref \"$refs\" body \"$body\")\"\n  printf '%s\\n' \"$id\"\n  set -f\n  for _enc in $labels; do\n    [ -n \"$_enc\" ] || continue\n    row_decode_set \"$_enc\"\n    label=$decoded\n    ledger_append \"$id\" \"$(ledger_line op label id \"$id\" namespace \"$NS\" label \"$label\" author \"$AUTHOR\")\"\n  done\n  set +f\n}\n\nledger_label() {\n  set -f\n  for _enc in $labels; do\n    [ -n \"$_enc\" ] || continue\n    row_decode_set \"$_enc\"\n    label=$decoded\n    ledger_append \"$id\" \"$(ledger_line op label id \"$id\" namespace \"$NS\" label \"$label\" author \"$AUTHOR\")\"\n  done\n  set +f\n}\n\nledger_block() {\n  ledger_append \"$parent\" \"$(ledger_line op block id \"$parent\" namespace \"$NS\" parent \"$parent\" child \"$child\" edge_kind \"$edge_kind\" author \"$AUTHOR\")\"\n}\n\nledger_note() {\n  ledger_append \"$id\" \"$(ledger_line op note id \"$id\" namespace \"$NS\" author \"$AUTHOR\" note_kind \"$note_kind\" to \"$note_to\" body \"$note_body\")\"\n}\n\nledger_status() {\n  ledger_append \"$id\" \"$(ledger_line op status id \"$id\" namespace \"$NS\" status \"$status\" author \"$AUTHOR\")\"\n}\n\nledger_close() {\n  ledger_append \"$id\" \"$(ledger_line op close id \"$id\" namespace \"$NS\" status closed reason \"$reason\" author \"$AUTHOR\")\"\n}\n\nledger_whoami() {\n  printf '%s\\n' \"$AUTHOR\"\n}\n\nledger_files() {\n  [ -d \"$LEDGER\" ] || return 0\n  find \"$LEDGER\" -name '*.jsonl' -type f | sort\n}\n\nledger_rows() {\n  files=$(ledger_files)\n  [ -n \"$files\" ] || return 0\n  printf '%s\\n' \"$files\" | while IFS= read -r f; do\n    cat \"$f\"\n  done | awk '\nfunction take(line, pos,   i, c, out, esc, hex, v, ok, h, d) {\n  out = \"\"\n  esc = 0\n  for (i = pos + 1; i \u003C= length(line); i++) {\n    c = substr(line, i, 1)\n    if (esc) {\n      if (c == \"n\") out = out \"\\n\"\n      else if (c == \"t\") out = out \"\\t\"\n      else if (c == \"r\") out = out \"\\r\"\n      else if (c == \"u\" && i + 4 \u003C= length(line)) {\n        hex = substr(line, i + 1, 4)\n        v = 0\n        ok = 1\n        for (h = 1; h \u003C= 4; h++) {\n          d = index(\"0123456789abcdef\", tolower(substr(hex, h, 1)))\n          if (d == 0) ok = 0\n          else v = v * 16 + d - 1\n        }\n        if (ok) { out = out sprintf(\"%c\", v); i += 4 }\n        else out = out c\n      }\n      else out = out c\n      esc = 0\n    } else if (c == \"\\\\\") esc = 1\n    else if (c == \"\\\"\") { got = out; got_i = i + 1; return }\n    else out = out c\n  }\n  got = out\n  got_i = length(line) + 1\n}\nfunction parse(line,   i, n, c) {\n  delete f\n  i = 1\n  n = length(line)\n  while (i \u003C= n) {\n    c = substr(line, i, 1)\n    if (c == \"\\\"\") {\n      take(line, i)\n      key = got\n      i = got_i\n      while (i \u003C= n && substr(line, i, 1) != \"\\\"\") i++\n      if (i > n) return\n      take(line, i)\n      f[key] = got\n      i = got_i\n    } else i++\n  }\n}\nfunction add_label(id, lab,   cur, i, n, p) {\n  if (lab == \"\") return\n  cur = labels[id]\n  n = split(cur, p, \",\")\n  for (i = 1; i \u003C= n; i++) if (p[i] == lab) return\n  if (cur == \"\") labels[id] = lab\n  else labels[id] = cur \",\" lab\n}\nfunction add_edge(id, kind, child,   cur) {\n  if (child == \"\") return\n  cur = edges[id]\n  item = kind \">\" child\n  if (cur == \"\") edges[id] = item\n  else edges[id] = cur \";\" item\n}\nfunction row_encode(s,   i, c, out) {\n  out = \"\"\n  if (s == \"\") return \"\"\n  for (i = 1; i \u003C= length(s); i++) {\n    c = substr(s, i, 1)\n    if (c == \"\\\\\") out = out \"\\\\\\\\\"\n    else if (c == \"\\n\") out = out \"\\\\n\"\n    else if (c == \"\\r\") out = out \"\\\\r\"\n    else if (c == \"\\t\") out = out \"\\\\t\"\n    else if (c == \" \") out = out \"\\\\s\"\n    else if (c == \"\\037\") out = out \"\\\\037\"\n    else out = out c\n  }\n  return out\n}\n{\n  parse($0)\n  id = f[\"id\"]\n  if (id == \"\") next\n  seen[id] = 1\n  if (f[\"op\"] == \"create\") {\n    title[id] = f[\"title\"]\n    ns[id] = f[\"namespace\"]\n    body[id] = f[\"body\"]\n    phase[id] = f[\"phase\"]\n    priority[id] = f[\"priority\"]\n    ref[id] = f[\"ref\"]\n    if (st[id] == \"\") st[id] = \"open\"\n  }\n  if (f[\"namespace\"] != \"\" && ns[id] == \"\") ns[id] = f[\"namespace\"]\n  if (f[\"op\"] == \"status\" || f[\"op\"] == \"close\" || f[\"op\"] == \"create\") {\n    if (f[\"status\"] != \"\") st[id] = f[\"status\"]\n  }\n  add_label(id, f[\"label\"])\n  if (f[\"op\"] == \"block\") add_edge(id, f[\"edge_kind\"], f[\"child\"])\n  if (f[\"op\"] == \"note\") notes[id]++\n}\nEND {\n  for (id in seen) {\n    blocked = 0\n    n = split(edges[id], e, \";\")\n    for (i = 1; i \u003C= n; i++) {\n      split(e[i], p, \">\")\n      if (p[1] != \"blocked_by\") continue\n      child = p[2]\n      cst = st[child]\n      if (cst != \"closed\") blocked = 1\n    }\n    ready = 0\n    if (st[id] != \"closed\" && blocked == 0) ready = 1\n    printf \"%s\\037%s\\037%s\\037%s\\037%s\\037%s\\037%d\\037%d\\037%d\\037%s\\037%s\\037%s\\037%s\\n\", id, row_encode(ns[id]), row_encode(title[id]), st[id], row_encode(labels[id]), row_encode(edges[id]), notes[id]+0, ready, blocked, row_encode(phase[id]), row_encode(priority[id]), row_encode(ref[id]), row_encode(body[id])\n  }\n}\n'\n}\n\nledger_split() {\n  row=\"$1\"\n  old=$IFS\n  set -f\n  IFS=$(printf '\\037')\n  # shellcheck disable=SC2086\n  set -- $row\n  set +f\n  IFS=$old\n  rid=${1:-}\n  row_decode_set \"${2:-}\"; rns=$decoded\n  row_decode_set \"${3:-}\"; rtitle=$decoded\n  rstatus=${4:-}\n  row_decode_set \"${5:-}\"; rlabels=$decoded\n  row_decode_set \"${6:-}\"; redges=$decoded\n  rcomments=${7:-}; rready=${8:-}; rblocked=${9:-}\n  row_decode_set \"${10:-}\"; rphase=$decoded\n  row_decode_set \"${11:-}\"; rpriority=$decoded\n  row_decode_set \"${12:-}\"; rref=$decoded\n  row_decode_set \"${13:-}\"; rbody=$decoded\n}\n\nledger_match() {\n  if [ \"$allns\" != 1 ] && [ -n \"$NS\" ] && [ \"$rns\" != \"$NS\" ]; then\n    return 1\n  fi\n  if [ -n \"$status\" ] && [ \"$rstatus\" != \"$status\" ]; then\n    return 1\n  fi\n  if [ -n \"$label\" ]; then\n    case \",$rlabels,\" in\n      *\",$label,\"*) : ;;\n      *) return 1 ;;\n    esac\n  fi\n  if [ \"${want_blocked:-0}\" = 1 ] && [ \"$rblocked\" != 1 ]; then\n    return 1\n  fi\n  if [ \"${want_ready:-0}\" = 1 ] && [ \"$rready\" != 1 ]; then\n    return 1\n  fi\n  return 0\n}\n\nledger_emit() {\n  want_blocked=${1:-0}\n  want_ready=${2:-0}\n  rows=$(ledger_rows | sort)\n  if [ \"$fmt\" = text ]; then\n    printf '%s\\n' \"$rows\" | while IFS= read -r row; do\n      [ -n \"$row\" ] || continue\n      ledger_split \"$row\"\n      ledger_match || continue\n      printf '%s\\t%s\\t%s\\n' \"$rid\" \"$rtitle\" \"$rstatus\"\n    done\n    return 0\n  fi\n  printf '['\n  first=1\n  printf '%s\\n' \"$rows\" | while IFS= read -r row; do\n    [ -n \"$row\" ] || continue\n    ledger_split \"$row\"\n    ledger_match || continue\n    if [ \"$first\" = 1 ]; then first=0; else printf ','; fi\n    printf '{\"id\":\"%s\",\"title\":\"%s\",\"status\":\"%s\",\"namespace\":\"%s\",\"labels\":\"%s\",\"edges\":\"%s\",\"comments\":%s}' \\\n      \"$(json_escape \"$rid\")\" \"$(json_escape \"$rtitle\")\" \"$(json_escape \"$rstatus\")\" \\\n      \"$(json_escape \"$rns\")\" \"$(json_escape \"$rlabels\")\" \"$(json_escape \"$redges\")\" \\\n      \"$rcomments\"\n  done\n  printf ']\\n'\n}\n\nledger_show() {\n  rows=$(ledger_rows)\n  found=0\n  # notes and history replace the record, matching pasture's separate commands.\n  if [ \"$mode\" = notes ] || [ \"$mode\" = history ]; then\n    hit=$(printf '%s\\n' \"$rows\" | while IFS= read -r row; do\n      [ -n \"$row\" ] || continue\n      ledger_split \"$row\"\n      [ \"$rid\" = \"$id\" ] && printf yes\n    done)\n    [ -n \"$hit\" ] || fail \"no ledger record $id\" \"no line names that id under $LEDGER\" \"create the record first, or pass the id create printed\"\n    safe=$(printf '%s' \"$id\" | sed -e 's\u002F%\u002F%25\u002Fg' -e 's#\u002F#%2F#g' -e 's\u002F \u002F%20\u002Fg' -e 's\u002F:\u002F%3A\u002Fg')\n    file=\"$LEDGER\u002F$safe.jsonl\"\n    if [ \"$mode\" = history ]; then\n      cat \"$file\"\n      return 0\n    fi\n    awk '\nfunction take(line, pos,   i, c, out, esc, hex, v, ok, h, d) {\n  out = \"\"; esc = 0\n  for (i = pos + 1; i \u003C= length(line); i++) {\n    c = substr(line, i, 1)\n    if (esc) {\n      if (c == \"n\") out = out \"\\n\"\n      else if (c == \"t\") out = out \"\\t\"\n      else if (c == \"r\") out = out \"\\r\"\n      else if (c == \"u\" && i + 4 \u003C= length(line)) {\n        hex = substr(line, i + 1, 4)\n        v = 0\n        ok = 1\n        for (h = 1; h \u003C= 4; h++) {\n          d = index(\"0123456789abcdef\", tolower(substr(hex, h, 1)))\n          if (d == 0) ok = 0\n          else v = v * 16 + d - 1\n        }\n        if (ok) { out = out sprintf(\"%c\", v); i += 4 }\n        else out = out c\n      }\n      else out = out c\n      esc = 0\n    } else if (c == \"\\\\\") esc = 1\n    else if (c == \"\\\"\") { got = out; got_i = i + 1; return }\n    else out = out c\n  }\n}\n{\n  i = 1\n  while (i \u003C= length($0)) {\n    if (substr($0, i, 1) == \"\\\"\") {\n      take($0, i); key = got; i = got_i\n      while (i \u003C= length($0) && substr($0, i, 1) != \"\\\"\") i++\n      take($0, i); f[key] = got; i = got_i\n    } else i++\n  }\n  if (f[\"op\"] == \"note\") print f[\"body\"]\n  delete f\n}\n' \"$file\"\n    return 0\n  fi\n  printf '%s\\n' \"$rows\" | while IFS= read -r row; do\n    [ -n \"$row\" ] || continue\n    ledger_split \"$row\"\n    [ \"$rid\" = \"$id\" ] || continue\n    found=1\n    if [ \"$fmt\" = text ]; then\n      printf 'id: %s\\ntitle: %s\\nstatus: %s\\nnamespace: %s\\nlabels: %s\\nedges: %s\\ncomments: %s\\nphase: %s\\npriority: %s\\nref: %s\\ndescription: %s\\n' \\\n        \"$(text_field \"$rid\")\" \"$(text_field \"$rtitle\")\" \"$rstatus\" \"$(text_field \"$rns\")\" \"$(text_field \"$rlabels\")\" \"$(text_field \"$redges\")\" \"$rcomments\" \\\n        \"$(text_field \"$rphase\")\" \"$(text_field \"$rpriority\")\" \"$(text_field \"$rref\")\" \"$(text_field \"$rbody\")\"\n    else\n      printf '{\"id\":\"%s\",\"title\":\"%s\",\"status\":\"%s\",\"namespace\":\"%s\",\"labels\":\"%s\",\"edges\":\"%s\",\"comments\":%s,\"phase\":\"%s\",\"priority\":\"%s\",\"ref\":\"%s\",\"description\":\"%s\"}\\n' \\\n        \"$(json_escape \"$rid\")\" \"$(json_escape \"$rtitle\")\" \"$(json_escape \"$rstatus\")\" \\\n        \"$(json_escape \"$rns\")\" \"$(json_escape \"$rlabels\")\" \"$(json_escape \"$redges\")\" \\\n        \"$rcomments\" \\\n        \"$(json_escape \"$rphase\")\" \"$(json_escape \"$rpriority\")\" \"$(json_escape \"$rref\")\" \"$(json_escape \"$rbody\")\"\n    fi\n  done\n  printf '%s\\n' \"$rows\" | while IFS= read -r row; do\n    [ -n \"$row\" ] || continue\n    ledger_split \"$row\"\n    [ \"$rid\" = \"$id\" ] && found=1\n  done\n  # The pipeline above cannot export found. Re-scan plainly.\n  hit=$(printf '%s\\n' \"$rows\" | while IFS= read -r row; do\n    [ -n \"$row\" ] || continue\n    ledger_split \"$row\"\n    [ \"$rid\" = \"$id\" ] && printf yes\n  done)\n  [ -n \"$hit\" ] || fail \"no ledger record $id\" \"no line names that id under $LEDGER\" \"create the record first, or pass the id create printed\"\n}\n\nledger_query() {\n  if [ -n \"$tree\" ]; then\n    ledger_tree \"$tree\"\n    return 0\n  fi\n  ledger_emit \"$blocked\" 0\n}\n\nledger_ready() {\n  ledger_emit 0 1\n}\n\nledger_tree() {\n  root=\"$1\"\n  rows=$(ledger_rows)\n  printf '%s\\n' \"$rows\" | while IFS= read -r row; do\n    [ -n \"$row\" ] || continue\n    ledger_split \"$row\"\n    [ \"$rid\" = \"$root\" ] || continue\n    printf '%s\\t%s\\n' \"$rid\" \"$redges\"\n  done\n}\ncase \"$OP\" in\n  # dispatch:create\n  create)\n    kind=task\n    title=\n    phase=\n    priority=\n    labels=\n    refs=\n    body=\n    while [ \"$#\" -gt 0 ]; do\n      case \"$1\" in\n        --kind) kind=\"$2\"; shift 2 ;;\n        --kind=*) kind=\"${1#*=}\"; shift ;;\n        --title) title=\"$2\"; shift 2 ;;\n        --title=*) title=\"${1#*=}\"; shift ;;\n        --phase) phase=\"$2\"; shift 2 ;;\n        --phase=*) phase=\"${1#*=}\"; shift ;;\n        --priority) priority=\"$2\"; shift 2 ;;\n        --priority=*) priority=\"${1#*=}\"; shift ;;\n        --label) label_push \"$2\"; shift 2 ;;\n        --label=*) label_push \"${1#*=}\"; shift ;;\n        --ref)\n          if [ -z \"$refs\" ]; then refs=\"$2\"; else refs=\"$refs\n$2\"; fi\n          shift 2 ;;\n        --ref=*)\n          _val=\"${1#*=}\"\n          if [ -z \"$refs\" ]; then refs=\"$_val\"; else refs=\"$refs\n$_val\"; fi\n          shift ;;\n        --body) body=\"$2\"; shift 2 ;;\n        --body=*) body=\"${1#*=}\"; shift ;;\n        *) fail \"create rejected argument '$1'\" \"the argument is not in the create contract\" \"use --kind, --title, --phase, --priority, --label, --ref, --body\" ;;\n      esac\n    done\n    [ \"$body\" = \"@-\" ] && body=\"$(cat)\"\n    fold_refs\n    if [ \"$BACKEND\" = ledger ]; then\n      ledger_create\n      exit $?\n    fi\n      set -- task create \"$title\" --type \"$kind\" --phase \"$phase\"\n      [ -n \"$priority\" ] && set -- \"$@\" --priority \"$priority\"\n      [ -n \"$body\" ] && set -- \"$@\" --description \"$body\"\n      if [ \"$allns\" != 1 ] && [ -n \"$NS\" ]; then set -- \"$@\" --namespace \"$NS\"; fi\n      out=\"$(pb \"$@\" --format text)\" || exit $?\n      id=\"$(printf '%s\\n' \"$out\" | sed -n 's\u002F^ID: *\u002F\u002Fp' | head -1)\"\n      [ -n \"$id\" ] || fail \"create returned no task ID\" \"the backend text output had no ID line\" \"check the store named in $EPOCH_FILE and the backend help page\"\n      printf '%s\\n' \"$id\"\n      set -f\n      for _enc in $labels; do\n        [ -n \"$_enc\" ] || continue\n        row_decode_set \"$_enc\"\n        label=$decoded\n        set -- task label add \"$id\" \"$label\"\n        pb \"$@\" >\u002Fdev\u002Fnull || exit $?\n      done\n      set +f\n    ;;\n  # dispatch:label\n  label)\n    id=\"${1:-}\"\n    [ \"$#\" -gt 0 ] && shift\n    need_id \"$id\"\n    labels=\n    while [ \"$#\" -gt 0 ]; do\n      case \"$1\" in\n        --*) fail \"label rejected argument '$1'\" \"the argument is not in the label contract\" \"pass the label as a positional after the task URI\" ;;\n        *) label_push \"$1\"; shift ;;\n      esac\n    done\n    [ -n \"$labels\" ] || fail \"label needs at least one label\" \"no label operand was given\" \"pass the label after the task URI\"\n    if [ \"$BACKEND\" = ledger ]; then\n      ledger_label\n      exit $?\n    fi\n      set -f\n      for _enc in $labels; do\n        [ -n \"$_enc\" ] || continue\n        row_decode_set \"$_enc\"\n        label=$decoded\n        set -- task label add \"$id\" \"$label\"\n        pb \"$@\" || exit $?\n      done\n      set +f\n    ;;\n  # dispatch:block\n  block)\n    child=\n    edge_kind=blocked_by\n    parent=\"${1:-}\"\n    [ \"$#\" -gt 0 ] && shift\n    need_id \"$parent\"\n    while [ \"$#\" -gt 0 ]; do\n      case \"$1\" in\n        --by) child=\"$2\"; shift 2 ;;\n        --by=*) child=\"${1#*=}\"; shift ;;\n        --kind) edge_kind=\"$2\"; shift 2 ;;\n        --kind=*) edge_kind=\"${1#*=}\"; shift ;;\n        *) fail \"block rejected argument '$1'\" \"the argument is not in the block contract\" \"use --by, --kind\" ;;\n      esac\n    done\n    [ -n \"$child\" ] || fail \"block needs --by CHILD\" \"the child operand was empty\" \"pass block PARENT --by CHILD\"\n    if [ \"$BACKEND\" = ledger ]; then\n      ledger_block\n      exit $?\n    fi\n      if [ \"$edge_kind\" = blocked_by ]; then\n        set -- task dep add \"$parent\" --blocked-by \"$child\"\n        pb \"$@\" || exit $?\n      else\n        set -- task dep add \"$parent\" --target \"$child\" --kind \"$edge_kind\"\n        pb \"$@\" || exit $?\n      fi\n    ;;\n  # dispatch:note\n  note)\n    note_body=\n    note_kind=\n    note_to=\n    fmt=json\n    id=\"${1:-}\"\n    [ \"$#\" -gt 0 ] && shift\n    need_id \"$id\"\n    while [ \"$#\" -gt 0 ]; do\n      case \"$1\" in\n        --author) AUTHOR=\"$2\"; shift 2 ;;\n        --author=*) AUTHOR=\"${1#*=}\"; shift ;;\n        --body) note_body=\"$2\"; shift 2 ;;\n        --body=*) note_body=\"${1#*=}\"; shift ;;\n        --kind) note_kind=\"$2\"; shift 2 ;;\n        --kind=*) note_kind=\"${1#*=}\"; shift ;;\n        --to) note_to=\"$2\"; shift 2 ;;\n        --to=*) note_to=\"${1#*=}\"; shift ;;\n        --format) fmt=\"$2\"; shift 2 ;;\n        --format=*) fmt=\"${1#*=}\"; shift ;;\n        *) fail \"note rejected argument '$1'\" \"the argument is not in the note contract\" \"use --author, --body, --kind, --to, --format\" ;;\n      esac\n    done\n    [ \"$note_body\" = \"@-\" ] && note_body=\"$(cat)\"\n    [ -n \"$AUTHOR\" ] || fail \"note needs --author\" \"the tracker never invents an author\" \"pass the registered actor id\"\n    [ -n \"$note_kind\" ] && note_body=\"[$note_kind] $note_body\"\n    [ -n \"$note_to\" ] && note_body=\"to:$note_to $note_body\"\n    if [ \"$BACKEND\" = ledger ]; then\n      ledger_note\n      exit $?\n    fi\n      set -- task comment add \"$id\" --author \"$AUTHOR\" -- \"$note_body\"\n      pb \"$@\" || exit $?\n    ;;\n  # dispatch:show\n  show)\n    mode=show\n    mode=show\n    fmt=json\n    id=\"${1:-}\"\n    [ \"$#\" -gt 0 ] && shift\n    need_id \"$id\"\n    while [ \"$#\" -gt 0 ]; do\n      case \"$1\" in\n        --notes) mode=notes; shift ;;\n        --history) mode=history; shift ;;\n        --format) fmt=\"$2\"; shift 2 ;;\n        --format=*) fmt=\"${1#*=}\"; shift ;;\n        *) fail \"show rejected argument '$1'\" \"the argument is not in the show contract\" \"use --notes, --history, --format\" ;;\n      esac\n    done\n    if [ \"$BACKEND\" = ledger ]; then\n      ledger_show\n      exit $?\n    fi\n      if [ \"$mode\" = notes ]; then\n        set -- task comments \"$id\" --format \"$fmt\"\n        pb \"$@\" || exit $?\n      elif [ \"$mode\" = history ]; then\n        set -- task timeline \"$id\"\n        pb \"$@\" || exit $?\n      else\n        set -- task show \"$id\" --format \"$fmt\"\n        pb \"$@\" || exit $?\n      fi\n    ;;\n  # dispatch:query\n  query)\n    status=\n    label=\n    tree=\n    fmt=json\n    blocked=0\n    allns=0\n    while [ \"$#\" -gt 0 ]; do\n      case \"$1\" in\n        --status) status=\"$2\"; shift 2 ;;\n        --status=*) status=\"${1#*=}\"; shift ;;\n        --label) label=\"$2\"; shift 2 ;;\n        --label=*) label=\"${1#*=}\"; shift ;;\n        --tree) tree=\"$2\"; shift 2 ;;\n        --tree=*) tree=\"${1#*=}\"; shift ;;\n        --format) fmt=\"$2\"; shift 2 ;;\n        --format=*) fmt=\"${1#*=}\"; shift ;;\n        --blocked) blocked=1; shift ;;\n        --all-namespaces) allns=1; shift ;;\n        *) fail \"query rejected argument '$1'\" \"the argument is not in the query contract\" \"use --status, --label, --tree, --format, --blocked, --all-namespaces\" ;;\n      esac\n    done\n    [ \"$blocked\" != 1 ] || [ -z \"$status\" ] || fail \"query rejected --blocked with --status\" \"the blocked read has no --status flag, so forwarding it exits 1\" \"drop --status, or run query --status without --blocked\"\n    if [ \"$BACKEND\" = ledger ]; then\n      ledger_query\n      exit $?\n    fi\n      if [ -n \"$tree\" ]; then\n        set -- task dep tree \"$tree\"\n        pb \"$@\" || exit $?\n        exit $?\n      elif [ \"$blocked\" = 1 ]; then\n        set -- task blocked\n        [ -n \"$label\" ] && set -- \"$@\" --label \"$label\"\n        if [ \"$allns\" != 1 ] && [ -n \"$NS\" ]; then set -- \"$@\" --namespace \"$NS\"; fi\n        [ \"$allns\" = 1 ] && set -- \"$@\" --all-namespaces\n        set -- \"$@\" --format \"$fmt\"\n        pb \"$@\" || exit $?\n      else\n        set -- task list\n        [ -n \"$status\" ] && set -- \"$@\" --status \"$status\"\n        [ -n \"$label\" ] && set -- \"$@\" --label \"$label\"\n        if [ \"$allns\" != 1 ] && [ -n \"$NS\" ]; then set -- \"$@\" --namespace \"$NS\"; fi\n        [ \"$allns\" = 1 ] && set -- \"$@\" --all-namespaces\n        set -- \"$@\" --format \"$fmt\"\n        pb \"$@\" || exit $?\n      fi\n    ;;\n  # dispatch:ready\n  ready)\n    label=\n    fmt=json\n    allns=0\n    while [ \"$#\" -gt 0 ]; do\n      case \"$1\" in\n        --label) label=\"$2\"; shift 2 ;;\n        --label=*) label=\"${1#*=}\"; shift ;;\n        --format) fmt=\"$2\"; shift 2 ;;\n        --format=*) fmt=\"${1#*=}\"; shift ;;\n        --all-namespaces) allns=1; shift ;;\n        *) fail \"ready rejected argument '$1'\" \"the argument is not in the ready contract\" \"use --label, --format, --all-namespaces\" ;;\n      esac\n    done\n    if [ \"$BACKEND\" = ledger ]; then\n      ledger_ready\n      exit $?\n    fi\n      set -- task ready\n      [ -n \"$label\" ] && set -- \"$@\" --label \"$label\"\n      if [ \"$allns\" != 1 ] && [ -n \"$NS\" ]; then set -- \"$@\" --namespace \"$NS\"; fi\n      [ \"$allns\" = 1 ] && set -- \"$@\" --all-namespaces\n      set -- \"$@\" --format \"$fmt\"\n      pb \"$@\" || exit $?\n    ;;\n  # dispatch:status\n  status)\n    status=\n    id=\"${1:-}\"\n    [ \"$#\" -gt 0 ] && shift\n    need_id \"$id\"\n    while [ \"$#\" -gt 0 ]; do\n      case \"$1\" in\n        --set) status=\"$2\"; shift 2 ;;\n        --set=*) status=\"${1#*=}\"; shift ;;\n        *) fail \"status rejected argument '$1'\" \"the argument is not in the status contract\" \"use --set\" ;;\n      esac\n    done\n    [ -n \"$status\" ] || fail \"status needs --set\" \"no target status was given\" \"pass --set open, in_progress or closed\"\n    if [ \"$BACKEND\" = ledger ]; then\n      ledger_status\n      exit $?\n    fi\n      set -- task update \"$id\" --status \"$status\"\n      pb \"$@\" || exit $?\n    ;;\n  # dispatch:close\n  close)\n    reason=\n    id=\"${1:-}\"\n    [ \"$#\" -gt 0 ] && shift\n    need_id \"$id\"\n    while [ \"$#\" -gt 0 ]; do\n      case \"$1\" in\n        --reason) reason=\"$2\"; shift 2 ;;\n        --reason=*) reason=\"${1#*=}\"; shift ;;\n        *) fail \"close rejected argument '$1'\" \"the argument is not in the close contract\" \"use --reason\" ;;\n      esac\n    done\n    if [ \"$BACKEND\" = ledger ]; then\n      ledger_close\n      exit $?\n    fi\n      set -- task close \"$id\"\n      [ -n \"$reason\" ] && set -- \"$@\" --reason \"$reason\"\n      pb \"$@\" || exit $?\n    ;;\n  # dispatch:whoami\n  whoami)\n    [ -n \"$AUTHOR\" ] || fail \"whoami has no recorded author\" \\\n      \"neither the header author: line nor PASTURE_AUTHOR names an actor\" \\\n      \"set author: in $EPOCH_FILE or export PASTURE_AUTHOR to a registered actor id\"\n    if [ \"$BACKEND\" = ledger ]; then\n      ledger_whoami\n      exit $?\n    fi\n      set -- task agents show \"$AUTHOR\"\n      pb \"$@\" >\u002Fdev\u002Fnull || exit $?\n      printf '%s\\n' \"$AUTHOR\"\n    ;;\n  # dispatch:signal\n  signal)\n    kind=\n    phase=\n    skill=\n    entry=\n    quote=\n    while [ \"$#\" -gt 0 ]; do\n      case \"$1\" in\n        --kind) kind=\"$2\"; shift 2 ;;\n        --kind=*) kind=\"${1#*=}\"; shift ;;\n        --phase) phase=\"$2\"; shift 2 ;;\n        --phase=*) phase=\"${1#*=}\"; shift ;;\n        --skill) skill=\"$2\"; shift 2 ;;\n        --skill=*) skill=\"${1#*=}\"; shift ;;\n        --entry) entry=\"$2\"; shift 2 ;;\n        --entry=*) entry=\"${1#*=}\"; shift ;;\n        --quote) quote=\"$2\"; shift 2 ;;\n        --quote=*) quote=\"${1#*=}\"; shift ;;\n        *) fail \"signal rejected argument '$1'\" \"the argument is not in the signal contract\" \"use --kind, --phase, --skill, --entry, --quote\" ;;\n      esac\n    done\n    [ -n \"$kind\" ] || fail \"signal needs --kind\" \"the kind operand was empty\" \"pass --kind with one registered signal kind\"\n    [ -n \"$phase\" ] || fail \"signal needs --phase\" \"the phase operand was empty\" \"pass --phase with one registered phase name\"\n    [ -n \"$skill\" ] || fail \"signal needs --skill\" \"the skill operand was empty\" \"pass --skill with the skill directory name\"\n    [ -n \"$entry\" ] || fail \"signal needs --entry\" \"the entry operand was empty\" \"pass --entry with the record that holds the interaction\"\n    [ -n \"$quote\" ] || fail \"signal needs --quote\" \"the quote operand was empty\" \"pass --quote with a short verbatim fragment\"\n    case \"$kind\" in\n      frustration|positive-signal|satisfaction|confusion|correction) ;;\n      *) fail \"signal rejected kind '$kind'\" \"the kind is not in the closed set, so a later count would treat an unregistered label as data\" \"pass --kind with one of: frustration, positive-signal, satisfaction, confusion, correction\" \"signal operation in the tracker adapter, before the backend call\" ;;\n    esac\n    case \"$phase\" in\n      request|elicit|propose|review|plan-review|ratify|handoff|impl-plan|worker-slices|code-review|impl-uat|landing) ;;\n      *) fail \"signal rejected phase '$phase'\" \"the phase is not in the registered vocabulary, so the record would not join the phase it names\" \"pass --phase with one of: request, elicit, propose, review, plan-review, ratify, handoff, impl-plan, worker-slices, code-review, impl-uat, landing\" \"signal operation in the tracker adapter, before the backend call\" ;;\n    esac\n    signal_line=$(signal_json)\n    if [ \"$BACKEND\" != ledger ]; then\n      FEEDBACK_ID=$(field feedback)\n      [ -n \"$FEEDBACK_ID\" ] || fail \"signal has no feedback task\" \"EPOCH.md has no feedback: line, so a note would run with an empty id and record nothing\" \"the lookup rule is in Signal Capture; do not create a task from the signal operation\"\n      [ -n \"$AUTHOR\" ] || fail \"signal needs a recorded author\" \"the header author: line and PASTURE_AUTHOR are both empty\" \"set author: in $EPOCH_FILE or export PASTURE_AUTHOR to a registered actor id\"\n    fi\n    if [ \"$BACKEND\" = ledger ]; then\n      ledger_signal\n      exit $?\n    fi\n      set -- task comment add \"$FEEDBACK_ID\" --author \"$AUTHOR\" -- \"$signal_line\"\n      pb \"$@\" || exit $?\n    ;;\n  *)\n    fail \"unknown operation '$OP'\" \"the operation set is closed\" \\\n      \"use one of create, label, block, note, show, query, ready, status, close, whoami, signal; run the probe if the header is missing\"\n    ;;\nesac\n~~~\n\u003C!-- \u002Fpasture:asset rec -->\n\n\n## Signal Capture\n\nRecord the user's friction or approval when it appears, so a later project-level skill can distill it. Capture is a side effect of work already happening: it never prompts the user and never blocks a phase.\n\n### Closed kind list\n\nThe kind is a closed list. Use exactly one of:\n\n- `frustration`: the user shows annoyance, repeats a complaint, or rejects the interaction itself.\n- `positive-signal`: the user endorses a direction or a result before a gate asks.\n- `satisfaction`: the user accepts a gate and says the result is what they wanted.\n- `confusion`: the user asks what a term means, misreads a question, or answers a different question.\n- `correction`: the user corrects a fact, a requirement, or an earlier answer.\n\n### One feedback task per epoch\n\nOn the pasture backend, the epoch creates one feedback task at epoch start and writes `feedback: \u003Ctask URI>` into `.agents.local\u002Fpasture\u002FEPOCH.md`. A second run reads the `feedback:` line and does not create another task. On the ledger backend, create nothing and record nothing: there is no feedback task and no `feedback:` line. No other skill creates a feedback task. If the pasture line is absent, the signal operation fails and records nothing; do not create a task to fill the gap.\n\n### Line format and recording\n\nWrite one JSON object per signal. The fields, in this order, are kind, phase, skill, entry and quote.\n\n~~~\n{\"kind\":\"\u003Ckind>\",\"phase\":\"\u003Cphase>\",\"skill\":\"\u003Cskill>\",\"entry\":\"\u003Centry-ref>\",\"quote\":\"\u003Cshort verbatim quote>\"}\n~~~\n\n- `kind`: one of the registered kinds above, exactly.\n- `phase`: one of request, elicit, propose, review, plan-review, ratify, handoff, impl-plan, worker-slices, code-review, impl-uat, landing.\n- `skill`: the skill that recorded it, named exactly as the skill directory.\n- `entry`: the record that holds the interaction, such as a task URI plus the question or section.\n- `quote`: a short verbatim fragment, max 20 words. Never paraphrase or tidy it.\n\nRecord the signal with one call. Do not name a backend command in this section.\n\n~~~bash\n\"${EPOCH_REC}\" signal --kind confusion --phase elicit --skill user-elicit --entry '\u003Ctask URI>#end-vision' --quote 'what does arm mean here'\n~~~\n\nThe adapter records that JSON object. An empty feedback id fails in the adapter and records nothing. Write the call in the same turn as the work. Do not ask permission to record. Do not wait for the write before you continue the phase.\n\nIf the write fails, follow the tracker write-failure rule: retry, keep the recorded backend, and surface the pending line at the next gate. A failed record never blocks the phase and never prompts the user.\n\n## Core Principles\n\n1. **AUDIT TRAIL PRESERVATION** — Never delete or destroy information, labels, or tasks\n2. **DEPENDENCY CHAINING** — Each task blocks its predecessor: `\"${EPOCH_REC}\" block \"${PARENT_URI}\" --by \"${CHILD_URI}\"`\n3. **USER ENGAGEMENT** — URE and UAT at multiple checkpoints\n4. **CONSENSUS REQUIRED** — All 3 reviewers must ACCEPT before proceeding\n5. **EAGER SEVERITY TREE** — Code reviews (Phase 10) always create 3 severity groups (BLOCKER, IMPORTANT, MINOR); empty groups closed immediately. Only an open BLOCKER gates a review wave; open findings converge at the end\n6. **FOLLOW-UP EPIC** — Fed ONLY by user-DEFER'd UAT items (Phase 5\u002F11), never by any review severity; the Supervisor creates it from those DEFER'd items\n7. **RIDE THE WAVE** — Phases 8-10 form one continuous cycle: Explore subagents (P8), workers implement (P9), ephemeral reviewers review (P10), iterating review→fix→re-review up to the chosen review-effort budget; only an open BLOCKER gates a wave, and after the last wave convergence queries every open pasture:finding toward 0\u002F0\u002F0 within maxRounds; on exhaustion, surface outstanding findings to the user at a gate; workers persist throughout\n\n## The 12-Phase Workflow\n\n```\nPhase 1:  pasture:p1-user       -> REQUEST (classify, research, explore)\n            s1_1-classify -> s1_2-research || s1_3-explore\nPhase 2:  pasture:p2-user       -> ELICIT (URE survey) + URD (single source of truth)\n            s2_1-elicit -> s2_2-urd\nPhase 3:  pasture:p3-plan       -> PROPOSAL-N (architect proposes)\nPhase 4:  pasture:p4-plan       -> REVIEW (3 parallel reviewers, ACCEPT\u002FREVISE)\nPhase 5:  pasture:p5-user       -> Plan UAT (user acceptance test)\nPhase 6:  pasture:p6-plan       -> Ratification (supersede old proposals)\nPhase 7:  pasture:p7-plan       -> Handoff (architect -> supervisor)\nPhase 8:  implementation plan -> IMPL_PLAN (supervisor decomposes into slices; Explore subagents)\nPhase 9:  worker slices       -> one slice per worker (parallel workers; Ride the Wave — workers persist for review)\nPhase 10: code review         -> reviewers review every slice in the wave; only an open BLOCKER gates a wave, then convergence at the end, else surface to the user\nPhase 11: pasture:p11-user    -> Implementation UAT\nPhase 12: landing             -> commit, push, hand off\n```\n\n### Phase 1 Expanded: REQUEST\n\nPhase 1 has 3 sub-steps:\n\n| Sub-step | Label | Description | Parallel? |\n|----------|-------|-------------|-----------|\n| s1_1-classify | `pasture:p1-user:s1_1-classify` | Capture and classify request along 4 axes (scope, complexity, risk, domain novelty) | Sequential (first) |\n| s1_2-research | `pasture:p1-user:s1_2-research` | Find domain standards, prior art | Parallel with s1_3 |\n| s1_3-explore | `pasture:p1-user:s1_3-explore` | Codebase exploration for integration points | Parallel with s1_2 |\n\nAfter classification, user confirms research depth. Then s1_2 and s1_3 run in parallel.\n\n## Starting an Epoch\n\n**Step 0: detect the tracker backend before the first write.**\n\nWrite the probe and the adapter by running the installer below against this skill file. It writes `.agents.local\u002Fpasture\u002Fprobe.sh` and `.agents.local\u002Fpasture\u002Frec` from the shipped fences in the Tracker Adapter section. Set `SKILL_FILE` to this file, run the installer, then the probe. Write `EPOCH.md` from the probe output before any task is created. The orchestrator runs this probe once; subagents read the header and never re-probe. A missing git origin is not an error backend. For pasture, the probe does not derive a namespace: record `namespace:` only to pin one, otherwise the adapter omits `--namespace` and Pasture derives it. For ledger, the probe does derive a namespace: it prints NS from derive_namespace, and every unpinned ledger write calls that same function. Copy NS to `namespace:` only to override it. A missing or unregistered author is a warning (`WARN=no-author`), not an error backend.\n\n```bash\n# pasture:install-tracker\ninstall_tracker_assets() {\n  skill=$1\n  dest=${2:-.agents.local\u002Fpasture}\n  if [ -z \"$skill\" ] || [ ! -f \"$skill\" ]; then\n    printf 'install: no skill file\\n  why: install_tracker_assets needs the rendered skill path\\n  where: Starting an Epoch install step\\n  fix: set SKILL_FILE to this skill and pass it as the first argument\\n' >&2\n    return 1\n  fi\n  mkdir -p \"$dest\" || return 1\n  awk -v dest=\"$dest\" '\n    $0 == \"\u003C!-- pasture:asset probe -->\" { kind = \"probe\"; next }\n    $0 == \"\u003C!-- pasture:asset rec -->\" { kind = \"rec\"; next }\n    $0 ~ \u002F^\u003C!-- \\\u002Fpasture:asset \u002F { kind = \"\"; grab = 0; next }\n    kind != \"\" && $0 == \"~~~sh\" { grab = 1; next }\n    kind != \"\" && grab == 1 && $0 == \"~~~\" { grab = 0; next }\n    grab == 1 && kind == \"probe\" { print > (dest \"\u002Fprobe.sh\"); next }\n    grab == 1 && kind == \"rec\" { print > (dest \"\u002Frec\"); next }\n  ' \"$skill\"\n  if [ ! -s \"$dest\u002Fprobe.sh\" ] || [ ! -s \"$dest\u002Frec\" ]; then\n    printf 'install: the skill did not emit probe.sh and rec\\n  why: the Tracker Adapter fences were missing or empty\\n  where: %s\\n  fix: regenerate the skill so it contains pasture:asset probe and pasture:asset rec\\n' \"$skill\" >&2\n    return 1\n  fi\n  chmod +x \"$dest\u002Fprobe.sh\" \"$dest\u002Frec\"\n}\n# \u002Fpasture:install-tracker\ninstall_tracker_assets \"$SKILL_FILE\" .agents.local\u002Fpasture\nsh .agents.local\u002Fpasture\u002Fprobe.sh\n# Then write .agents.local\u002Fpasture\u002FEPOCH.md from the keys named below. Do not\n# copy a derived NS into namespace: unless you mean to pin it. In every shell\n# session resolve the entry point:\nEPOCH_REC=$(sed -n 's\u002F^rec: \u002F\u002Fp' .agents.local\u002Fpasture\u002FEPOCH.md | tail -1)\n```\n\nIf the probe prints `BACKEND=error`, stop and report before the first mutation: the store exists but cannot be used. If it prints `BACKEND=ledger`, record `backend:`, `ledger:`, `rec:` and `probed_at:` from the output and continue; the adapter writes one JSONL line per mutation under that ledger and derives the namespace with the same function that printed NS. Offer a gitignore entry; never require one. If it prints `BACKEND=pasture`, record `backend:`, `db:`, `author:`, `rec:` and `probed_at:` from the output. Record `namespace:` only to pin one; the adapter then passes `--namespace`, and that pin wins over Pasture's derivation.\n\n**Step 0b: create the epoch feedback task once, and only on the pasture backend.** This is the only create. On the pasture backend, create the task once and record `feedback:`. On the ledger backend, create nothing and record nothing. The lookup rule, including who may create the task, is in Signal Capture.\n\n```bash\nBACKEND=$(sed -n 's\u002F^backend: \u002F\u002Fp' .agents.local\u002Fpasture\u002FEPOCH.md | tail -1)\nif [ \"$BACKEND\" = pasture ]; then\n  FEEDBACK_ID_URI=$(sed -n 's\u002F^feedback: \u002F\u002Fp' .agents.local\u002Fpasture\u002FEPOCH.md | tail -1)\n  if [ -z \"$FEEDBACK_ID_URI\" ]; then\n    FEEDBACK_ID_URI=$(\"${EPOCH_REC}\" create --kind task --title \"FEEDBACK: epoch signals\" --phase unscoped)\n    printf 'feedback: %s\\n' \"$FEEDBACK_ID_URI\" >> .agents.local\u002Fpasture\u002FEPOCH.md\n  fi\nfi\n```\n\n**Option 1: Manual Task Creation**\n```bash\n# Phase 1: Capture user request\nREQUEST_ID_URI=$(\"${EPOCH_REC}\" create --kind task --title \"REQUEST: {{feature}}\" --phase request \\\n  --body \"{{verbatim user request}}\")\n\"${EPOCH_REC}\" label \"$REQUEST_ID_URI\" pasture:p1-user:s1_1-classify\n\"${EPOCH_REC}\" note \"$REQUEST_ID_URI\" --author pasture-system\u002F00000000-0000-0000-0000-000000000000 --body \"Requested role (not an assignment): architect\"\n\n# Then proceed through phases manually\n```\n\nUse the explicit task graph; no molecule subsystem is required.\n\n## Phase Transitions\n\nEach phase creates a task and chains dependencies. Cross-references use description frontmatter instead of peer-reference commands.\n\n```bash\n# After Phase 1 creates \"${REQUEST_ID_URI}\"\n\"${EPOCH_REC}\" block \"${REQUEST_ID_URI}\" --by \"${ELICIT_ID_URI}\"    # REQUEST blocked by ELICIT\n\n# After Phase 2 creates \"${ELICIT_ID_URI}\" and URD\n\"${EPOCH_REC}\" block \"${ELICIT_ID_URI}\" --by \"${PROPOSAL_ID_URI}\"   # ELICIT blocked by PROPOSAL\n# URD linked via frontmatter in its description:\n#   references:\n#     request: \"${REQUEST_ID_URI}\"\n#     elicit: \"${ELICIT_ID_URI}\"\n\n# After Phase 5 (UAT) and Phase 6 (ratify), update URD\n\"${EPOCH_REC}\" note \"${URD_ID_URI}\" --author pasture-system\u002F00000000-0000-0000-0000-000000000000 --body \"UAT results: {{summary}}\"\n\"${EPOCH_REC}\" note \"${URD_ID_URI}\" --author pasture-system\u002F00000000-0000-0000-0000-000000000000 --body \"Ratified: scope confirmed as {{summary}}\"\n```\n\n## Follow-up Epic\n\n**Trigger:** UAT (Phase 5 or 11) produces one or more **user-DEFER'd items**.\nThe FOLLOWUP epic is fed ONLY by those DEFER'd UAT items — **never** by any review severity (review findings converge at the end; close rule: C-finding-lifecycle).\n**Owner:** Supervisor creates the follow-up epic.\n\n```bash\nFOLLOWUP_EPIC_ID_URI=$(\"${EPOCH_REC}\" create --kind epic --title \"FOLLOWUP: User-deferred improvements from UAT\" --phase unscoped --priority 3 \\\n  --body \"---\nreferences:\n  request: \"${REQUEST_ID_URI}\"\n  uat: \"${UAT_ID_URI}\"\n---\nAggregated user-DEFER'd items from UAT (Phase 5\u002F11).\")\n\"${EPOCH_REC}\" label \"$FOLLOWUP_EPIC_ID_URI\" pasture:epic-followup\n```\n\n### Follow-up lifecycle (same protocol, FOLLOWUP_* prefix)\n\nThe follow-up epic runs the same protocol phases with FOLLOWUP_* prefixed task types:\n\n```\nFOLLOWUP → FOLLOWUP_URE → FOLLOWUP_URD → FOLLOWUP_PROPOSAL-1 → FOLLOWUP_IMPL_PLAN → the follow-up slice\n```\n\n- **FOLLOWUP_URE**: Scoping URE with user to determine which DEFER'd items to address\n- **FOLLOWUP_URD**: Requirements doc for follow-up scope (references original URD)\n- **FOLLOWUP_PROPOSAL-{N}**: Proposal accounting for original URD + FOLLOWUP_URD + the DEFER'd items\n- **FOLLOWUP_IMPL_PLAN**: Supervisor decomposes follow-up into slices\n- **the follow-up slice**: Each slice implements the DEFER'd-item work decomposed into leaf tasks\n\nSee `\u002Fpasture:supervisor` and `\u002Fpasture:impl-review` for full creation commands.\n\n## EAGER Severity Tree (Phase 10)\n\nCode reviews ALWAYS create ONE severity trio (3 group tasks) per wave, per axis, per round, even if empty:\n\n```bash\n# Create all 3 severity groups immediately (EAGER, not lazy)\nBLOCKER_ID_URI=$(\"${EPOCH_REC}\" create --kind task --title \"BLOCKER\" --phase code_review \\\n   --body \"Severity group for this review round; close only if empty\")\n\"${EPOCH_REC}\" label \"$BLOCKER_ID_URI\" pasture:severity:blocker\nIMPORTANT_ID_URI=$(\"${EPOCH_REC}\" create --kind task --title \"IMPORTANT\" --phase code_review \\\n   --body \"Severity group for this review round; close only if empty\")\n\"${EPOCH_REC}\" label \"$IMPORTANT_ID_URI\" pasture:severity:important\nMINOR_ID_URI=$(\"${EPOCH_REC}\" create --kind task --title \"MINOR\" --phase code_review \\\n   --body \"Severity group for this review round; close only if empty\")\n\"${EPOCH_REC}\" label \"$MINOR_ID_URI\" pasture:severity:minor\n\n# Empty groups are closed immediately\n\"${EPOCH_REC}\" close \"${IMPORTANT_ID_URI}\"\n\"${EPOCH_REC}\" close \"${MINOR_ID_URI}\"\n```\n\n**Dual-parent BLOCKER:** Bind BLOCKER_FINDING_ID_URI from the finding task's create output before running this fence. BLOCKER findings block both the severity group AND the slice:\n```bash\n\"${EPOCH_REC}\" block \"${BLOCKER_ID_URI}\" --by \"${BLOCKER_FINDING_ID_URI}\"\n\"${EPOCH_REC}\" block \"${SLICE_ID_URI}\" --by \"${BLOCKER_FINDING_ID_URI}\"\n```\n\nSee [reviewer-review-code](..\u002Freviewer-review-code\u002FSKILL.md) for severity definitions.\n\n## Tracking Progress\n\n```bash\n# View dependency chain\n\"${EPOCH_REC}\" query --tree \"${LATEST_TASK_ID_URI}\"\n\n# Check blocked work\n\"${EPOCH_REC}\" query --blocked\n\n# See all epoch tasks by phase\n\"${EPOCH_REC}\" query --label=\"pasture:p1-user:s1_1-classify\"    # REQUEST tasks\n\"${EPOCH_REC}\" query --label=\"pasture:p2-user:s2_1-elicit\"      # ELICIT tasks\n\"${EPOCH_REC}\" query --label=\"pasture:p3-plan:s3-propose\"        # PROPOSAL tasks\n\"${EPOCH_REC}\" query --phase worker_slices          # Implementation slices\n```\n\n## Skills to Invoke\n\nEach phase transition MUST include an explicit `Skill(...)` invocation directive. When launching agents for a phase, the prompt MUST tell the agent to call the corresponding skill as its first action.\n\n| Phase | Skill | Invocation Directive |\n|-------|-------|---------------------|\n| 1 (REQUEST: classify, research, explore) | `\u002Fpasture:user-request` | `Skill(\u002Fpasture:user-request)` |\n| 2 (ELICIT + URD) | `\u002Fpasture:user-elicit` | `Skill(\u002Fpasture:user-elicit)` |\n| 3-6 (PROPOSAL, REVIEW, UAT, RATIFY) | `\u002Fpasture:architect` | `Skill(\u002Fpasture:architect)` |\n| 5, 11 (UAT) | `\u002Fpasture:user-uat` | `Skill(\u002Fpasture:user-uat)` |\n| 7 (HANDOFF) | `\u002Fpasture:architect-handoff` | Architect calls `Skill(\u002Fpasture:architect-handoff)` after ratification |\n| 8-10 (IMPL_PLAN, SLICES, CODE REVIEW) | `\u002Fpasture:supervisor` | Supervisor prompt MUST start with `Skill(\u002Fpasture:supervisor)` |\n| 12 (LANDING) | Manual git commit and push | N\u002FA |\n\n**CRITICAL — interviewing phases:** The interviewing phases MUST explicitly invoke their skill. Do **not** improvise interview questions:\n- **Phase 2 (URE):** invoke `Skill(\u002Fpasture:user-elicit)` — skipping it produces low-quality elicitation.\n- **Phases 5 & 11 (UAT):** invoke `Skill(\u002Fpasture:user-uat)` — it drives the FIX-NOW, DEFER, or WON'T-DO disposition and demonstrative examples.\n\n**CRITICAL:** When the architect hands off to the supervisor (Phase 7 → 8), the supervisor launch prompt MUST:\n1. Start with `Skill(\u002Fpasture:supervisor)` — without this, the supervisor skips role-critical procedures\n2. Include all Pasture task IDs (REQUEST, URD, RATIFIED PROPOSAL, HANDOFF)\n3. Include the HANDOFF Pasture task ID — the handoff is authored in that task body (no filesystem path)\n\n## Never Delete Policy\n\n**DO:** Add labels, add comments, update status\n**DON'T:** Close tasks prematurely, delete tasks, remove labels\n\n```bash\n# Correct: Add ratify label\n\"${EPOCH_REC}\" label \"${PROPOSAL_ID_URI}\" pasture:p6-plan:s6-ratify\n\"${EPOCH_REC}\" note \"${PROPOSAL_ID_URI}\" --author pasture-system\u002F00000000-0000-0000-0000-000000000000 --body \"RATIFIED: All reviewers ACCEPT\"\n\n# Wrong: Don't close\n# \"${EPOCH_REC}\" close \"${PROPOSAL_ID_URI}\"  # NEVER DO THIS\n```\n\u003C!-- END GENERATED FROM pasture schema -->\n",{"name":5,"description":7},"main","5813de22110ddc609b465a982b4811646ff72d18","internal\u002Ftarget\u002Fclaudecode\u002Fassets\u002Fpasture-skills\u002Fskills\u002Fepoch",0,"https:\u002F\u002Fgithub.com\u002Fdayvidpham\u002Fpasture","https:\u002F\u002Fgithub.com\u002Fdayvidpham","dhpham.com","2026-11-11 16:48:48",{"source":8,"skillName":5,"commands":45,"prompt":55,"zipAvailable":56},[46,49,52],{"tool":47,"command":48},"npx","npx skills add dayvidpham\u002Fpasture --skill epoch",{"tool":50,"command":51},"bunx","bunx skills add dayvidpham\u002Fpasture --skill epoch",{"tool":53,"command":54},"pnpm","pnpm dlx skills add dayvidpham\u002Fpasture --skill epoch","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fdayvidpham\u002Fpasture\u002Fblob\u002Fmain\u002Finternal\u002Ftarget\u002Fclaudecode\u002Fassets\u002Fpasture-skills\u002Fskills\u002Fepoch\u002FSKILL.md",true,[],[59,67,82,92,104,115],{"skillNo":60,"name":61,"nameZh":6,"description":62,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":63,"categories":64,"keywords":65,"publishedAt":31},"01M6DAP7307B3JVZX5DSNAE08Y","fixture","Isolated fixture skill","https:\u002F\u002Fgithub.com\u002Fdayvidpham\u002Fpasture\u002Fblob\u002Fmain\u002Ftestdata\u002Finstall\u002Fglobal\u002Fclaude-code\u002Fnative-marketplace\u002Ffixture-plugin\u002Fskills\u002Ffixture\u002FSKILL.md",[],[61,66],"fixture skill",{"skillNo":68,"name":69,"nameZh":6,"description":70,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":71,"categories":72,"keywords":73,"publishedAt":31},"01M6DAP48A4K365N83NBG0GFVC","worker","Vertical slice implementer (full production code path)","https:\u002F\u002Fgithub.com\u002Fdayvidpham\u002Fpasture\u002Fblob\u002Fmain\u002Fskills\u002Fworker\u002FSKILL.md",[],[69,74,75,28,76,77,78,79,80,81],"complete","vertical slice","implementer","Production","path","code","slice","implement",{"skillNo":83,"name":84,"nameZh":6,"description":85,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":86,"categories":87,"keywords":88,"publishedAt":31},"01M6DAP18QJM7P7N31TYGNJBCP","worker-implement","Implement assigned vertical slice following TDD layers","https:\u002F\u002Fgithub.com\u002Fdayvidpham\u002Fpasture\u002Fblob\u002Fmain\u002Fskills\u002Fworker-implement\u002FSKILL.md",[],[89,69,81,80,90,75,91],"worker implement","layers","tdd",{"skillNo":93,"name":94,"nameZh":6,"description":95,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":96,"categories":97,"keywords":98,"publishedAt":31},"01M6DANYHC7P63RFEN26F70MC2","worker-complete","Signal slice completion after quality gates pass","https:\u002F\u002Fgithub.com\u002Fdayvidpham\u002Fpasture\u002Fblob\u002Fmain\u002Fskills\u002Fworker-complete\u002FSKILL.md",[],[74,99,69,100,80,101,102,103],"worker complete","signal","gates","quality","quality gates",{"skillNo":105,"name":106,"nameZh":6,"description":107,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":108,"categories":109,"keywords":110,"publishedAt":31},"01M6DANWZ31TRP4NN4T1BFYKSR","worker-blocked","Report a blocker to supervisor via Pasture","https:\u002F\u002Fgithub.com\u002Fdayvidpham\u002Fpasture\u002Fblob\u002Fmain\u002Fskills\u002Fworker-blocked\u002FSKILL.md",[],[111,69,112,113,114],"worker blocked","via","supervisor","report",{"skillNo":116,"name":117,"nameZh":6,"description":118,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":119,"categories":120,"keywords":124,"publishedAt":31},"01M6DANSVDBXBFH93B6EX3H2QK","user-uat","User Acceptance Testing with demonstrative examples","https:\u002F\u002Fgithub.com\u002Fdayvidpham\u002Fpasture\u002Fblob\u002Fmain\u002Fskills\u002Fuser-uat\u002FSKILL.md",[121],{"categoryNo":122,"categoryName":123,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量",[125,126,127,128,129,130,131,132,133],"uat","user uat","user","testing","acceptance testing","with","user acceptance testing","examples","acceptance"]