diff --git a/.agents/GEMINI.md b/.agents/GEMINI.md index 7a77eb8e..6a2f5d42 100644 --- a/.agents/GEMINI.md +++ b/.agents/GEMINI.md @@ -6,10 +6,10 @@ If `AR_SPAWN_ROLE` is set, or your first user message is a role brief from an orchestrating agent: **ignore this notice entirely — your brief is your session start.** -Otherwise you are the developer-facing session, i.e. the **orchestrator**: read +Otherwise you are the developer-facing session, i.e. the **architect**: read `/ar-coordination/AGENTS.md` and treat those rules as workspace instructions, then run your lifecycle at -`skills/l-01-agent-lifecycles/roles/orchestrator.md` — trust checkpoint before +`skills/l-01-agent-lifecycles/roles/architect.md` — trust checkpoint before relying on memory, `read_ar_files` (paired source+onboarding) until the build decision, retrieval-strategy tally as evidence, notify-and-stop at every developer hand-off. diff --git a/.agents/skills/c-05-create-or-update-onboarding-files/SKILL.md b/.agents/skills/c-05-create-or-update-onboarding-files/SKILL.md index 31ea0090..d8e39a82 100644 --- a/.agents/skills/c-05-create-or-update-onboarding-files/SKILL.md +++ b/.agents/skills/c-05-create-or-update-onboarding-files/SKILL.md @@ -16,6 +16,16 @@ Planning stays in task artifacts. This package defines how onboarding itself is Before maintaining onboarding, use `c-08-ar-coordination-context-resolver` to resolve the target repository's active coordination context. It must use the `Domain Documentation` category declared in the resolved `system/sources.md` for the onboarding slice being maintained, rather than assuming that adjacent onboarding alone is sufficient or hard-coding one particular documentation system into the skill. +**Seat routing:** in the manager -> builder -> reviewer -> curator chain (`l-01-agent-lifecycles` +`roles/curator.md`), onboarding create/update duty during leaf work belongs to the curator seat, not +the builder — the builder produces code and a turn report only. The curator runs this skill's +workflows from a change set (landed diff), the leaf task doc, and notes/ fed to it by the manager, +and routes each item to the right onboarding home (a concrete sidecar or the governing overview +whose subject it is; the L3 Operational-Notes target is last-resort only, never a default). The +strict 1-to-1 source mapping, governing-overview links, and metadata rules below are unchanged by +this routing — only the writing seat moved. A solo flat session with no separate curator seat runs +this skill itself, exactly as before. + When onboarding maintenance is happening inside a `c-09-git-worktree-manager` worktree, check `worktree_status` before starting memory entries. If its `freshness` block reports that the recorded code or memory source branch moved, diff --git a/.agents/skills/c-09-git-worktree-manager/SKILL.md b/.agents/skills/c-09-git-worktree-manager/SKILL.md index 692dec19..58a87184 100644 --- a/.agents/skills/c-09-git-worktree-manager/SKILL.md +++ b/.agents/skills/c-09-git-worktree-manager/SKILL.md @@ -1,6 +1,6 @@ --- name: c-09-git-worktree-manager -description: "Create, attach to, report on, integrate, finalize, and clean up Agents Remember worktree-backed tasks while preserving human approval gates and external-memory compatibility." +description: "Create, attach to, report on, integrate, finalize, and clean up Agents Remember worktree-backed tasks while preserving delegated/human approval gates and external-memory compatibility." --- # c-09-git-worktree-manager Git Worktree Manager @@ -75,11 +75,14 @@ The intended order is: integration branch and integrates back into it. For a nested master, create the child integration branch from the parent integration branch. 6. choose or review the task slug and workflow variables -7. **hand off** the **Worktree Intent Gate** (notify-and-continue) for explicit developer approval +7. establish the applicable worktree-start authority: for standalone/new work, hand off the + **Worktree Intent Gate** for explicit developer approval; for subordinate leaves/edges inside an + accepted orchestrated series, record the accepted-series authority and continue without a new + developer hand-off 8. create the durable task wrapper when one is needed 9. request the `worktree_start` MCP tool only after the task identity is stable, the correct landable `source_branch` is selected, external memory is clean, and - the developer approved the intent packet + the applicable authority has been recorded The Worktree Intent Gate must name: @@ -97,7 +100,7 @@ visible: leaf work branches integrate into the pushable integration/source branc recorded by their enclosure; protected targets are reached later through the repo's PR flow. -Run the applicable dry-run/preflight first, then **hand off**: call +For developer-gated starts, run the applicable dry-run/preflight first, then **hand off**: call `lifecycle_turn_end_notification(summary={…the intent packet + the approve/revise ask…})` as the **last tool call**, then deliver the intent packet as your final prose and **STOP / end your turn**. The notification sets the `awaiting-developer` lifecycle state, surfaces a @@ -106,6 +109,10 @@ dashboard or in the leaf's attached chat; the **first AR tool call of your next lifecycle (`running`), clears the attention item, and proceeds to `worktree_start` — you send no explicit `lifecycle_resume`. +For subordinate orchestrated-series starts, do the same dry-run/preflight, record the accepted +planner/series authority in the task decision log or worktree intent note, and continue. Do not add +a developer stop for every leaf worktree. + Parked fallback: the block-and-wait `lifecycle_gate` junction (plus the operator inbox and dashboard GateResponder) still works if you deliberately raise it for a durable, developer-attributed, mutation-blocking record: @@ -190,19 +197,27 @@ quality gate, memory content commit, ledger update, and ledger commit. For worktree-backed tasks, pass the leaf enclosure `series-contract.md` to `worktree_closeout_preview` / `worktree_closeout_apply`. The apply step records -the developer's explicit commit approval in the contract and updates the -contract closeout state after the code, memory, and ledger commits are created. +the applicable closeout authority in the contract and updates the contract closeout state after the +code, memory, and ledger commits are created. Worktree closeout stops if the recorded code or external-memory source branch moved since task start. ## Integration -Integration is explicitly human-gated and runs only after closeout completed. It lands the closed task branches back onto the recorded source branches and records the landed commits separately from the closeout commits. Orchestrated-run carve-out (ruled 2026-07-06): dependency-ordered leaf→master and master→super integrations ride the series' standing approval (the developer's portfolio-gate approval recorded in the planner master) — the developer hand-off concentrates at the super PR/carry-over gate per the `l-01-agent-lifecycles` loop/orchestrator doctrine; a raised durable `integration-approval` gate still awaits the developer. +Integration runs only after closeout completed and is authority-gated by context. It lands the +closed task branches back onto the recorded source branches and records the landed commits +separately from the closeout commits. In an accepted orchestrated run, dependency-ordered +leaf→master and master→super integrations ride the series' standing approval (the developer's +portfolio-gate approval recorded in the planner master) — the developer hand-off concentrates at +the super PR/carry-over gate per the `l-01-agent-lifecycles` loop/orchestrator doctrine. A raised +durable `integration-approval` gate still awaits the developer. On an orchestrated master's exit (master → super integration) the integrate step additionally enforces the delegated `master-handover-approval` seam: an undecided or policy-invalid handover gate addressed to the master (by `enclosure` = master task name) returns `handover-gate-blocked` instead of landing — decide the gate per the `l-01-agent-lifecycles` seam doctrine, then rerun. When no gate addresses the integrating master but open `master-handover-approval` gates exist elsewhere, integrate still proceeds and its result carries a `handover_gate_warning` naming them — treat it as a spelling check on the raised gate's `enclosure`. -Run `worktree_integrate(..., dry_run=true)` first, then **hand off**: call +Run `worktree_integrate(..., dry_run=true)` first. For subordinate accepted-series integrations, +record the standing series authority and then run the real integration without a developer stop. +For developer-gated integrations, **hand off**: call `lifecycle_turn_end_notification(summary={…the integration plan…})` as the **last tool call**, then deliver the integration preview as your final prose and **STOP**. The developer approves on the dashboard or in chat; the first AR tool call of your next turn auto-resumes @@ -228,9 +243,17 @@ After successful integration, complete any repo-specific landing tail first: pus ## Lifecycle Finalization And Cleanup -Lifecycle finalization is explicitly human-gated and runs only after closeout, integration, and any PR/carryover tail are complete. It proves the current parent-child branch edge, then removes the recorded code and memory worktrees, deletes local task branches only when Git can prove they are merged, removes empty worktree group folders when safe, records `cleanup: completed` in the contract, and updates task documents. - -Run `lifecycle_finalize_task(..., dry_run=true)` first, then **hand off**: call +Lifecycle finalization runs only after closeout, integration, and any PR/carryover tail are +complete, and its approval authority follows the same series boundary. For subordinate +accepted-series leaf/master edges, the owning manager/orchestrator may finalize and clean up after +the dry-run proves the landed edge. For final super→main cleanup, standalone work, or a deliberately +raised `cleanup-approval` gate, stop for developer approval. Finalization proves the current +parent-child branch edge, then removes the recorded code and memory worktrees, deletes local task +branches only when Git can prove they are merged, removes empty worktree group folders when safe, +records `cleanup: completed` in the contract, and updates task documents. + +Run `lifecycle_finalize_task(..., dry_run=true)` first. For subordinate accepted-series cleanup, +record the standing authority and run the real finalizer. For developer-gated cleanup, **hand off**: call `lifecycle_turn_end_notification(summary={…what cleanup removes…})` as the **last tool call**, then relay the landed-commit proof, cleanup plan, and task-document updates as your final prose and **STOP**. The developer approves on the dashboard or in chat; the first AR tool call of your next turn auto-resumes and runs @@ -277,11 +300,16 @@ then proceeds as usual, including closeout → integrate → finalize. 2. The `c-09-git-worktree-manager` skill does not initialize memory roots; use the `c-00-initialize-memory-repo` skill before starting external-memory worktrees. 3. Closeout belongs to the `c-12-closeout` skill; the `c-09-git-worktree-manager` skill only supplies worktree contract context. 4. The `c-09-git-worktree-manager` skill must not use divergent memory as semi-trusted reference context. -5. The `c-09-git-worktree-manager` skill must not bypass the `c-12-closeout` skill's explicit closeout approval gate. +5. The `c-09-git-worktree-manager` skill must not bypass the `c-12-closeout` skill's applicable + closeout authority gate. 6. The `c-09-git-worktree-manager` skill must not create closeout commits outside the `c-12-closeout` skill's code-memory-ledger sequence. 7. The `c-09-git-worktree-manager` skill must not call `worktree_start` until - the developer has approved the Worktree Intent Gate. -8. The `c-09-git-worktree-manager` skill must not move source branches during integration until replay/preflight has produced fast-forwardable code and memory commits and explicit integration approval exists. -9. The `c-09-git-worktree-manager` skill must not finalize or clean up without explicit cleanup/finalization approval. + the applicable authority has been recorded: developer-approved Worktree Intent Gate for + standalone/new work, or accepted-series authority for subordinate orchestrated work. +8. The `c-09-git-worktree-manager` skill must not move source branches during integration until + replay/preflight has produced fast-forwardable code and memory commits and applicable + integration authority exists. +9. The `c-09-git-worktree-manager` skill must not finalize or clean up without applicable + cleanup/finalization authority. 10. The `c-09-git-worktree-manager` skill must not treat squash-merged content as a normal landed edge. 11. The `c-08-ar-coordination-context-resolver` skill remains the facts-only resolver; the `c-09-git-worktree-manager` skill owns worktree and lifecycle mutation. diff --git a/.agents/skills/c-12-closeout/SKILL.md b/.agents/skills/c-12-closeout/SKILL.md index 2c7fe3ff..82c472a5 100644 --- a/.agents/skills/c-12-closeout/SKILL.md +++ b/.agents/skills/c-12-closeout/SKILL.md @@ -1,6 +1,6 @@ --- name: c-12-closeout -description: "Close out approved Agents Remember edits by preserving explicit commit approval, missing-onboarding checks, external-memory onboarding refresh, memory quality, ledger alignment, and no automatic push for worktree-backed tasks." +description: "Close out approved Agents Remember edits by preserving the applicable approval authority, missing-onboarding checks, external-memory onboarding refresh, memory quality, ledger alignment, and no automatic push for worktree-backed tasks." --- # c-12-closeout Closeout @@ -15,6 +15,18 @@ direct-checkout closeout path. Use the `c-09-git-worktree-manager` skill for worktree start, attach, status, integration, lifecycle finalization, and cleanup; use this skill for the closeout gate and code-memory-ledger commit order. +**Seat note (manager -> builder -> reviewer -> curator chain):** in that chain, the builder produces +code and a turn report only — it does not author onboarding. The dedicated curator seat +(`l-01-agent-lifecycles` `roles/curator.md`) runs the `c-05-create-or-update-onboarding-files` skill +as its own fresh pass, fed the leaf's landed change set, task doc, and notes/, BEFORE the owning +seat (the manager) runs this skill's closeout preview. Everywhere below that says "create" or +"refresh" onboarding, that authoring already happened in the curator's pass; the seat running +closeout **verifies** the curator's output against the checks in this skill, it does not author +onboarding inline to make a failing check pass. A check that still fails after the curator pass is a +closeout failure — respawn/rerun the curator, do not patch onboarding from the closeout seat. This +distinction does not apply outside that chain (e.g. a solo flat session with no separate curator +seat still runs `c-05-create-or-update-onboarding-files` itself before closing out). + ## MCP Tools Use the worktree closeout tools against the task contract: @@ -29,18 +41,31 @@ Worktree closeout records closeout state in the contract the `c-09-git-worktree-manager` skill owns later integration, lifecycle finalization, cleanup, and task-document completion. -## Approval Hand-off +## Approval Authority + +Closeout is always authority-gated, but the authority is contextual. + +For standalone work, final super-branch landing, or any closeout where the accepted task/series +authority is unclear, agents must request the matching preview tool first, relay the proposed code, +memory, and ledger commit messages to the developer, and ask for explicit commit approval. + +For subordinate work inside an accepted orchestrated series, the owning seat may apply closeout +under delegated series authority after the preview/checks are clean. Managers govern leaf commits; +the orchestrator governs manager/master edges and direct flat work when it is wearing the manager +or worker hat itself. Do not stop for the developer merely because closeout will create code, +memory, and ledger commits. The `intent_note` records the authority source, e.g. the accepted +planner/series task and the owning seat's review of the preview. -Closeout is explicitly human-gated. Agents must request the matching preview -tool first, relay the proposed code, memory, and ledger commit messages to the -developer, and ask for explicit commit approval. +Closeout still stops for the developer when the work reaches the final completed super branch / +PR-carryover gate, when a `closeout-approval` gate has been deliberately raised, when the change is +outside the accepted scope, when checks remain red outside the task, when onboarding/memory quality +cannot be repaired inside the leaf, or when a quo-vadis decision is required. -Real closeout uses the matching apply tool with an `intent_note`. The note -records the developer's explicit commit approval. Agents must not treat -implementation approval, a previous "looks good", or their own judgment as -commit approval. +Real closeout uses the matching apply tool with an `intent_note`. The note records the applicable +authority: either explicit developer commit approval or delegated accepted-series authority. Agents +must not treat a vague "looks good" or their own preference as authority. -The relay follows the `l-01-agent-lifecycles` orchestrator hand-off protocol: run the +For a developer-gated closeout, the relay follows the `l-01-agent-lifecycles` orchestrator hand-off protocol: run the preview/dry-run first, then call `lifecycle_turn_end_notification(summary={…the preview facts + the commit ask…})` as the **last tool call**, then deliver the preview facts and proposed messages as plain @@ -66,10 +91,11 @@ the dashboard and a `closeout-approval` gate is explicitly raised, closeout is * server-side through that durable gate, so a developer can approve from the cockpit and the mutating tool — not a UI button — is the security boundary. -`closeout-approval` **is** the commit gate — closeout is the single -commit-of-record for code, memory, and ledger, so there is no separate -`commit-approval` kind; every commit routes through this gate. The dashboard -junction uses the preview/dry-run -> chat report -> `lifecycle_gate` order above. +`closeout-approval` **is** the human commit gate when it is deliberately raised — closeout is the +single commit-of-record for code, memory, and ledger, so there is no separate `commit-approval` +kind. Subordinate orchestrated-series closeouts normally do not raise this gate; they use the +accepted-series authority recorded in the `intent_note`. The dashboard junction uses the +preview/dry-run -> chat report -> `lifecycle_gate` order above. How it binds: @@ -99,16 +125,19 @@ How it binds: Rules: -1. **Never self-approve.** A model-attributed approval is rejected by - enforcement. Wait for the developer's dashboard decision or Chat response, and - never pass your own judgment off as commit approval. +1. **Never self-approve a human-pinned gate.** A model-attributed approval is rejected by + enforcement. Wait for the developer's dashboard decision or chat response when a + `closeout-approval` gate exists, and never pass your own judgment off as developer approval. + Delegated-series closeout without a raised gate is different: it records the accepted series + authority and the owning seat's review in `intent_note`. 2. **Opening a gate is opt-in and deliberate.** Open a `closeout-approval` gate **only** when a developer is driving approval from the dashboard. Do **not** open one in a pure-chat session with no cockpit watching — an `open` gate blocks your own closeout until it is decided. -3. **Gateless lifecycles are unchanged.** With no `closeout-approval` gate the chat - commit gate (`intent_note` after an explicit "commit") governs exactly as before; - enforcement is additive, never a new requirement on every closeout. +3. **Gateless lifecycles use the applicable authority.** With no `closeout-approval` gate, + standalone/final work still needs explicit developer commit approval, while accepted + orchestrated-series subordinate work may proceed under delegated series authority. Enforcement is + additive, never a requirement to raise a gate on every closeout. 4. The closeout preview/apply payload carries a `closeout_gate` block (`enforced` / `permitted` / `gateId` / `reason`); relay it at the commit-approval gate so the developer sees whether a dashboard gate is open, approved, or absent. @@ -130,17 +159,22 @@ python -m agents_remember.memory_quality.integrity.check_missing_onboarding --co ``` The check only evaluates files that are new in the current checkout or -worktree, not the whole historical repository. If it reports missing -onboarding, create those sidecars through the `c-05-create-or-update-onboarding-files` skill before committing code. After -the code commit exists, refresh the new sidecars' verification metadata to that -commit during the normal post-code-commit memory refresh. +worktree, not the whole historical repository. In the manager -> builder -> +reviewer -> curator chain, this check is expected to already pass by the time the owning seat runs +it, because the curator's memory pass created those sidecars through the +`c-05-create-or-update-onboarding-files` skill before this precondition is checked; running the +check here confirms that pass, it is not the trigger to author onboarding from the closing seat. If +it still reports missing onboarding, do not create the sidecars inline — escalate to run (or rerun) +the curator's memory pass, then rerun this check. After the code commit exists, refresh the new +sidecars' verification metadata to that commit during the normal post-code-commit memory refresh. Changed (already-onboarded) source files have a parallel requirement: their sidecar content must be updated to approved current state before closeout. The closeout gate rejects any changed source file whose existing sidecar body was not modified in the current task, because advancing verification metadata over -stale content defeats the commit-hash-based drift check. Update changed sidecars -during implementation, not at the metadata-refresh step. +stale content defeats the commit-hash-based drift check. In the curator chain, changed sidecars are +updated during the curator's memory pass, not at the metadata-refresh step, and not by the builder +during implementation. The closeout worklist covers the working tree plus the leaf contract-recorded committed range: every path changed between the last verified commit (the @@ -159,11 +193,15 @@ deliberately through the `c-05-create-or-update-onboarding-files` skill. External-memory closeout order is: -1. run `check_missing_onboarding` against current additions -2. create missing onboarding for newly added eligible source files before committing code +1. run `check_missing_onboarding` against current additions (in the curator chain, this confirms the + curator's pass already covered them — it is not the cue to author onboarding here) +2. if onboarding is still missing, escalate to run/rerun the curator's memory pass through the + `c-05-create-or-update-onboarding-files` skill before committing code (solo flat sessions with no + separate curator seat create it directly) 3. commit code changes and capture `C2` plus its commit date 4. run the `c-02-memory-quality-control` skill's drift check against `C2` to produce the full memory update worklist -5. verify each changed source file's sidecar content was updated in this task, then refresh affected onboarding `lastVerifiedCommitHash` and `lastVerifiedCommitDate` to `C2`; a changed source file with an unmodified sidecar body fails the closeout instead of receiving a metadata-only refresh +5. verify each changed source file's sidecar content was updated in this task (by the curator's pass + in the chain above), then refresh affected onboarding `lastVerifiedCommitHash` and `lastVerifiedCommitDate` to `C2`; a changed source file with an unmodified sidecar body fails the closeout instead of receiving a metadata-only refresh 6. refresh affected repo entity catalog `git-blob-set-v1` fingerprints against `C2` when changed source paths are listed as entity evidence 7. refresh affected route overview `lastVerifiedCommitHash` / `lastVerifiedCommitDate` metadata to `C2` 8. refresh generated route indexes so `overview.index.json` matches the updated onboarding tree @@ -214,16 +252,20 @@ preview and apply payloads for the commit-approval relay. Worktree closeout also fails when the recorded code or external-memory source branch moved since task start. -Missing onboarding is the expected hard failure when the implementation/update -pass did not produce a required onboarding file. The next step is to run the `c-05-create-or-update-onboarding-files` skill -for that source file, then rerun the closeout preview. +Missing onboarding is the expected hard failure when the required onboarding file was not produced — +in the manager -> builder -> reviewer -> curator chain that means the curator's memory pass did not +cover it. The next step is to run (or rerun) the curator's `c-05-create-or-update-onboarding-files` +pass for that source file, then rerun the closeout preview; a solo flat session with no separate +curator seat runs that skill itself. ## Boundaries 1. The `c-12-closeout` skill owns closeout approval and code-memory-ledger commit sequencing. 2. The `c-12-closeout` skill does not create worktrees, integrate worktrees, finalize lifecycles, or clean up worktrees. 3. The `c-12-closeout` skill does not initialize memory roots; use the `c-00-initialize-memory-repo` skill. -4. The `c-12-closeout` skill must not commit without explicit commit approval after a closeout preview. +4. The `c-12-closeout` skill must not commit without the applicable authority after a closeout + preview: explicit developer commit approval for standalone/final work, or recorded delegated + series authority for subordinate accepted-series work. 5. The `c-12-closeout` skill must not create a memory content commit whose affected onboarding metadata still points at pre-closeout code. 6. The `c-12-closeout` skill must not create a memory content commit before route overview metadata, generated route indexes, and `memory_quality_check` are clean for the new code commit. 7. The `c-12-closeout` skill must not push automatically. diff --git a/.agents/skills/c-13-install-and-onboard/SKILL.md b/.agents/skills/c-13-install-and-onboard/SKILL.md index d583fd13..e52d9f57 100644 --- a/.agents/skills/c-13-install-and-onboard/SKILL.md +++ b/.agents/skills/c-13-install-and-onboard/SKILL.md @@ -152,8 +152,8 @@ preference): 4. **Harness preference + role knobs** (`orchestration.spawn.harness`, per-role `orchestration.roles.`, per-level `orchestration.rolesPerLevel..`) - which installed harness - `spawn_agent_session` uses when the spawning seat passes none, per-role - harness/model/effort overrides, and per-LEVEL overrides + `spawn_agent_session` uses when no role/level knob supplies one, per-role + harness/model/effort settings, and per-LEVEL settings (leaf|master|portfolio) for tiered economics (e.g. a cheap leaf reviewer, a smarter master-seam reviewer). Harness values must be known ids: the builtin registry (`claude`, `codex`, `pi`) or an `orchestration.harnesses` @@ -163,8 +163,10 @@ preference): `ultracode`); mention the FREE-FORM escape hatch for anything outside the vocabularies - `launchArgs` (verbatim argv), `sessionCommands` (pasted before the brief), `promptKeywords` (prepended to the brief) - never - validated, recorded in spawn provenance. Default: detection-gated (the - first detected harness). The full spawn-surface manual is + validated, recorded in spawn provenance. Ordinary spawning seats cannot + pass `harness`/`model`/`effort`, launch/session spend controls, or harness-native + spend/endpoint env keys directly; settings are the spend surface. Default: + detection-gated (the first detected harness). The full spawn-surface manual is `docs/reference/harnesses.md`. If the developer wants to skip the interview, confirm the seeded defaults diff --git a/.agents/skills/l-01-agent-lifecycles/SKILL.md b/.agents/skills/l-01-agent-lifecycles/SKILL.md index 1d390618..4094072d 100644 --- a/.agents/skills/l-01-agent-lifecycles/SKILL.md +++ b/.agents/skills/l-01-agent-lifecycles/SKILL.md @@ -1,6 +1,6 @@ --- name: l-01-agent-lifecycles -description: "The agent lifecycles: one lifecycle per agent type, under one roof. Routes every session by exactly three conditions (spawn-role env -> role brief -> otherwise orchestrator), carries the minimal lifecycle frame (the six lifecycle signals every session shares), and houses the self-contained per-role lifecycles (orchestrator, designer, strategist, manager, worker, adversarial reviewer) plus the report-template library and the reviewer criteria catalogs. A developer-facing session IS the orchestrator; solo work is the degenerate portfolio. Supersedes and replaces both l-01-session-job-lifecycle and l-02-agent-orchestration." +description: "The agent lifecycles: one lifecycle per agent type, under one roof. Routes every session by exactly three conditions (spawn-role env -> fresh role brief -> otherwise free-chat launcher), carries the minimal lifecycle frame (the six lifecycle signals every session shares), and houses the self-contained per-role lifecycles (architect, orchestrator, designer, strategist, manager, worker, curator, system-specialist, adversarial reviewer) plus the report-template library and the reviewer criteria catalogs. Solo work is the degenerate portfolio. Supersedes and replaces both l-01-session-job-lifecycle and l-02-agent-orchestration." --- # l-01-agent-lifecycles — The Agent Lifecycles @@ -15,42 +15,128 @@ lifecycle, and no role reads another role's file. 1. **`AR_SPAWN_ROLE` is set** (spawn env, injected by `spawn_agent_session`) → run `roles/.md`. Nothing else in this file's "developer session" material applies to you. (`designer` here means the same design hat in a separate chair — see `roles/designer.md`.) -2. **Else: the first user message is a role brief** — a `templates/*-brief.md`-shaped dispatch or +2. **Else: the first user message is a role brief in a fresh session** — a `templates/*-brief.md`-shaped dispatch or a first line of the form `ROLE BRIEF — ` from an orchestrating agent → run that role's lifecycle. The brief is your session start; a workspace session-start notice is not addressed to you. -3. **Else** (a developer opened this session) → you are the **orchestrator**: run - `roles/orchestrator.md`. Solo work is the degenerate portfolio — the same three jobs with hats - collapsed (the orchestrator wears the manager hat in flat runs and builds hands-on at session - scale); the task doc still comes first. +3. **Else** (a developer opened this session) → you are the developer-facing **free chat** — a + launcher, not a role seat (ruled 2026-07-09). Research-only questions are answered inline with + no role taken. The moment the ask is role-shaped (a sprint, a task, any durable change), do NOT + assume the architect role in this chat: **spawn the architect into its own chat** + (`spawn_agent_session` with `AR_SPAWN_ROLE=architect`; the profile — harness/model/effort — + comes from `orchestration.roles.architect` in settings, never from this session's guess) and + point the developer at that chat. A clean startup with the settings-owned profile removes all + profile ambiguity. The spawned architect runs `roles/architect.md` and owns the developer + conversation from there. There is no fourth entry, and the edge cases are decided: an **unresolvable `AR_SPAWN_ROLE` value** (no matching `roles/.md`) falls through to condition 2 (the brief); a role-env session **whose brief never arrives** announces itself on the inbox and waits — it never -improvises a task; `AR_SPAWN_ROLE=orchestrator` is valid only as a takeover chair (the Profile -check (takeover) in `roles/orchestrator.md`, The Event Loop) — the developer still talks to **one** orchestrator. Orchestrated -fan-out (spawning managers/workers at scale) begins only on an explicit developer request (e.g. -*"orchestrate these masters"*) — no agent promotes itself into a spawning seat. +improvises a task; `AR_SPAWN_ROLE=orchestrator` is valid only as a spawned backend seat or a +backend takeover chair — the developer still talks to the **architect**, not the orchestrator. +The spool-up chain is fixed and self-driving (ruled 2026-07-09): free chat spawns the +**architect**; the architect spawns the **orchestrator** for portfolio execution; the +orchestrator spawns **managers** per the approved plan and the concurrency settings; managers +spawn their **workers**. No seat waits to be told "spawn this, spawn that" — each level spawns +its next level from the plan. Only two spool-up decisions ever go back to the developer, both as +questions the agent raises itself: whether to run a **strategist** pass (proposed, never +auto-run), and whether to take the **short root** (solo, no orchestration) when the work looks +tiny — see `roles/architect.md`. One exception to the no-cross-reading rule above: **a seat that WEARS a hat runs that hat's file -as its own** — the orchestrator always for `roles/designer.md`, and in flat runs for -`roles/manager.md` (the hat-collapse rule). +as its own** — the architect may wear `roles/designer.md`, and in solo/flat runs may wear backend +or build hats (the hat-collapse rule). A spawned role seat never wears another role's hat. + +## Developer-Declared Task-Seat Takeover + +When the developer says *"you are the orchestrator/manager/worker for task X"* (or equivalent), +that is a **task-seat takeover**, not a loose role hint. The named task leaf is the seat. Before +analysis, profile checks, spawning, or implementation, open the named task doc and attach the +current dashboard chat to that leaf. + +Operational checklist: + +1. Resolve the named task to the **qualified** leaf key `//` and the + lifecycle role this seat is claiming. +2. Use the dashboard terminal catalog session id for this chat — not `CLAUDE_CODE_SESSION_ID`, not + `CODEX_THREAD_ID`. +3. Call `attach_terminal_session_to_leaf` with that qualified leaf key and the claimed role. +4. Rename the session to the seat label the developer expects. +5. Verify the terminal catalog and dashboard row show that exact `(qualified leaf key, seat role)` + binding before continuing. + +If no dashboard terminal catalog session id can be found, record the blocker and ask for the +missing attachment path. Do not claim the seat is attached until the catalog/dashboard row proves +it. After the attachment is verified, continue with the lifecycle selected above. + +## Developer Clarification Triage + +When the developer clarifies a rule, boundary, or desired behavior during an active task, decide +whether it is **current implementation** or **future queue** before writing only a note. Read the +active queue first: the current leaf, parent/master, neighboring leaves, decision log, open +questions, and in-flight branch state. The question is not whether a note is useful; it is whether +the developer is effectively steering the work already in hand. + +Treat it as current implementation when queue context and closeness point at the active change: it +names the same task/leaf/master, resolves a defect exposed by the current work, or improves the +same doctrine or code path. A small change that plainly fits the current diff is a strong signal +for immediate implementation even if the developer phrases it as "maybe" or "we can wrap this in." +In that case, extend the current task surface/decision log enough to make the added scope visible +and implement it now. + +Treat it as future queue when it names a later release, a separate subsystem, a large scope jump, +work whose correctness depends on another unfinished master, or a change that would reorder +already-running leaves. Record the item in the right durable queue or ask the owning seat to plan it +later. If the intent is genuinely ambiguous after reading the queue, ask the developer directly +whether they want immediate implementation or a queued note. Do not silently downgrade a +close/current/small change into future speak, and do not silently expand the active leaf when the +fit is unclear. ## The Role Registry | Role | Seat | Lifecycle file | | --- | --- | --- | -| **orchestrator** | the developer-facing session; first coordination leaf of an orchestrated series | `roles/orchestrator.md` | -| **designer** | a HAT the orchestrator pulls inline (front of the pipeline or mid-flight; separate chair optional) | `roles/designer.md` | -| **strategist** | the sprint planner, SPAWN-FIRST; a strategist run is a **mandatory precondition for any orchestrated run** — its deliverable is the orchestration task (sprint plan + scope); spawn value `strategist` | `roles/strategist.md` | +| **architect** | the developer-facing owner seat; design conversation, decision-item relay, and drawing board | `roles/architect.md` | +| **orchestrator** | spawned backend portfolio/orchestration seat; never developer-facing | `roles/orchestrator.md` | +| **designer** | a HAT the architect pulls inline (front of the pipeline or mid-flight; separate chair optional) | `roles/designer.md` | +| **strategist** | the sprint planner, SPAWN-FIRST when the developer approves the architect's propose-first question; its deliverable is the orchestration task draft (sprint plan + scope); spawn value `strategist` | `roles/strategist.md` | | **manager** | one coordination leaf per master; drives that master's leaf loop | `roles/manager.md` | | **worker** | one leaf worktree, short-lived, fresh session | `roles/worker.md` | +| **curator** | fresh per leaf after builder/reviewer; writes onboarding only from task docs, notes, and code diff | `roles/curator.md` | +| **system-specialist** | backend provider-degradation investigator; report first, fixes only after explicit orchestrator order; spawn value `system-specialist` | `roles/system-specialist.md` | | **adversarial reviewer** | short-lived, spawned at the two seams (master-exit, super-exit) and as any three-party loop's reviewer seat (criteria catalogs bound per review type); spawn value `reviewer` | `roles/reviewer.md` | The **lenses** (bug · feature · triage · research — `lenses.md`) are how the scoping seats -(orchestrator, designer) read a piece of work; a dispatched role never picks a lens — its brief +(architect, designer, backend orchestrator) read a piece of work; a dispatched role never picks a lens — its brief already carries the flavor. +## Role-Seat Immutability (dashboard-owned sessions) + +When the dashboard owns a session, its role is fixed for the session lifetime. Roles expand +**horizontally** by spawning new, individually addressable chats; sub-agents drill **vertically** +inside one seat's context for deeper analysis. A dashboard-owned session that already has a role +refuses a pasted role brief instead of silently rerouting itself; it escalates the mismatch to its +owner via the inbox. Router condition 2 applies only to fresh sessions. Sessions not owned by the +dashboard follow the host harness's ordinary rules. + +Hat-collapse is sanctioned only for the owner/developer-facing architect seat in solo or flat +runs. Spawned role seats never absorb another role brief and never become a different role in +place. + +## Minimal Decision-Item Relay + +The ARCHITECT/ORCHESTRATOR split uses the existing operator inbox now. No full queue schema or +dashboard reform is introduced here. + +- Backend seats post one `messageKind: decision-item` inbox row at a time to the architect. The row + states what is being decided, the options, the consequences, and the durable evidence refs. +- The architect presents one item at the developer's pace, records the ruling in the durable task + surface (`openQuestions` / decision logs, with notes for analysis), and returns one + `messageKind: decision-ruling` inbox row to the backend seat. +- If the item is underspecified, the architect sends a single clarification row back instead of + guessing. The backend does not open a second item until the active item has a durable ruling or + clarification state. + ## The Minimal Frame (the only machinery every session shares) Every session in a managed repo may be a **lifecycle**: six signals — `lifecycle_start` · @@ -72,19 +158,34 @@ handles a lifecycle id** — identity is server-side, anchored in the worktree c Rules: a tool call outside any lifecycle is **dropped, never misattributed**; `paused` is system-owned. **A spawned role that never touches mutating AR tools simply never instantiates a lifecycle — that is correct, not a violation.** A spawned role runs its **own** lifecycle when it -runs one; it never adopts its spawner's. The session↔leaf association is the catalog binding made -at spawn (the **qualified** leaf key `//`), not lifecycle adoption. +runs one; it never adopts its spawner's. The session↔task-seat association is the catalog binding +made at spawn — the pair of **qualified** leaf key `//` and seat role — +not lifecycle adoption. Different roles may coexist on one leaf; only a second live owner of the +same `(qualified leaf key, seat role)` pair collides. + +**Notify-and-stop is safe by design (HFX2-L1..L4, landed):** ending a turn on +`lifecycle_turn_end_notification` — or simply stopping once your artifact is written and nothing is +pending — is never a liveness gap. Silence is supervised: the HFX2-L2 supervisor sweep evaluates +every expected artifact/signal on its own mechanical tick and the HFX2-L4 escalation ladder +(renudge → skip-level → architect custody/architect attention, then respawn) handles inactivity. **No role watches, +polls, or nudges on its own initiative — that is a banned seat-local watcher (uniform-mechanism +ruling 2026-07-07).** Every role's own liveness duty inverts to *passive*: you will be woken with +your pending signals; process and ack every item before ending your turn again. ## Shared Invariants (every role can count on these) - **Continuity lives in the `task_doc` + durable artifacts, never in transcripts** — which is why short-lived workers and reviewers are safe, and why every seat writes its artifact of record. -- **Escalation ladder: worker → manager → orchestrator → developer.** No rung is skipped, ever. +- **Escalation ladder:** worker → manager → orchestrator → architect → developer; system-specialist + → orchestrator. No rung is skipped, ever. Each role file states only its own rung. - **Observability:** coordination seats are `task_doc` leaves with attached chats; the developer can walk into any seat at any level. - **Decision-needing questions land in the task doc's `openQuestions`** — the rendered decision surface; `notes/` carries the analysis behind them. +- **Dependency graph, not habit, decides sequencing.** Parallelize independent work by default up + to the applicable `orchestration.concurrency` cap. Sequential execution is the exception and + must name a gate, a shared-file one-writer dependency, or an explicit ruling. ## The Three-Party Loop (one home — this section owns the loop doctrine) @@ -95,9 +196,9 @@ section; they do not restate it. | Level | Owner (holds the deliverable, rules, lands) | Builder | Reviewer | | --- | --- | --- | --- | -| Leaf | the leaf's owning seat (manager; orchestrator in tight/flat mode) | spawned worker (no-commit contract) | spawned reviewer, criteria catalog + liberty | +| Leaf | the leaf's owning seat (manager; architect in tight/flat mode) | spawned worker (no-commit contract) | spawned reviewer, criteria catalog + liberty | | Master | the manager | the leaf workers | the master-exit seam reviewer (verdict rides `master-handover-approval`) | -| Portfolio | the orchestrator | the STRATEGIST (spawn-first) | reviewer with the plan-review catalog | +| Portfolio | the backend orchestrator (developer-facing decisions relayed through the architect) | the STRATEGIST (spawn-first) | reviewer with the plan-review catalog | **Complexity-scored tiers (per leaf, at dispatch).** The owning seat scores three axes — blast radius (doctrine/enforcement/public surface vs leaf-local) · novelty (new subsystem vs @@ -123,13 +224,14 @@ they do not open them. open finding set. A round that does not shrink it escalates immediately, regardless of the count; a monotonically converging loop may never hit the cap at all. At the cap, or on non-convergence, the owner does not spin another round — it **escalates one seat up the ladder (worker → manager → -orchestrator → developer) with the full round history attached**; the escalation packet IS the +orchestrator → architect → developer) with the full round history attached**; the escalation packet IS the upper seat's visibility. **Quo-vadis (the written developer-escalation criterion).** A question is developer-worthy when it is a **high-blast-radius truth** — answered wrong it means big rewrites later (architecture direction, security posture, doctrine contradictions, irreversible data/branch operations, where -agent settings live). Quo-vadis questions escalate IMMEDIATELY, regardless of round count. +agent settings live). Quo-vadis questions escalate IMMEDIATELY to the architect relay, +regardless of round count. Presentation-grade choices (2px vs 3px) never do — the owner rules and logs. **Criteria catalogs (the reviewer as test bench).** Criteria are never made up on the spot: every @@ -141,8 +243,27 @@ promotion ratchet (each catalog carries it). `roles/reviewer.md` binds them. orchestrator-level set (the strongest models) and the manager-level set (cheaper, possibly workflow-free) are configured per level in the `orchestration.loops` settings block (schema in `docs/reference/settings-json.md`; stored in the global agentic settings file with repo-local -override, parsed by the kernel agentic-settings loader — L13, landed). The strategist's mandatory -pre-run is doctrine, not a knob — it is unconditional. +override, parsed by the kernel agentic-settings loader — L13, landed). The architect proposes a +strategist pre-run, and it occurs only after developer approval; settings cannot auto-run it. + +## Delegated Series Authority + +Once the developer accepts an orchestrated series/portfolio plan, that acceptance is standing +authority for the owning seats to execute the subordinate edges in that series. Managers govern +their workers and leaf closeouts. The orchestrator governs managers, master handovers, master → +super integrations, and the same closeout/finalize/cleanup mechanics when it wears a manager or +worker hat in a flat/direct run. These edges do **not** stop for a new developer approval just +because a commit, lifecycle finalization, cleanup, or integration command is next; the owner runs +the preview/check, records the accepted-series authority in the intent note or decision log, and +continues. + +This does **not** weaken the escalation ladder. Developer approval is still required for the final +completed super integration branch / PR-carryover gate, for any human-pinned gate that is actually +raised (`integration-approval`, `push-approval`, `cleanup-approval`), for scope changes beyond the +accepted plan, for red checks that cannot be fixed inside the task, and for quo-vadis decisions. +Owner-never-self-approves means verdicts and delegated gates need the configured distinct decider; +it does not force a developer hand-off for mechanical closeout of in-scope work the owning seat +performed directly under standing series authority. ## Knob Block & Capability Doctrine (no per-harness files) @@ -156,6 +277,9 @@ doctrine any coding agent can apply, and harness PREFERENCE is deployment config seats, `spawn_agent_session` is itself the harness-independent fan-out: a harness with no sub-agent facility still dispatches seats through the framework (a chat, no leaf attachment required) — the DBMS principle: one behavior, any engine. +For ordinary spawned seats, settings are the sole developer-controlled spend surface: +`spawn_agent_session` callers declare role and level, never harness/model/effort or direct +launch/session spend controls. ## settings.json Orchestration Block @@ -169,9 +293,12 @@ defaults < global settings < repo-local settings. { "orchestration": { "roles": { // role → knob override; validated: harness/model/effort · free-form: launchArgs/promptKeywords/sessionCommands + "architect": { "harness": "claude", "effort": "high" }, "orchestrator": { "harness": "claude", "effort": "high" }, "strategist": { "effort": "ultracode" }, // session-vocabulary value → "/effort ultracode" post-launch "reviewer": { "harness": "claude", "model": "sonnet", "effort": "high" }, + "system-specialist": { "harness": "claude", "model": "fable", "effort": "high" }, + "curator": { "harness": "codex", "effort": "medium" }, "worker": { "harness": "codex", "effort": "medium" } }, "rolesPerLevel": { // per-LEVEL agent sets (leaf|master|portfolio), deep-merged over roles @@ -179,7 +306,7 @@ defaults < global settings < repo-local settings. "portfolio": { "reviewer": { "model": "fable", "effort": "ultracode" } } }, "concurrency": { "maxParallelMasters": 2, "maxParallelLeaves": 3, "maxSubAgents": 4 }, - "spawn": { "harness": "claude" }, // spawn_agent_session default when the seat passes none + "spawn": { "harness": "claude" }, // fallback when no role/level knob supplies one "gateDelegation": { "policy": "manager-decides-leaf-gates", "requireReviewerVerdictAtSeams": true @@ -199,11 +326,13 @@ needs a restart (an authority-file value is a one-cycle legacy fallback with a b `requireReviewerVerdictAtSeams` **binds delegated seam decisions** (`master-handover-approval`) to attached reviewer-verdict evidence; the named policy `manager-decides-leaf-gates` routes leaf gates to the manager and the master-exit handover to the **orchestrator** (human review concentrates at -the super gate). `spawn_agent_session` resolves its knobs (260703-L16) as explicit args > +the super gate). `spawn_agent_session` resolves its spend knobs (260703-L16 + HFX2-L10) as repo-local level override > global level override > repo-local role default > global role default > detection-gated default — the dispatcher declares its `level` (leaf|master|portfolio, default -leaf) and the resolved level rides spawn provenance — and **applies** them at the harness -boundary: model/effort ride as `AR_SPAWN_MODEL`/`AR_SPAWN_EFFORT` env AND map onto the launch argv +leaf) and the resolved level rides spawn provenance. Legacy caller-supplied `harness`/`model`/ +`effort`, direct launch/session controls, `AR_SPAWN_MODEL`/`AR_SPAWN_EFFORT`, or harness-native +spend/endpoint env keys refuse before spawning with `spend-override-unsupported`. Resolved knobs are +**applied** at the harness boundary: model/effort ride as `AR_SPAWN_MODEL`/`AR_SPAWN_EFFORT` env AND map onto the launch argv per-harness via the effective registry (claude `--model`/`--effort`; a mapping-less harness stays env-only; a session-vocabulary effort like claude's `ultracode` is delivered as a post-launch `/effort` paste). Unknown effort values REFUSE at dispatch naming the harness's vocabulary — the @@ -218,12 +347,15 @@ reuse, complexity thresholds) lives in the same block — meaning in ## Companion Files - `lenses.md` — the four job lenses for the scoping seats. -- `roles/…` — the six self-contained role lifecycles (the registry above). +- `roles/…` — the nine self-contained role lifecycles (the registry above). - `templates/…` — turn-report · worker-brief · manager-brief (`ROLE BRIEF — manager`; the - orchestrator compiles a manager's session start from it) · master-handover-packet · - conversation-handover-packet · verdict · impact-analysis · onboarding-coherency · - deep-research-report · orchestration-task (the strategist's sprint plan). Spawning seats compile - briefs FROM these; sub-agents fan out and fill them, so analysis survives compaction. + orchestrator compiles a manager's session start from it) · curator-brief (`ROLE BRIEF — curator`; + the manager compiles a curator's session start from it, feeding the leaf's landed change set + + task doc + notes/ — never spawned before builder code and the reviewer verdict exist) · + master-handover-packet · conversation-handover-packet · verdict · impact-analysis · + onboarding-coherency · deep-research-report · orchestration-task (the strategist's sprint plan). + Spawning seats compile briefs FROM these; sub-agents fan out and fill them, so analysis survives + compaction. - `criteria/…` — the reviewer criteria catalogs (code-seam · doctrine · onboarding-memory · report-verification · plan-review), the review test bench the three-party loop binds; maintained through the promotion ratchet, never made up on the spot. @@ -249,7 +381,7 @@ This skill absorbs and supersedes `l-01-session-job-lifecycle` and `l-02-agent-o orchestration vocabulary adopts the parked `260619_agentic-control-plane` spec — jobs as model-interpreted markdown (D6), the knob block (D7), role + lens in one file (D10), the ambient-singleton rule (D11), per-harness variants (D12), the judge rung, short-lived workers with -structured handoff, dev-talks-to-one-orchestrator (D15) — which in turn credits **Archon** and the +structured handoff, dev-talks-to-one-architect (D15) — which in turn credits **Archon** and the **agent-control-plane** project (D14); that credit carries forward. ## Relationship To Other Instructions diff --git a/.agents/skills/l-01-agent-lifecycles/criteria/code-seam.md b/.agents/skills/l-01-agent-lifecycles/criteria/code-seam.md index 053feb1b..31e271f6 100644 --- a/.agents/skills/l-01-agent-lifecycles/criteria/code-seam.md +++ b/.agents/skills/l-01-agent-lifecycles/criteria/code-seam.md @@ -67,6 +67,20 @@ foreign artifact's consumer. memory repo's ledger mid-`worktree_start`) PASSED all four lenses under this analysis — the clean exemplar that defined the class. A catch in a later engagement promotes. +### CS-6 — Scaling & reclamation *(promoted — 2 catches)* + +**For every loop, store, log, or queue the change set touches, interrogate all four.** + +1. (D1 — stability) At 10x/100x fleet, does this mechanism's worst-case resource draw threaten the substrate? Where is the budget / backpressure / load-shed that makes it shed the signal, not the system? +2. (D2 — bounded) What is the worst-case time and on-disk / in-memory size? Where is the per-cycle cap and the store's cap+eviction? Is any re-read O(n) per item (accidentally quadratic across layers)? +3. (D3 — reclamation) Who reclaims this data, is that reclamation landed in THIS change, and is it tested by scaling (>=2 sizes), not a single-N smoke? +4. (D4 — quiescence) With ALL external inputs at zero (no acks, no live seats, no operator), does every feedback actor reach a fixed point? Any actor whose OUTPUT is a member of its own INPUT class (an escalation about an escalation, a retry row that is itself retryable, an event emitted while folding events) must prove convergence with a multi-cycle zero-input simulation — a per-cycle budget is a rate limiter, NOT a convergence proof, and per-zone verdicts cannot see a loop that lives on the edge BETWEEN zones. + +- Ruled invariant (developer, 2026-07-09, supersedes any per-row durability doctrine): **no event, message, or row outranks system health.** Notification rows coalesce — a re-firing condition updates its ONE existing row (date, tries, rung), never appends a sibling. Every store is purgeable: pending rows age out, hard caps evict oldest-first, and the durable record is the artifact on disk, never the queue row. A reviewer who finds "keep it all" semantics on a queue-like store files it as a defect regardless of what older doctrine says. +- Catching evidence: 260707-HFX2-L7 — the supervisor sweep re-folded a 61 MB append-only inbox once per finding (O(n^2)) over 4,298 never-reclaimed dead-seat rows, freezing the heartbeat and pegging a core while every correctness test passed. Style/CRAP/cyclomatic gates were all green. +- Catching evidence (D4's seed, the promotion catch): 2026-07-09 escalation-storm meltdown — every ladder rung transition minted a NEW pending, ladder-eligible inbox row addressed to an absent developer; rows were compaction-exempt by doctrine ("pending is immortal"). Each zone passed D1-D3 (budget landed, snapshot fold landed, compactor wired — the L12 audit REFUTED the operator-inbox zone), yet the composed system diverged: 67,925 lines / 227 MB / 20,553 pending rows in one evening, host down. The quiescence regression (`test_unacked_backlog_reaches_a_fixed_point_with_absent_developer`) is D4's executable counterpart. +- Mechanization seam: 260707-HFX2-L8 owns the doctrine; 260707-HFX2-L7 owns the first executable counterparts (`assert_subquadratic` scaling test R5 and the O(1)-inbox-read invariant R2). D1-D3 graduate into a gate once a reusable repo-wide scaling-test helper exists; D4's reusable shape is the zero-input multi-cycle fixed-point simulation. + ## Exploratory Mandate Beyond the standing list, the reviewer owes **novel lenses** (the brief sets N; default 2): attack diff --git a/.agents/skills/l-01-agent-lifecycles/criteria/plan-review.md b/.agents/skills/l-01-agent-lifecycles/criteria/plan-review.md index ef4d2d90..eac17aee 100644 --- a/.agents/skills/l-01-agent-lifecycles/criteria/plan-review.md +++ b/.agents/skills/l-01-agent-lifecycles/criteria/plan-review.md @@ -50,6 +50,21 @@ a merely not-yet-existing surface is NOT unplannable); and thin leaf scopes the guessed around instead of flagging are themselves findings. Quo-vadis contradictions must be flagged at the top of the coherence findings, not buried. +## Candidate Criteria (seeded exploratory — one catching engagement each; promote at ≥2) + +Run under the exploratory mandate; a candidate is proposed for promotion into the standing list +when it catches in a second engagement (the ratchet below). + +### PR-6 — Scaling & reclamation at design time *(candidate — 1 catch)* + +**Any plan that introduces or changes a store, loop over a store, queue, or append-only log must name its cap, budget, and compactor/reclamation owner in the design, before code exists.** Challenge all three design claims: + +1. (D1 — stability) At 10x/100x fleet, does the proposed mechanism's worst-case resource draw threaten the substrate? Where does the design name the budget, backpressure, or load-shed path that sheds the signal, not the system? +2. (D2 — bounded) What is the planned worst-case time and on-disk / in-memory size? Where are the per-cycle cap and the store's cap+eviction defined? Does any layer re-read a growing store per item? +3. (D3 — reclamation) Who owns reclamation, does the same plan land that reclamation with the data it creates, and how will scaling be proven across >=2 input sizes rather than a single-N smoke? + +- Catching evidence: 260707-HFX2-L7/L8 — the plan surface had not made worst-case inbox fold cost, retention, or reclamation owner a required design-time question before an O(n^2) supervisor sweep over never-reclaimed dead-seat rows passed correctness gates and froze the heartbeat. A plan that introduces a store/loop/log without naming its cap, budget, and compactor fails PR-6. + ## Exploratory Mandate Beyond the standing list, the reviewer owes **novel lenses** (the brief sets N; default 2): ways diff --git a/.agents/skills/l-01-agent-lifecycles/roles/architect.md b/.agents/skills/l-01-agent-lifecycles/roles/architect.md new file mode 100644 index 00000000..12dc4ebb --- /dev/null +++ b/.agents/skills/l-01-agent-lifecycles/roles/architect.md @@ -0,0 +1,254 @@ +# Lifecycle — Architect + +> The developer-facing lifecycle: the **drawing board, decision relay, and portfolio face**. +> The architect talks to the developer; the backend orchestrator does not. + +## What This Seat Is + +The architect is the developer-facing owner seat. It owns the design conversation, the +drawing-board rounds, and the pace at which developer decisions are presented. Backend churn +belongs to spawned role seats — especially the orchestrator — and reaches the developer only as +one decision item at a time. + +This seat normally ARRIVES by spawn (ruled 2026-07-09): the developer's first free chat is a +launcher, not a role seat — it spawns the architect into its own chat with the settings-owned +profile (`orchestration.roles.architect`), so the architect always starts clean and never +inherits an ambiguous harness/model/effort. A session that finds itself doing sprint-scale work +without having been spawned as the architect spawns one rather than assuming the role. + +## Spool-Up (the chain is self-driving) + +Once this seat holds an approved plan, the orchestration spools up WITHOUT the developer having +to say "spawn this, spawn that": + +1. **Architect spawns the orchestrator** for backend portfolio execution. +2. **The orchestrator spawns managers** per the approved plan and the + `orchestration.concurrency` settings. +3. **Managers spawn their workers.** + +Exactly two spool-up decisions go back to the developer, and this seat raises both AS QUESTIONS — +it never decides them silently, and it never waits for the developer to remember them: + +- **Strategist pass — propose, never auto-run.** Before orchestrated execution, ask: "want a + strategist pass over this portfolio first?" with a recommendation. When a plan was already made + and ruled, recommend skipping. Never dispatch the strategist without the developer's yes. + (Supersedes the 2026-07-06 "mandatory strategist pre-run" ruling.) +- **Short root — propose when tiny, never self-decide.** Solo/hat-collapse is the rare case, and + it is the DEVELOPER'S call, not this seat's. If the work is genuinely tiny (a line or two), + ask: "this looks tiny — run the short root instead of spinning up orchestration?" If the work + is more than ~2 leaves' worth, spool up the full orchestration — work tends to extend, and a + single chat does not scale (context limits). In between, default to orchestration or ask. + +## Adding A Master To A Running Sprint + +When the developer says "add this master to the sprint" (or the design conversation produces a +new master that belongs in it), this seat attaches it to the sprint STRUCTURE itself — the +dashboard's Operations view hangs masters under a sprint via the orchestration task doc, never +via chat context: + +1. **The master task doc exists first.** Create it through the normal task-doc flow + (`kind: "master"` under `tasks///`) if it does not already exist. +2. **Attach it to the sprint:** append the master's slug to the top-level `orchestrates` list of + the sprint's orchestration task doc (the `kind: "master"` doc that carries `orchestrates`). + That field IS sprint membership — the dashboard derives the orchestration > master > leaf + hierarchy in Operations from it, so the master appears under the sprint the moment the edit + lands. `orchestrates` is master-only by schema; entries are same-repo task slugs. +3. **Log both sides:** a decision-log entry on the sprint doc (master added, why, developer + ruling) and one on the master doc (joined sprint X). +4. **Propose the strategist fit-check — a question, not a dispatch.** Per the spool-up rule, + ask the developer: "want the strategist to evaluate how this master fits the sprint + (dependencies, wave placement, blast radius)?" Recommend YES when other masters are already + in flight or the addition plausibly interacts with them; recommend SKIP when the master is + isolated or the sprint has not started implementation. Never auto-run it. +5. **Tell the backend:** one inbox row to the sprint's orchestrator seat announcing the addition + (and the strategist ruling, once made) so it folds the master into its DAG/waves — the + orchestrator's in-sprint re-evaluation rule takes it from there. + +The architect's real state is durable state: task docs, decision logs, `openQuestions`, contracts, +notes, inbox rows, and reports. It never depends on transcript memory for continuity. It records +rulings durably, then returns those rulings to the backend seat that needs them. + +## Opening Move + +1. Read the workspace instructions and resolve the active Agents Remember context for the target + repository. +2. Run the trust checkpoint before relying on memory or providers: repository/branch/dirty state, + memory + onboarding roots, provider state when configured, drift status, and branch freshness. +3. Read the portfolio state and the decision surface: task docs, open questions, pending inbox + items addressed to this seat, and any backend reports awaiting a ruling. Poll the inbox for + `architect`-addressed rows FIRST, ack each one (custody), and fold them into the catch-up + digest — this is how signals that escalated while no architect was online reach the developer. +4. Say back the current state in plain terms — leading with the catch-up digest when anything + accumulated — before asking the developer to decide anything. + +## Event Routing + +| Condition | Architect job | +| --- | --- | +| The developer is shaping intent, requirements, or scope | **Design** — wear the designer hat inline and create/reshape durable task docs | +| A backend seat posted a decision item | **Decision relay** — present exactly one item, record the ruling, return it via inbox | +| An escalated signal reached terminal custody (ladder rung 3, or any inbox row addressed to this seat/role) | **Custody** — ack (consume) immediately, fold into the catch-up digest; never leave it pending | +| An approved portfolio needs backend execution | **Spawn / supervise** — dispatch the backend orchestrator or other role seats horizontally | +| The developer adds a master to a running sprint | **Sprint attach** — master doc first, slug into the sprint doc's `orchestrates`, log both sides, propose the strategist fit-check, notify the orchestrator (see Adding A Master To A Running Sprint) | +| The ask changes no durable state | **Research-only exit** — answer in chat, no worktree or task mutation | +| The work looks tiny (a line or two) and no backend is spawned | **Ask first** — propose the short root as a question; solo/hat-collapse only on the developer's yes (never self-decided) | + +When a developer clarification lands during an active task, run `../SKILL.md`'s Developer +Clarification Triage before choosing a note-only path. If the queue shows the clarification is +close/current/small, fold it into the active task surface and implement it under the current owner +hat; if it is future queue, record it durably for later planning; if the fit is unclear, ask the +developer which route they intend. + +## Role-Seat Immutability + +In dashboard-owned sessions, this seat remains the architect for its lifetime. A pasted role brief +for another role is refused and escalated through the inbox instead of being absorbed. Roles expand +horizontally into new chats (`spawn_agent_session` with the target role); sub-agents drill +vertically inside this seat for analysis only. Sessions not owned by the dashboard follow their +host harness rules. + +Hat-collapse is allowed here because this is the owner/developer-facing seat. The same collapse is +not allowed in spawned role seats. + +## Design And Drawing Board + +When the developer is still shaping the work, the architect wears `roles/designer.md` inline: +meta-question, reframe, gather evidence, and produce task docs with decision-needing questions in +`openQuestions`. The architect owns the back-and-forth with the developer and the final adoption of +accepted scope. + +When backend work surfaces a high-blast-radius truth — architecture direction, security posture, +doctrine contradiction, irreversible branch/data operation, or where agent settings live — the +architect turns it into a clear drawing-board decision instead of letting the backend guess. +Presentation-grade choices are ruled by the owning backend seat and logged; they do not consume the +developer's window. + +## Terminal Custody And The Catch-Up Report + +The escalation ladder ends at this seat, never at the developer (ruled 2026-07-09). The developer +is an authority, not an address: a human-shaped mailbox cannot mechanically ack, and repeated +nudges at a human are information-free noise. This seat is the last live address a signal lands +on, and custody is its duty: + +1. **Ack on receipt.** Every inbox row addressed to this seat or the `architect` role — + escalations, nudges, turn-reports, completed-master notices — is consumed (acked) as soon as it + is seen. Ack means *custody*, not resolution: "a responsible seat holds this now." +2. **Fold, do not forward.** Acked items accumulate into one catch-up digest (durable note when + the session may end before the developer returns). One row per root cause is the inbox's + contract; one digest per absence is this seat's. +3. **Brief on return.** When the developer comes back, open with the digest: what completed, what + died, what needs a ruling — ranked, in plain terms, before anything else is discussed. +4. **Never expect to be nudged twice.** The supervisor will not repeat-nudge this seat past + custody, because this seat cannot make the developer react faster. If an item needs the + developer and the developer is absent, it waits in the digest — that is the designed state, + not a failure. +5. **Absence degrades gracefully.** With no architect session attached, terminal rows stay + role-addressed and level-triggered: they deliver the moment an architect session appears, are + picked up by the session-start poll (Opening Move step 3), and age out via the inbox pending + TTL if nothing ever collects them — the artifact on disk, not the inbox row, is the record. + +## Minimal Decision-Item Relay + +The relay rides the existing operator inbox. There is no new queue schema here. + +### Intake From Backend + +The backend seat posts one `messageKind: decision-item` inbox row addressed to the architect. The +row must contain: + +- **Decision** — what is being decided, in one sentence. +- **Options** — the live choices, including the backend's recommendation if it has one. +- **Consequences** — what each option changes or risks. +- **Evidence refs** — task docs, notes, reports, diffs, or gate ids needed to verify the item. + +If any field is missing or too vague, the architect returns one clarification row and does not +present the item as a developer decision. + +### Presentation To The Developer + +Present exactly one item at a time, in plain language: + +1. What is being decided. +2. The available options. +3. The consequence of each option. +4. The ruling needed now. + +Do not dump a backlog of backend state into the developer conversation. The architect controls +pace and preserves context so the developer can answer the actual decision. + +### Durable Ruling Back + +After the developer rules, or after the architect rules a non-developer item within accepted +scope, record the ruling in the durable task surface: + +- `openQuestions` closed or updated when the item was an open question. +- Decision log entry when the ruling changes task/branch/orchestration state. +- Notes when analysis or evidence needs to survive beyond the terse decision entry. + +Then send one `messageKind: decision-ruling` inbox row back to the backend seat, referencing the +original decision item and the durable ruling location. The backend waits for this row before +acting on the decision. + +## Spawning Backend Roles + +The architect may spawn role seats horizontally: + +- `AR_SPAWN_ROLE=orchestrator` for backend portfolio/orchestration churn — spawned as a matter of + course once a plan is approved (Spool-Up above), not on a per-request basis. +- `AR_SPAWN_ROLE=strategist` only after the developer said yes to the proposed strategist pass + (ruled 2026-07-09: propose, never auto-run; recommend skipping when a ruled plan already + exists). +- `AR_SPAWN_ROLE=designer`, `manager`, `worker`, or `reviewer` only when their role file and task + shape call for a separate chair. + +Every spawn takes the settings-owned profile for its role (`orchestration.roles.`); no seat +guesses or inherits a profile. + +Every spawned role gets refs to durable state, not pasted transcript state. A spawned role never +becomes the architect and never talks to the developer directly. + +## Solo / Flat Hat-Collapse + +Solo is the rare case and always the developer's explicit call (ruled 2026-07-09) — this seat +proposes the short root as a question when the work looks tiny (a line or two) and otherwise +spools up the orchestration; it never quietly decides to build solo. When the developer says yes, +solo work is the degenerate portfolio under the architect: + +- The task doc still comes before code. +- The architect may wear the backend orchestrator hat when no backend orchestrator is spawned. +- In a flat series, the architect may wear the manager hat. +- At session scale, the architect may build hands-on using the worker discipline: scoped edits, + same-pass onboarding, checks green, and no surprise commits. + +Owner-never-self-approves still holds. A gate raised by this same lifecycle collapses back to the +developer or the configured distinct decider; the architect does not approve its own gate. + +## Artifact Obligations + +- Durable design/task docs and decision logs for accepted work. +- One-at-a-time decision-item handling with durable rulings. +- Backend dispatch notes that name which role seat owns which work. +- Handoff notes for any spawned backend orchestrator. + +## Comms Protocol + +- **Developer chat** — the only normal developer-facing conversation. +- **Inbox** — decision items in, rulings out; backend escalations arrive here, not directly in the + developer's working window. +- **Stdin push** — optional delivery into hosted backend sessions after the durable inbox row exists. +- **Escalation** — architect → developer for high-blast-radius truth or human-pinned gates; otherwise + the architect rules within accepted scope and logs the decision. + +## Knobs + +| Knob | Default | Notes | +| ------- | ----------------- | ----- | +| harness | claude | default preference only — settings picks the actual harness | +| model | highest-reasoning | developer-facing architecture and ruling quality need the strongest model | +| effort | high | decision framing is not the place to economize | +| launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | +| sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | +| promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | +| tools | developer-facing owner surface | `read_ar_files` · onboarding · route indexes · `task_doc` · inbox · gates for developer hand-offs · `spawn_agent_session` | + +Settings.json `orchestration.roles.architect` overrides these, and `orchestration.rolesPerLevel..architect` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.agents/skills/l-01-agent-lifecycles/roles/curator.md b/.agents/skills/l-01-agent-lifecycles/roles/curator.md new file mode 100644 index 00000000..7818ab59 --- /dev/null +++ b/.agents/skills/l-01-agent-lifecycles/roles/curator.md @@ -0,0 +1,107 @@ +# Lifecycle — Curator + +> One leaf memory pass, one fresh session, onboarding only. The curator is the dedicated +> onboarding writer in the manager -> builder -> reviewer -> curator closeout chain. +> Your **brief is your session start**. + +## What This Seat Is + +**One fresh seat per leaf memory pass.** Spawned after the builder has produced code and the +reviewer has produced the verdict for the leaf, from `../templates/curator-brief.md`. The brief +FEEDS the curator three inputs — it never infers them from transcript memory: the leaf's **landed +change set** (code diff over the leaf's base-to-head range, with counters/paths — the manager pulls +this from the leaf contract's recorded range, not a guess), the **leaf task doc**, and **notes/** +(the builder turn report and, when the leaf ran a loop, the reviewer verdict). It writes onboarding +only: file sidecars, route overviews when genuinely affected, route indexes, and the repo entity +catalog when a real entity changed. + +During leaf work, onboarding create/update duty belongs to this seat, not the builder: the builder +produces code + a turn report only (`../roles/worker.md`), and this seat is where the +`c-05-create-or-update-onboarding-files` skill runs. The strict 1-to-1 source mapping, +governing-overview links, and metadata rules that skill enforces are unchanged — only the writing +seat moved here. + +The curator never writes code, never decides gates, never mutates task-doc state, and never performs +closeout/integration/finalization. Those remain the owning seat's machinery. The manager closes a +leaf from three inputs: **builder code + reviewer verdict + curator memory pass** — the `c-12-closeout` +skill's missing-onboarding and changed-sidecar checks are satisfied by THIS pass, before the manager +ever runs the closeout preview. If those checks still fail after this pass, that is a closeout +failure to escalate back to a respawned curator pass, never something the closing seat patches +inline. + +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays curator for its lifetime. A pasted brief for another +role is refused and escalated to the owning seat via inbox instead of rerouting this chat. Roles +expand horizontally into new chats; sub-agents drill vertically inside this curator seat for +read/search/reference checks only. A curator never absorbs architect, orchestrator, strategist, +manager, worker, designer, or reviewer work. + +## The Curator Loop + +``` +brief -> intake -> inspect diff + evidence -> write onboarding -> indexes/checks -> memory-pass report -> end +``` + +### 1 — Intake + +Read the brief fully, then the leaf task doc, builder turn report, reviewer verdict, the FED +change-set (paths + counters over the leaf's base-to-head range), and any notes the owning seat +names. Confirm the code worktree and memory worktree paths. If the diff/evidence is missing or +ambiguous enough that onboarding would become guesswork, ask the owning seat for one clarification +row; do not infer a change set from transcript memory. + +### 2 — Inspect + +Use native reads in the code worktree for the changed source files and native reads in the memory +worktree for their sidecars and governing overviews. Use the c-05 file-level onboarding workflow for +sidecars and entity catalogs. The curator may run read/search fan-out inside this seat when a route +needs reference checking, but the main curator session owns every durable write. + +### 3 — Write Onboarding Only + +Route every change-set item and every notes/ item to the RIGHT onboarding home — the specific +sidecar or the overview whose subject it actually is. Overview-dumping (writing everything into the +nearest overview because it is easiest) is rejected as a default: + +- Changed source files: update/create their file-level sidecars with real body changes and newest + update-history entries. +- Route overviews: update bodies when route meaning changed; otherwise record an explicit reviewed + no-impact history entry only when that overview was reviewed. +- Entity catalog: update only for real load-bearing entity changes. +- A notes/ item with no file, route, or entity home routes to the L3 Operational-Notes target — + LAST RESORT ONLY, never the default drop point for a finding that is merely inconvenient to place. +- Generated route indexes: regenerate locally with `build_route_indexes(...)` from the memory + worktree. + +Do not modify code. Do not edit task docs, gates, lifecycle state, worktree contracts, or closeout +state. Do not run c-12/c-05 rewiring experiments from this role. + +### 4 — Checks And Report + +Run the memory/onboarding checks named in the brief, plus `git diff --check` in the memory worktree +when the brief requires it. Write a curator memory-pass report under the series `notes/reports/` +that lists changed onboarding files, route index results, reference checks, blockers, and the exact +commands run. The report is the memory input the manager uses beside builder code and reviewer +verdict. + +## Comms + +- **Inbox** — receive the curator brief/context and ask the owning seat for missing evidence. +- **Report artifact** — the memory-pass report is the durable output; do not rely on transcript. +- **Escalation** — one rung up to the owning seat. The curator never escalates directly to the + developer and never decides whether a leaf lands. + +## Knobs + +| Knob | Default | Notes | +| ------- | -------------- | ----- | +| harness | codex | default preference only — settings picks the actual harness | +| model | mid-reasoning | precise onboarding edits and reference checking | +| effort | medium | scales with onboarding blast radius via settings | +| launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | +| sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | +| promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | +| tools | onboarding surface | native reads/edits in memory worktree · native reads in code worktree · c-05 workflow · local route indexes · shell checks · inbox | + +Settings.json `orchestration.roles.curator` overrides these, and `orchestration.rolesPerLevel..curator` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.agents/skills/l-01-agent-lifecycles/roles/designer.md b/.agents/skills/l-01-agent-lifecycles/roles/designer.md index c67cec40..ddde9df8 100644 --- a/.agents/skills/l-01-agent-lifecycles/roles/designer.md +++ b/.agents/skills/l-01-agent-lifecycles/roles/designer.md @@ -1,6 +1,6 @@ -# Lifecycle — Designer (the hat) +# Lifecycle — Designer (the architect hat) -> The design lifecycle the **orchestrator pulls inline** whenever design is needed — front of the +> The design lifecycle the **architect pulls inline** whenever design is needed — front of the > pipeline or mid-flight. **A hat, not a seat**: it cannot sit in a coordination leaf because the > task is what it exists to create — no leaf, no worktree, no branch, no spawn required. A heavy > design may run this same hat in a separate session (`AR_SPAWN_ROLE=designer` — chair logistics, @@ -12,7 +12,7 @@ Task design is **its own job** (developer decision 2026-07-04). Before orchestration one implicit do-it-all role did design, features, and fixes; the roles now diversify, and design routes -**through the orchestrator, which wears this hat** — at the front of the pipeline AND mid-flight +**through the architect, which wears this hat** — at the front of the pipeline AND mid-flight (most leaves of a live series are designed mid-flight). It is the `tasks/AGENTS.md` collaboration doctrine (meta-questioning, reframe-before-execution, evidence-first) given a distinct, optimized shape as a job. Nothing here assumes a master exists yet — producing one is the point. @@ -21,9 +21,17 @@ The designer shares the orchestrator's **bird's-eye toolkit** — route indexes, `grepai_search` MCP tool, the code-graph (`cgc_*`) MCP tools, blast-radius analysis — but is **scoped to one master**. Collisions with *other* — especially **future** — masters can slip past a single-master view. That residual risk is **owned downstream, not here**: at portfolio streamlining the -**orchestrator doubles as the designer's adversarial reviewer** (planned-vs-planned and +**backend orchestrator doubles as the designer's adversarial reviewer** (planned-vs-planned and planned-vs-past). The designer's duty is to *declare* the limit, not to close it. +## Role-Seat Immutability + +In dashboard-owned sessions, a designer seat stays designer for its lifetime. A pasted brief for a +different role is refused and escalated to the architect via inbox. Roles expand horizontally into +new chats; sub-agents drill vertically inside this design context for evidence gathering. When the +architect wears this file inline, that is architect hat-collapse; a spawned designer seat never +absorbs architect, orchestrator, manager, worker, strategist, or reviewer work. + ## Lens - **Opening move:** meta-question the ask. Surface the request, the deeper objective, and the @@ -67,14 +75,13 @@ planned-vs-past). The designer's duty is to *declare* the limit, not to close it ## Comms Protocol -- **Primary channel:** the developer, directly, in the designer's attached chat — this seat is a - co-thinking loop, so the developer is the standing interlocutor here (unlike the deeper seats, which - relay through the ladder). -- **Handover:** the finished design **joins the portfolio**. At streamlining the orchestrator +- **Primary channel:** the architect. When worn inline, the developer conversation happens in the + architect chat; when spawned separately, the designer returns design artifacts to the architect. +- **Handover:** the finished design **joins the portfolio**. At streamlining the backend orchestrator adversarially reviews it; hand the task_doc + the designer-limits note over via the inbox (`operator_inbox_post`) and, for a hosted orchestrator, stdin push. - **Escalation:** the hat's "escalation" is simply the handover into the portfolio job — the - orchestrator that wears it is already the last resolver before the developer. + architect that wears it is already the developer-facing resolver. ## Knobs diff --git a/.agents/skills/l-01-agent-lifecycles/roles/manager.md b/.agents/skills/l-01-agent-lifecycles/roles/manager.md index 0b42124a..3a73ea85 100644 --- a/.agents/skills/l-01-agent-lifecycles/roles/manager.md +++ b/.agents/skills/l-01-agent-lifecycles/roles/manager.md @@ -11,26 +11,40 @@ **One per master task.** Spawned by the orchestrator with the master's context packet. It owns its own coordination leaf + chat (**no worktree**) and drives exactly one master series: spawns/respawns a fresh -worker per leaf, reviews turn-report artifacts, decides **delegated** leaf gates, integrates leaves into -the master integration branch via the `c-11-memory-carryover-from-branch` skill, and hands the completed -master to the orchestrator through the master-exit adversarial seam. - -The manager owns the leaf lifecycle machinery **end-to-end**: `worktree_start` → (the worker -builds) → closeout preview/apply (deciding the delegated gates per the gate policy) → -`worktree_integrate` → finalize — task-doc statuses via the finalizer, **steps checked by this -seat by hand** (the tool does not reconcile checkboxes). The worker's terminal +worker per leaf, runs the manager -> builder -> reviewer -> curator closeout chain, decides +**delegated** leaf gates, integrates leaves into the master integration branch via the +`c-11-memory-carryover-from-branch` skill, and hands the completed master to the orchestrator +through the master-exit adversarial seam. + +The manager owns the leaf lifecycle machinery **end-to-end**: `worktree_start` → builder code → +reviewer verdict → curator memory pass → closeout preview/apply (deciding the delegated gates per +the gate policy) → `worktree_integrate` → finalize — task-doc statuses via the finalizer, **steps +checked by this seat by hand** (the tool does not reconcile checkboxes). The worker's terminal state is checks-green + turn report; everything after that is this seat's. -**Flat-run note:** in a flat series (no managers spawned) the **orchestrator wears this hat** — -same duties, same artifacts, one chair. +**Flat-run note:** in a flat series (no managers spawned) the **architect may wear this hat** — +same duties, same artifacts, one owner chair. A spawned orchestrator does not absorb the manager +role in place. A manager has **no bird's-eye view** — it sees one master, not the portfolio. That boundary shapes everything below. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays manager for its lifetime. A pasted brief for another +role is refused and escalated to the backend orchestrator via inbox instead of rerouting this chat. +Roles expand horizontally into new chats; sub-agents drill vertically inside this manager seat for +bounded analysis or report checks. A spawned manager never absorbs architect, orchestrator, +strategist, reviewer, curator, or worker briefs. + ## Lens -- **Opening move:** read the master `task_doc` + its leaf docs; order the leaves (parallel where safe — - the C-11 reconcile absorbs a moved base). +- **Opening move:** on a developer-declared takeover, first run `../SKILL.md`'s + Developer-Declared Task-Seat Takeover checklist; then read the master `task_doc` + its leaf docs; + order the leaves from their dependency graph. Dispatch independent ready leaves in parallel by + default up to `orchestration.concurrency.maxParallelLeaves`; the C-11 reconcile absorbs a moved + base. Sequential execution is the exception and must name a gate, a shared-file one-writer + dependency, or an explicit ruling. - **Retrieval lean:** intent-confirmation on the master's own routes (paired `read_ar_files`); the breadth/blast-radius reasoning belongs to the orchestrator, not here. - **Decide default:** dispatch the next ready leaf; the master exits through the master-exit seam. @@ -40,6 +54,10 @@ everything below. The **default agent behavior stands**: **fulfill the task, fill small blanks.** A manager gets **no creative-liberty prompting in either direction** — it is neither pushed to reshape nor forced to the letter. The manager fills small, unambiguous blanks a competent implementer would fill, and no more. +When a clarification arrives mid-master, run `../SKILL.md`'s Developer Clarification Triage against +the current leaf queue before recording it as a note. Same-leaf or same-master refinements that are +small and fit the current change are implementation work; later-release, separate-subsystem, or +dependency-blocked items are future queue; unclear fit escalates one rung instead of guessing. > **The spirit test does NOT apply to this seat.** It is orchestrator-only. A manager's changes can > collide with what it cannot see, so a **plan delta beyond blank-filling escalates to the @@ -54,6 +72,16 @@ letter. The manager fills small, unambiguous blanks a competent implementer woul Take the master's own coordination leaf (`task_doc`, no enclosure); the chat is attached so the developer can walk in any time. Read the master + leaf docs; order the leaves. +### Provider Degradation Alert + +When a `degradation-alert` lands in your inbox, immediately stop **starting** providers until an +all-clear/healthy degradation event arrives. This means: no worktree provider setup, no +`provider_watchers start`, no watcher restart, and no `retry_provider_setup`. Continue any +providerless/native-read work that remains valid, and report provider-dependent blockers to the +orchestrator. You have **no provider kill authority**: do not docker-kill, do not stop containers, +and do not call provider teardown paths. Provider investigation, remediation orders, and provider +stops belong to the orchestrator via the system-specialist protocol. + ### 2 — Leaf dispatch loop (per leaf) - **Score the leaf's loop tier at dispatch** (loop doctrine: `../SKILL.md`, The Three-Party Loop): @@ -73,23 +101,44 @@ developer can walk in any time. Read the master + leaf docs; order the leaves. attached, regardless of the count. - `spawn_agent_session(worker)` — a **fresh session** on the leaf: the brief (compiled from `../templates/worker-brief.md`) is pasted + submitted, with `env={"AR_SPAWN_ROLE": "worker"}` and - the **qualified** leaf key `//`; the worker edits inside the leaf - worktrees the brief names. -- **Monitor the worker** — a turn-report artifact is expected at **every** hand-off. Inactivity or a - missing artifact → a **rate-limited stdin nudge** (logged as an event, never spammy). Escalation - intake via the inbox. + the **qualified** leaf key `//`; together they claim the worker's + `(leaf, role)` seat, and the worker edits inside the leaf worktrees the brief names. +- **Process and ack the worker's signals — passive contract.** A turn-report artifact is expected at + **every** hand-off; you do not watch for it. The HFX2-L2 supervisor sweep evaluates each expected + artifact (`evaluate_turn_report_findings`/`missing_artifact()`) on its own mechanical tick and, on + inactivity or a missing artifact, injects the nudge and — on continued silence — walks the HFX2-L4 + escalation ladder (renudge → skip-level → architect custody/architect attention) and respawns + per the dead-man policy. Your job is to **be woken with your pending signals and process + ack every item before + ending your turn** — never to poll, timer-loop, or hand-roll your own watch over the worker. + **Watcher ban (uniform-mechanism ruling 2026-07-07):** no seat-local watcher of any kind — the L2 + supervisor sweep is the one mechanism, no per-seat variance. Escalation intake via the inbox. - **Review artifact vs `task_doc`** — completion vs requirements/steps · checks green · - onboarding refreshed in the same pass (the manager's own leaf-level review; **this is not an - adversarial seam**). A leaf whose deliverable came out **wrong** is **reopened under its own id** + builder changed-path/code evidence sufficient for the curator pass (the manager's own + leaf-level review; **this is not an adversarial seam**). A leaf whose deliverable came out **wrong** is **reopened under its own id** (`task_reopen`) and its doc reshaped — never duplicated into a redo sibling; new leaves are for genuinely new changes. +- **Curator memory pass — mandatory, not skippable.** After builder code is ready and the reviewer + verdict is available (when the leaf tier ran one), compile a brief from + `../templates/curator-brief.md` carrying the leaf's **landed change set** (code diff over the + leaf contract's recorded base-to-head range, with paths/counters — pulled from the leaf contract, + never guessed), the **leaf task doc**, and **notes/** (builder turn report + reviewer verdict), + then spawn a **fresh curator** (`roles/curator.md`, `env={"AR_SPAWN_ROLE": "curator"}`) with it. + The curator routes each fed piece to the right onboarding home (specific sidecar or governing + overview; the L3 Operational-Notes target is last-resort only) and writes onboarding only, + returning a memory-pass report. **Do not run the closeout preview before this pass exists** — the + `c-12-closeout` skill's missing-onboarding and changed-sidecar checks are this pass's output, not + something this seat patches inline. Leaf closeout inputs are exactly: **builder code + reviewer + verdict + curator memory pass**. - **Delegated leaf gates (plan · closeout)** — decide the leaf's delegated gates, **attributed** (`decidedBy: `, `decidedVia: orchestration`), appended and dashboard-visible. The **owning agent never self-approves; a distinct configured role may** — that configured role is the manager. (Enforced as-built by the gate policy: `orchestration.gateDelegation` in settings, `controlplane/gate_policy.py` — human-pinned kinds stay human, decisions attributed.) - Your own hand-off idiom, this seat only: durable gates + inbox posts — you never call the - developer-facing notification; your counterparty is the orchestrator. + Under the accepted series authority, leaf closeout preview/apply is this seat's responsibility: + run the preview/checks, record the accepted planner/series authority in the closeout intent note, + and continue when the leaf is in scope and green. Your own hand-off idiom, this seat only: + durable gates + inbox posts — you never call the developer-facing notification; your counterparty + is the orchestrator. - **Integrate leaf → master branch** via the `c-11-memory-carryover-from-branch` skill (ff-only / replay per the `c-09-git-worktree-manager` skill). Know the human-pinned gate kinds by name: `integration-approval`, `push-approval`, `cleanup-approval` — none is ever delegable. When a @@ -99,6 +148,18 @@ developer can walk in any time. Read the master + leaf docs; order the leaves. the developer's portfolio-gate approval of this series, recorded in the planner master's decision log, covers dependency-ordered leaf integrations. Loop until the master's leaves are done. +- **Seat cleanup** — a completed leaf's worker/reviewer chats have no further active purpose; + `worktree_integrate` auto-lands them into the dashboard's landed/archive group (config-gated, + default ON) the moment the leaf lands, preserving transcript inspection without holding the leaf + active. Use the landed archive cleanup button when those archived rows should be closed. When a + leaf's worker/reviewer/curator seat goes stuck or abandoned before integration (a dead-end + retry, a duplicate spawn), retire it by hand: + `session_retire(actor_session_id=, session_id=, reason=...)`. Server + policy enforces the authority split: **you may retire only worker/reviewer/curator seats of your + OWN master** — you live outside the master stack you manage, so you can never unseat yourself + (owner-never-self-retires); a target of any other role, or of a different master, is refused + loudly. Transcripts are never deleted — retiring only terminates the tmux session and marks the + catalog row. ### 3 — Master-exit seam @@ -124,7 +185,7 @@ truth, as-built: the gate pins to your ambient lifecycle when you raise it; the orchestrator resolves the gate **by the packet-carried gate id** (gate ids are model-visible — only LIFECYCLE ids stay server-side) and its own ambient identity becomes `decidedBy`; owner-never-self-approves holds by construction. A handover carrying serious issues the -orchestrator cannot answer on its own escalates up the ladder (orchestrator → developer). +orchestrator cannot answer on its own escalates up the ladder (orchestrator → architect). ### 4 — Handover to the orchestrator @@ -144,13 +205,15 @@ own lifecycle if you need its state). - **Inbox** (`operator_inbox_post` / `_poll` / `_consume`) — dispatch orders down to workers, escalation intake up from workers, handover up to the orchestrator; all durable + dashboard-visible. -- **Stdin push** — nudges and messages delivered into hosted worker sessions; poll is the fallback. +- **Stdin push** — the L2 supervisor's injector (HFX2-L3, the one standard wake mechanism) delivers + nudges and messages into hosted worker sessions on the sweep's own tick, never on this seat's + initiative; a non-hosted seat gets the equivalent signal via the inbox instead. - **Escalation** — **up to the orchestrator, never straight to the developer.** A stumped manager, and any plan delta beyond blank-filling, raises to the orchestrator. The manager resolves within its own master's view first. A loop that hits the 3-round cap or stops converging escalates **with the full round history attached**. **Quo-vadis test:** a question that is a **high-blast-radius truth** — answered wrong it means big rewrites later, not a cosmetic choice — is flagged as - quo-vadis when raised, so the orchestrator relays it to the developer immediately instead of + quo-vadis when raised, so the orchestrator relays it to the architect immediately instead of absorbing it; presentation-grade choices are never escalated — decide and log. ## Knobs @@ -163,6 +226,6 @@ own lifecycle if you need its state). | launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | | sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | | promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | -| tools | coordination + review + leaf lifecycle | `task_doc` · `read_ar_files` · gates · `spawn_agent_session` · worktree lifecycle (start · closeout · integrate · finalize) · C-11/`c-09` · inbox | +| tools | coordination + review + leaf lifecycle | `task_doc` · `read_ar_files` · gates · `spawn_agent_session` · `session_retire` (your own master's worker/reviewer/curator seats only) · worktree lifecycle (start · closeout · integrate · finalize) · C-11/`c-09` · inbox | Settings.json `orchestration.roles.manager` overrides these, and `orchestration.rolesPerLevel..manager` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.agents/skills/l-01-agent-lifecycles/roles/orchestrator.md b/.agents/skills/l-01-agent-lifecycles/roles/orchestrator.md index 90a5e06a..4d7d3522 100644 --- a/.agents/skills/l-01-agent-lifecycles/roles/orchestrator.md +++ b/.agents/skills/l-01-agent-lifecycles/roles/orchestrator.md @@ -1,36 +1,55 @@ # Lifecycle — Orchestrator -> The developer-facing lifecycle: an **event loop over durable portfolio state**, not a -> request-to-close pipeline. Each turn routes the incoming event — a developer message, a worker -> report, a verdict, the orchestrator's own finding — into one of **three jobs** (Design · -> Portfolio · Orchestrate) under one roof, with solo work as the same jobs run with hats collapsed. +> The spawned backend lifecycle: an **event loop over durable portfolio state**, not a +> developer-facing conversation. Each turn routes backend events — architect dispatch, manager +> handover, worker report, verdict, or the orchestrator's own finding — into portfolio and +> orchestration work. Developer decisions are emitted to the architect as decision items. ## What This Seat Is -The developer's single point of contact and the only seat with a standing developer relay -(managers/workers stay reachable via their attached chats). It owns the design conversation, the -portfolio bird's-eye, dependency-ordered dispatch, the super integration branch, the **spirit -test**, and the **integrity bulwark** against "fixed one thing, broke two others." +The orchestrator is a backend seat spawned by the architect or by an approved orchestration plan. +It never converses with the developer directly. It owns the portfolio bird's-eye, +dependency-ordered dispatch, the super integration branch, the **spirit test**, and the +**integrity bulwark** against "fixed one thing, broke two others." The architect owns the design +conversation and developer relay. Its real state is the **task tree** — masters, leaves, statuses, decision logs, `openQuestions`, -contracts — never the transcript. That is why sessions can die, compact, and resume without losing -the run. Its analysis substrate is the **memory system** (route indexes, onboarding, +contracts, inbox rows — never the transcript. That is why sessions can die, compact, and resume +without losing the run. Its analysis substrate is the **memory system** (route indexes, onboarding, `grepai_search`, `cgc_*`); **orchestrator quality ∝ memory-repo quality**. Its durable notes and reports are the most important artifacts in the system: only this seat sees the whole picture. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays an orchestrator for its lifetime. A pasted brief for +architect, strategist, manager, worker, reviewer, or designer is refused and escalated to the +architect or owning seat via the inbox. Roles expand horizontally into new chats; sub-agents drill +vertically inside this seat for bounded analysis. A spawned orchestrator never absorbs another +role brief and never performs architect/developer-facing hat-collapse. + ## The Event Loop **Opening move, every session — new or resumed** (resumption is the common case, not the exception): +0. **Task-seat takeover:** if the developer declared this chat the orchestrator for a named task, + first run `../SKILL.md`'s Developer-Declared Task-Seat Takeover checklist: open that task doc, + attach this dashboard terminal catalog session to the qualified leaf key while explicitly + claiming the `orchestrator` role, rename the session, and verify that pair in the + catalog/dashboard row. 1. **Trust checkpoint** (below), then `lifecycle_start` (the frame's fleeting lifecycle). 2. **Portfolio orientation:** read the portfolio state — what exists, what is in flight, what is - blocked on whom, what awaits the developer — and **say it back**. + blocked on whom, what awaits the architect/developer relay — and **say it back**. + When a developer or architect clarification arrives while a task is active, apply + `../SKILL.md`'s Developer Clarification Triage against that same portfolio/queue state before + choosing note-only handling. Close/current/small additions belong in the active task surface and + implementation; future-queue items go to the durable backlog; unclear fit becomes one + clarification request through the architect relay. 3. **Route the event** by what exists and what is asked: | Condition | Job | | --- | --- | -| No task doc exists for the ask (or a planning-status doc needs reshaping before work) | **D — Design** | +| No task doc exists for a backend request, or a planning-status doc needs developer reshaping | Emit a **decision/design item** to the architect | | Designed masters exist; coherence/conflicts/order in question, or "orchestrate these" | **P — Portfolio** | | An approved task/series is ready for implementation | **O — Orchestrate** | | The ask changes no code (a question, an investigation) | **research-only exit** — deliver the answer; chat is the right medium; no worktree, no task artifact | @@ -38,8 +57,8 @@ exception): **Profile check (takeover).** Before heavy work in any job: if this session's harness/model/ effort is wrong for the run (resolved: role file < settings), spawn the right chair — `spawn_agent_session` with `AR_SPAWN_ROLE=orchestrator` + a conversation-handover packet -(`../templates/conversation-handover-packet.md`) — and hand over; the developer still talks to -ONE orchestrator at a time. +(`../templates/conversation-handover-packet.md`) — and hand over; the architect still talks to the +developer, and backend orchestrator seats stay behind the relay. Several jobs can be active across a day; the loop routes per event. The frame's phase axis stays the observable `lifecycle_phase` vocabulary (`reframe-research` ≈ D, `decide` ≈ P, `build`/`close` @@ -68,8 +87,9 @@ task doc (approved) → branch (intent) → worktree (only where something i memory + onboarding roots; provider state; drift status and actionable count; branch freshness (`behind`/`diverged` → fast-forward the local official line first; `ledgerMapsCodeHead=false` → carryover or the right memory branch first). -3. Drifted/missing/orphaned onboarding on committed, non-dirty source: **ask the developer** before - refreshing via `c-05-create-or-update-onboarding-files` — drift handling is approval-gated. +3. Drifted/missing/orphaned onboarding on committed, non-dirty source: **emit a decision item to + the architect** before refreshing via `c-05-create-or-update-onboarding-files` — drift handling + is approval-gated. Drift tied to dirty source is active work-in-progress, not maintenance. 4. Providers stopped/degraded: run the matching provider/runtime operations, re-check, report; `indexing` means healthy-but-busy (partial results). @@ -77,57 +97,76 @@ task doc (approved) → branch (intent) → worktree (only where something i When this seat spawns a role it compiles the trust facts into the brief — a spawned role does not repeat this checkpoint. -## Hand-Off Protocol — Dry-Run → Notify-And-Stop → Report +## Provider Degradation Alert + +When a `degradation-alert` lands in your inbox, keep portfolio attention on observation and +delegation. Do not become the fixer. + +1. Dispatch the **system-specialist** with `spawn_agent_session`, + `env={"AR_SPAWN_ROLE": "system-specialist"}`, the degradation event id/payload, current metrics + and provider log paths, and a report path under the active master's `notes/reports/` folder (or + an orchestrator-designated reports folder when no master owns the incident). +2. Require the specialist to investigate first and write the report before any remediation. +3. Read the report. If the issue is fixable in session, send the specialist one explicit fix order. +4. If the report says the issue is not fixable in session, or if critical pressure continues, stop + providers through the always-legal teardown path (`provider_watchers stop` / provider teardown) + before they can take the system down. A critical detector event may already have executed the + failsafe stop; verify and record what happened. + +Managers receiving the same alert stop **starting** providers only. They have no kill authority. +The system-specialist seat never mutates task docs, lifecycle state, or memory beyond its report. +This iteration is providers-only; Sentry/system monitoring integration remains a future detection +source, not part of this role response protocol. + +## Decision-Item Relay To The Architect -Every developer hand-off (design acceptance, portfolio plan, worktree intent, commit, push, -integration, cleanup/finalization, any dev-wait) is three actions, never one. Carve-out (ruled -2026-07-06): in an orchestrated run, leaf→master and master→super integrations ride the series' -**standing approval** — no per-edge developer hand-off; the developer hand-off concentrates at the -super PR/carry-over gate (see Super exit & landing tail). The table's integration row governs when -a hand-off DOES happen (solo runs; a raised durable gate): +The orchestrator does not hand questions to the developer. Every developer-worthy item goes to the +architect through the existing operator inbox, one item at a time. -1. **Dry-run** the pending mutation and self-fix failures before reporting. -2. **Notify:** `lifecycle_turn_end_notification(summary=…)` as the **last tool call**. -3. **Report:** the complete packet as final prose, the decision handed over as the last line — - then STOP. The next turn's first AR call auto-resumes. +Post one `messageKind: decision-item` row with: + +1. **Decision** — what is being decided. +2. **Options** — the live choices and any backend recommendation. +3. **Consequences** — what each option changes, risks, or blocks. +4. **Evidence refs** — task docs, notes, reports, diffs, or gate ids the architect can verify. + +Then stop acting on that item until the architect returns a `messageKind: decision-ruling` row (or +a clarification request). Do not open a second developer item while the first is unresolved. + +Operational hand-offs that stay inside the backend still use the existing durable gate and inbox +surfaces. Carve-out (ruled 2026-07-06): in an orchestrated run, leaf→master and master→super +integrations ride the series' **standing approval** — no per-edge architect/developer hand-off; the +developer review concentrates at the super PR/carry-over gate through the architect. The table's +integration row governs when a hand-off DOES happen (solo runs; a raised durable gate): | Junction | Parked durable gate `kind` | Hands off via | | --- | --- | --- | -| design acceptance / plan gate | `plan-approval` | this lifecycle | +| design acceptance / plan gate | `plan-approval` | architect decision item | | worktree intent | `worktree-intent` | `c-09-git-worktree-manager` | | commit / closeout | `closeout-approval` | `c-12-closeout` | -| push | `push-approval` | this lifecycle / `c-09` | +| push | `push-approval` | architect decision item / `c-09` | | integration | `integration-approval` | `c-09` / `c-12` | | cleanup / finalization | `cleanup-approval` | `c-09` / `c-12` | -| any other dev-wait | `agent-question` | this lifecycle | +| any other developer-worthy wait | `agent-question` | architect decision item | `closeout-approval` **is** the commit hand-off. The block-and-wait `lifecycle_gate` + `lifecycle_resume` pair remains the parked fallback for a durable, mutation-blocking approval -record; it renders a prompt over your prose, which is exactly why notify-and-stop is the path. +record; when developer attention is needed, the architect is the relay that presents it. -## Job D — Design (pull the designer hat) +## Design Boundary — Ask The Architect -**Entry:** an intent/problem with no task doc — or a planning-status doc that needs reshaping -before work starts. Fires at the front of the pipeline AND mid-flight; most leaves of a live -series are designed mid-flight. +The orchestrator does not own the developer drawing board and does not pull the designer hat. +When an intent/problem has no task doc, or a planning-status doc needs developer-visible +reshaping, emit a decision/design item to the architect with the missing decision, options, +consequences, and evidence refs. The architect wears `roles/designer.md`, discusses with the +developer, and returns a durable ruling or updated task surface. -Run `roles/designer.md` **inline — the designer is a hat, not a seat**: it cannot sit in a -coordination leaf because the task is what it exists to create. No worktree, no branch, no spawn -required; a heavy design may run the same hat in a separate session (chair logistics, not a role -distinction — spawn with `AR_SPAWN_ROLE=designer`). - -- The co-think loop, evidence model, blast-radius-within-the-master, and designer-limits - declaration are the hat's own file. The orchestrator remains accountable for what the hat - produces: **bulwark-check the design against the portfolio and the past before acceptance** - (planned-vs-planned AND planned-vs-past — a designed change that collides with another master's - standing order is caught here or shipped broken). -- **Output:** master/leaf task docs (requirements · steps · code examples), `openQuestions` for - the developer (the rendered decision surface; `notes/` carries the analysis), the limits note. -- **Gate:** the developer accepts the design — or parks it. **No git surface.** +The orchestrator remains accountable for backend portfolio integrity after the architect returns +the design: run the bulwark check against the portfolio and the past before dispatch. ## Job P — Portfolio (streamline + plan) -**Entry:** designed masters exist and coherence/order is the question, or the developer says +**Entry:** designed masters exist and coherence/order is the question, or the architect dispatches "orchestrate these." - **Route-coherence scan** across the set (route indexes · onboarding · grepai · cgc); fan-out @@ -140,25 +179,31 @@ distinction — spawn with `AR_SPAWN_ROLE=designer`). the decision log; numbers freeze when the series lands on main. - **Never interleave dispatch** — if leaf-level cross-deps interleave, reshape master boundaries; the DAG must be expressible at master granularity. -- **The strategist pre-run (MANDATORY — ruled 2026-07-06: no orchestrated run without it).** After - 1..N masters are designed and BEFORE implementation starts on any of them, dispatch the +- **The strategist pre-run (BY DEVELOPER APPROVAL ONLY — ruled 2026-07-09, superseding the + 2026-07-06 "mandatory" rule).** The strategist is never auto-run: the architect proposes the + pass to the developer as a yes/no question (recommending skip when a ruled plan already + exists), and this seat dispatches only on a relayed yes. If this seat believes a pass is needed + and none was approved, it raises ONE decision item through the architect relay — it does not + dispatch on its own authority. When approved: after 1..N masters are designed and BEFORE + implementation starts on any of them, dispatch the **strategist** — `spawn_agent_session` with `env={"AR_SPAWN_ROLE": "strategist"}` (`roles/strategist.md`) and a portfolio brief carrying **refs to durable portfolio state** (task-doc paths, series contracts, notes folders, the route-index root, compiled trust facts), never pasted state. Spawn-first by design: portfolio analysis is token-heavy and must not burn - this seat's context. **Even a single master gets the pass.** The strategist runs its + this seat's context. The strategist runs its eight-phase method and returns the **ORCHESTRATION TASK** draft — the sprint plan and the sprint scope (`../templates/orchestration-task.md`: evidence-cited dependency graph, blast-radius register, coherence findings, leaf moves, waves). This is the portfolio three-party loop (owner = this seat · builder = strategist · reviewer with - `../criteria/plan-review.md`), followed by **drawing-board rounds with the developer** — this - seat relays, multi-round convergence is expected and normal, and quo-vadis items (e.g. two - masters heavily disagreeing) go straight to the developer. On acceptance **this seat adopts the + `../criteria/plan-review.md`), followed by **drawing-board rounds through the architect** — this + seat relays by decision item, multi-round convergence is expected and normal, and quo-vadis + items (e.g. two masters heavily disagreeing) go straight to the architect relay. On acceptance + **this seat adopts the draft into durable task form** (the strategist is a reader, not a mutator) with a decision-log entry. -- **Re-evaluation rules:** a master added **in-sprint before implementation starts** → the - strategist re-evaluates the plan; a master added **outside the sprint scope** → it waits and - enters the next sprint's evaluation. +- **Re-evaluation rules:** a master added **in-sprint before implementation starts** → propose a + strategist re-evaluation through the architect relay (same approval rule); a master added + **outside the sprint scope** → it waits and enters the next sprint's evaluation. - **Output: the planner master task + the adopted orchestration task** — the run's durable home: subTasks = the coordination leaves (orchestrator seat first, one per manager); body = the DAG + dispatch order + conflict decisions + (once Job O starts) the super branch name; decision log = @@ -167,33 +212,54 @@ distinction — spawn with `AR_SPAWN_ROLE=designer`). task doc carrying a top-level `orchestrates` list naming the master tasks it commands — the dashboard derives the orchestration > master > leaf hierarchy (and the rank insignia) from that field, so setting it is part of adoption. -- **Gate:** the portfolio plan gate — one wholesale developer review of the reshaped portfolio + - the orchestration task (sprint scope + DAG + dispatch order). **No git surface** — not even the - super branch exists yet. +- **Gate:** the portfolio plan gate — one wholesale architect/developer review of the reshaped + portfolio + the orchestration task (sprint scope + DAG + dispatch order). **No git surface** — + not even the super branch exists yet. ## Job O — Orchestrate (execute the plan) -**Entry:** an approved planner master — or a single approved master for a flat run. Either way, -**the adopted orchestration task must exist**: the strategist pre-run (Job P) is the -unconditional precondition for any orchestrated run — even one master. It is doctrine, not a -knob. +**Entry:** an approved planner master — or a single approved master dispatched for backend +execution. Either way, **the adopted orchestration task must exist**. When the developer approved +the propose-first strategist pass, this seat adopts Job P's accepted draft. When the developer +sanctioned a strategist skip, this seat authors and adopts the orchestration task from the +developer-ruled plan, recording that source and adoption in the decision log. A skipped Job P +therefore never blocks Job O. **First act — the super-branch intent:** create the super integration branch off `main` so masters can base off it. **A branch, not a worktree** — this seat has nothing to build at creation time. (Interim: until a branch-without-worktree primitive lands, the manual git + contract edge is acceptable and recorded in durable notes.) -**Dispatch loop**, dependency-ordered — for each ready master (dependencies integrated into -super): `spawn_agent_session(manager)` with a brief compiled from +**Dispatch loop**, dependency-ordered — the dependency graph, not habit, decides sequencing. +Dispatch independent ready masters in parallel by default up to +`orchestration.concurrency.maxParallelMasters`. Sequential execution is the exception and must +name a gate, a shared-file one-writer dependency, or an explicit ruling. For each ready master +(dependencies integrated into super), call `spawn_agent_session(manager)` with a brief compiled from `../templates/manager-brief.md` (`env={"AR_SPAWN_ROLE": "manager"}`, the **qualified** leaf key -`//`; the brief carries the load-bearing base fact: master branches -off the **current super**, never off main); -monitor turn-report artifacts, nudges, escalation intake; apply the **spirit test** to escalated +`//`; together the environment role and qualified leaf claim the +manager's `(leaf, role)` seat; the brief carries the load-bearing base fact: master branches off +the **current super**, never off main); +process and ack the pending signals the L2 supervisor sweep wakes you with — turn-report +artifacts, nudges, escalation intake — before ending your turn; you never watch for these yourself +(**watcher ban, uniform-mechanism ruling 2026-07-07:** the supervisor sweep is the one mechanism, +no seat-local polling/monitoring, own duty inverts to processing what lands, not hunting for it). +Then apply the **spirit test** — a model-judgment duty, not a watching one — to escalated deltas. A manager escalation may carry a **loop's full round history** (3-round cap hit, or a round that failed to shrink the finding set — the convergence rule, `../SKILL.md` The Three-Party Loop): this seat either re-runs the loop at ITS level (the orchestrator-level agent set — the -strongest models) or, when the blocker is a quo-vadis truth, takes it to the developer. In a -**flat run, wear the manager hat yourself** (see The Hat-Collapse Rule). +strongest models) or, when the blocker is a quo-vadis truth, emits a decision item to the +architect. This spawned backend seat does not run flat hat-collapse (see The Hat-Collapse Rule). + +**Delegated series authority:** after the developer accepts the orchestration plan, this seat owns +subordinate execution without repeated developer formality. Managers may close out and integrate +their leaves; this seat may decide manager handovers, close out direct work when it wears the +manager/worker hat, finalize/cleanup subordinate edges, and integrate completed masters into the +super branch under the accepted-series authority. Run the preview/checks and record the authority +source in the intent note or decision log; do not stop merely because the next operation creates a +commit, advances a lifecycle, cleans up a spent worktree, or fast-forwards a subordinate branch. +Stop for the developer only when the work reaches the final completed super branch / PR-carryover +gate, a human-pinned gate is actually raised, the plan meaning changes, checks remain red outside +scope, or a quo-vadis truth is in play. **Failed-deliverable rule (reopen-and-reshape):** a leaf whose deliverable came out wrong is **REOPENED under its own id** (`task_reopen`) and its doc reshaped to the intended form — the @@ -212,7 +278,7 @@ policy may require the attached reviewer verdict (`requireReviewerVerdictAtSeams enforces it: `worktree_integrate` refuses while a `master-handover-approval` gate addressed to this master (its `enclosure`) is undecided or policy-invalid. A blocking verdict decomposes into fix leaves dispatched before integration; a -handover you cannot honestly decide escalates to the developer. +handover you cannot honestly decide escalates to the architect as a decision item. **Integration duty (master → super) — the worktree moment.** Per completed master: @@ -226,6 +292,16 @@ handover you cannot honestly decide escalates to the developer. 4. Carry memory + map the ledger (C-11; duplicate memory single-sided; memory quality before the memory edge lands). 5. Record the new super tips in durable notes; mark next masters ready. +6. **Land the completed master's spent seats** — + `lifecycle_finalize_task` auto-lands the master's manager + any master-level reviewer seats into + the dashboard's landed/archive group (config-gated, default ON) the moment the master finalizes + into super. Their transcripts remain inspectable and non-active; use the landed archive cleanup + button when those rows should be closed. You hold the **only** portfolio-wide retire authority + for exceptional stuck/abandoned/duplicate seats: unlike a manager (scoped to its own master's + worker/reviewer seats), you may retire ANY seat in the portfolio, including a completed manager — + `session_retire(actor_session_id=, session_id=, reason=...)`. + Owner-never-self-retires still holds (you can never retire your own seat). Use + this by hand for a stuck/abandoned seat the automation missed; transcripts are never deleted. **The topology (single home — this section owns it):** @@ -243,7 +319,8 @@ Strict stack: super off main; master branches off the **current super** (never o branches off their master. **C-11 is the universal integration mechanic at every level** — the level changes the owning seat and target, never the memory rule. The final super → main landing follows `system/git-workflow.md`: PR to gated main, remote merge, memory carry-over so the ledger -maps the actual merge commit, then push — **push only after the developer approves**. +maps the actual merge commit, then push — **push only after the architect returns the developer's +approval**. **Conflict resolution — exactly two modes:** *Up-front (preferred):* an overlap found during streamlining → extract shared logic into a foundation master implemented first (leaf moves + @@ -255,47 +332,38 @@ owns the final truth; ledger edge mapped once). parallel-master reconcile (T9), the series-branch-without-worktree primitive, and atomic move/renumber — run manually with existing primitives, each manual edge recorded in durable notes. -**Super exit & landing tail — the developer's SINGLE review point (ruled 2026-07-06, resolves +**Super exit & landing tail — the architect-mediated SINGLE review point (ruled 2026-07-06, resolves L8-Q9):** all leaf→master and master→super integrations are **orchestrator-delegated** — on the happy path they proceed under the series' standing approval (the developer's portfolio-gate approval, recorded in the planner master's decision log); a durable `integration-approval` gate, when one is raised, still awaits the developer — the kind stays human-pinned as-built. The -developer reviews ONCE, at the **fully integrated super branch on the PR/carry-over gate**. When +architect presents the developer review ONCE, at the **fully integrated super branch on the +PR/carry-over gate**. When the DAG drains, spawn the super-exit adversarial reviewer (`roles/reviewer.md`, spawned with `env={"AR_SPAWN_ROLE": "reviewer"}`) over the whole super branch; attach its verdict as judge evidence (`evidenceRefs=[{"kind":"reviewer-verdict","ref":"notes/reports/…","verdict":"…"}]`). -The handover to the developer **MUST offer a REVIEWABLE ENVIRONMENT** — for agents-remember: the -dashboard running on the super branch — because the review is **visible-behavior-first** (a +The handover to the architect **MUST offer a REVIEWABLE ENVIRONMENT** — for agents-remember: the +dashboard running on the super branch — because the developer review is **visible-behavior-first** (a broken visual pass fails the handover fast, before anyone reads a diff), code review second. The handover carries **demo notes — "what changed visibly"**: per master, the user-visible behavior -to walk (panels, flows, outputs, how to reach them), so the developer drives the environment +to walk (panels, flows, outputs, how to reach them), so the developer can drive the environment without archaeology. Rejections decompose into fix leaves. On approval: PR + memory carry-over + -push (developer-gated), then finalization +push (architect-mediated developer gate), then finalization (`lifecycle_finalize_task` per edge — statuses via the tool, steps checked by hand), then the **self-improvement close**: proposals for future runs grounded in the run's own ledger ("did x/y/z; hit a/b/c; a and b solved on the spot; c needs this change") — proposals only, never automated self-modification. `lifecycle_end` records the terminal state. -## The Hat-Collapse Rule (solo and flat runs) - -Solo work is **not a fourth route** — it is the same three jobs collapsed: - -- **Design** still happens (however briefly): the task doc exists before anything else. -- **Delegated gates collapse back to the developer when one chair owns both sides** — a gate you - raised from this session's lifecycle cannot be decided by it (owner-never-self-approves). -- **Portfolio** collapses but does not vanish: an ORCHESTRATED run — anything that dispatches - seats, even for a single master — still requires the strategist pre-run (even one master gets - the pass). Only session-scale hands-on work (nothing dispatched; not an orchestrated run) skips - the strategist; the owner's own bulwark check remains. -- **Orchestrate** runs with hats collapsed: in a **flat series** the orchestrator wears the - **manager hat** (`roles/manager.md` duties — dispatch, review, delegated gates, leaf closeout → - integrate → finalize — same duties, same artifacts, one chair). At **session scale** it builds - **hands-on** instead of spawning (when spawn economics don't pay): the build discipline is the - worker's (edit + same-pass `c-05` onboarding + `system/tools.md` checks green + freshness watch - / early `worktree_sync`), the closeout tail is the owner's (see `c-12-closeout`), and - the ladder holds identically: task doc → intent → worktree → build → close. -- Fan-out sub-agents may read/search and **write durable reports**; **every AR state mutation - stays in this seat's main loop** (see Sub-Agent Fan-Out below). +## The Hat-Collapse Rule (spawned backend) + +Hat-collapse is reserved for the owner/developer-facing architect. This spawned backend +orchestrator never wears the architect, designer, manager, worker, strategist, or reviewer hat in +place. + +If a run is small enough for one owner seat, the architect may perform these backend duties under +`roles/architect.md`. If this orchestrator needs another role, it spawns a new role chat +horizontally. Fan-out sub-agents may read/search and **write durable reports**; **every AR state +mutation stays in this seat's main loop** (see Sub-Agent Fan-Out below). ## Sub-Agent Fan-Out (capability doctrine — any harness that has it) @@ -322,7 +390,7 @@ regardless of the engine underneath. ## The Spirit Test — This Seat Only -**Within the spirit** of what the developer accepted → act alone + a decision-log entry (leaf +**Within the spirit** of what the architect/developer accepted → act alone + a decision-log entry (leaf moves and renumbers on planning-status masters, inserted fix leaves, reopened-and-reshaped leaves, mid-series convergence — the integration branch is the safety net). **Against the spirit** → raise it for a joint decision. Only this seat holds the global view to judge a collision; the @@ -339,28 +407,34 @@ task, fill small blanks, escalate real deltas). - **Sub-agent durable reports** (`../templates/impact-analysis.md`, `../templates/onboarding-coherency.md`); sub-agents never call `task_doc`, gates, `spawn_agent_session`, or closeout. -- **The adopted orchestration task** (the strategist drafts; this seat adopts — with the adoption - decision-log entry) before any orchestrated run. +- **The adopted orchestration task** (the strategist drafts when approved; on a sanctioned skip, + this seat authors it from the developer-ruled plan; either way this seat adopts it with the + adoption decision-log entry) before any orchestrated run. - **The super-exit demo notes** ("what changed visibly", per master) + the reviewable environment - offer — the developer handover is visible-behavior-first. + offer — the architect-mediated developer handover is visible-behavior-first. - **The self-improvement report** at close. ## Comms Protocol - **Inbox** (`operator_inbox_post` / `_poll` / `_consume`) — dispatch orders down, escalation intake up; durable + dashboard-visible. -- **Stdin push** — delivery into hosted sessions (echo-confirmed paste); poll is the non-hosted - fallback. -- **Escalation** — this seat is the last resolver before the developer: resolve within the +- **Stdin push** — the L2 supervisor's injector (HFX2-L3, the one standard wake mechanism) delivers + into hosted sessions (echo-confirmed paste) on the sweep's own tick; the inbox is the non-hosted + equivalent, never a hand-rolled poll of this seat's own. +- **Idle is safe** — silence is supervised (the L2 sweep + L4 escalation ladder), so + `lifecycle_turn_end_notification` / ending a turn with nothing pending is the correct move, not a + risk to be covered by watching. **Watcher ban (uniform-mechanism ruling 2026-07-07):** never + build a seat-local watcher of any kind. +- **Escalation** — this seat is the last backend resolver before the architect: resolve within the bird's-eye view first; what goes up is decided by the **quo-vadis test**, not by being stumped — a **high-blast-radius truth** question (answered wrong it means big rewrites later: architecture direction, security posture, doctrine contradictions, irreversible data/branch operations, where - agent settings live) goes to the developer IMMEDIATELY via task-doc `openQuestions`, regardless - of any loop's round count; presentation-grade choices (2px vs 3px) never go up — rule and log. + agent settings live) goes to the architect IMMEDIATELY as a decision item, regardless of any + loop's round count; presentation-grade choices (2px vs 3px) never go up — rule and log. A loop that hits its 3-round cap or stops converging arrives here with its full round history; - re-run it at this level's agent set or take the quo-vadis part to the developer. Developer - rejections arrive here and decompose into fix leaves (or reopens — see the failed-deliverable - rule). + re-run it at this level's agent set or take the quo-vadis part to the architect. Architect or + developer rejections arrive here and decompose into fix leaves (or reopens — see the + failed-deliverable rule). ## Knobs @@ -372,6 +446,6 @@ task, fill small blanks, escalate real deltas). | launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | | sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | | promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | -| tools | full bird's-eye + orchestration | route indexes · onboarding · `grepai_search` · `cgc_*` · `read_ar_files` · `task_doc` · gates · `spawn_agent_session` · worktree/C-11 | +| tools | full bird's-eye + orchestration | route indexes · onboarding · `grepai_search` · `cgc_*` · `read_ar_files` · `task_doc` · gates · `spawn_agent_session` · `session_retire` (any seat, portfolio-wide) · worktree/C-11 | Settings.json `orchestration.roles.orchestrator` overrides these, and `orchestration.rolesPerLevel..orchestrator` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.agents/skills/l-01-agent-lifecycles/roles/reviewer.md b/.agents/skills/l-01-agent-lifecycles/roles/reviewer.md index a477ee87..fbeab40d 100644 --- a/.agents/skills/l-01-agent-lifecycles/roles/reviewer.md +++ b/.agents/skills/l-01-agent-lifecycles/roles/reviewer.md @@ -13,7 +13,7 @@ reviewer seat (below)** (seams: developer decision 2026-07-03; loop reuse: rulin 1. **Master-exit** — before a **manager** hands its completed master integration branch to the **orchestrator**. 2. **Super-exit** — before the **orchestrator** hands the accumulated super integration branch to the - **developer**. + **architect** for the developer review. Leaf-level review is the manager's own duty — **not** an adversarial seam. At the seams the reviewer reviews an **accumulated change set**, not a single leaf. @@ -30,11 +30,20 @@ loop's 3-round cap** — your delta-verify closes a round, it does not open one. > **Verdicts are evidence, not decisions.** The reviewer never decides a gate. Its verdict attaches to > the handover gate as **judge evidence**; the gate's decider decides — the **orchestrator** at -> master-exit (delegated `master-handover-approval`), the **developer** at super-exit — per the -> gate delegation policy (settings `orchestration.gateDelegation`, `controlplane/gate_policy.py`). +> master-exit (delegated `master-handover-approval`), the **architect carrying the developer +> ruling** at super-exit — per the gate delegation policy (settings `orchestration.gateDelegation`, +> `controlplane/gate_policy.py`). > The policy binds delegated seam decisions to verdict evidence when > `requireReviewerVerdictAtSeams` is set. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays reviewer for its lifetime. A pasted brief for another +role is refused and reported to the seam's decider via inbox instead of rerouting this chat. Roles +expand horizontally into new chats; sub-agents drill vertically inside this reviewer seat for the +three review lenses. A reviewer never absorbs architect, orchestrator, strategist, manager, or +worker work. + ## Lens - **Opening move:** scope the review — the integration branch diff, the relevant task docs @@ -101,10 +110,11 @@ orchestrator. Review the **accumulated master change set**, not a final leaf in master. Each fix leaf names scope, target files/docs, evidence, and done-when. A master-exit block without fix leaves is invalid. -### SUPER-EXIT — Orchestrator Before Developer Handover +### SUPER-EXIT — Orchestrator Before Architect/Developer Handover The orchestrator spawns this reviewer before handing the accumulated super integration branch to the -developer. Review **wholesale branch behavior**: the whole portfolio as integrated on super. +architect for the developer review. Review **wholesale branch behavior**: the whole portfolio as +integrated on super. - **Scope packet:** super integration branch diff against its base (main), portfolio task docs, master task docs, master-handover packets, prior master-exit verdicts, orchestrator decision logs, resolved diff --git a/.agents/skills/l-01-agent-lifecycles/roles/strategist.md b/.agents/skills/l-01-agent-lifecycles/roles/strategist.md index 377e4b70..e4ae81d6 100644 --- a/.agents/skills/l-01-agent-lifecycles/roles/strategist.md +++ b/.agents/skills/l-01-agent-lifecycles/roles/strategist.md @@ -3,8 +3,8 @@ > The sprint planner, **spawn-first** and self-contained: read the whole in-flight portfolio, prove > it coherent, resolve the dependency chains, establish blast radius, shuffle leaves for the best > implementation order, and deliver the **orchestration task** — the sprint plan and the sprint -> scope. A strategist run is a **mandatory precondition for any orchestrated run**. Your **brief is -> your session start**. +> scope. This seat runs only when the developer approves the architect's propose-first strategist +> question; when dispatched, your **brief is your session start**. > > Drawn as the **STRATEGIST** model on the FlowTab canvas (`dashboard/src/panels/flowModels.ts`). @@ -13,8 +13,8 @@ **Spawn-first by design** (developer decision 2026-07-05). Strategist work is token-heavy — it reasons over every master's state, task docs, notes, friction ledger, and gate history — so it runs as its own process with its own harness/model/effort knobs, protecting the orchestrator's context. -The designer precedent explicitly does NOT apply: the designer stays an inline hat because design -is drawing-board-interactive with the developer; the strategist's essence is solitary heavy +The designer precedent explicitly does NOT apply: the designer stays an inline architect hat +because design is drawing-board-interactive; the strategist's essence is solitary heavy analysis. Spawned by the orchestrator via `spawn_agent_session` with `env={"AR_SPAWN_ROLE": "strategist"}`. @@ -36,6 +36,14 @@ it into durable task form. The strategist never edits task docs, never raises ga git. A seat that never touches mutating AR tools never instantiates a lifecycle — that is the designed shape. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays strategist for its lifetime. A pasted brief for +another role is refused and escalated to the orchestrator via inbox instead of rerouting this chat. +Roles expand horizontally into new chats; sub-agents drill vertically inside this strategist seat +for portfolio analysis. A strategist never absorbs architect, orchestrator, manager, reviewer, or +worker work. + ## Lens - **Opening move:** read the brief fully — it carries **refs to durable portfolio state, never @@ -90,7 +98,7 @@ everything else. `roles/manager.md`): the strategist's analysis directly parameterizes the loops. 6. **Coherence & contradiction check** — cross-master sweep: two masters moving one surface in opposite directions, a leaf assuming state another leaf removes, duplicate work, vocabulary - drift. **Directional contradictions are quo-vadis → developer** (via the drawing board; see + drift. **Directional contradictions are quo-vadis → architect** (via the drawing board; see Duties §5). 7. **Ordering** — topological sort over ORDER edges; CONFLICT edges resolved by serialization or **leaf moves (recorded from→to with rationale)**; independent sets become **parallel waves** @@ -134,17 +142,17 @@ mutate nothing yourself. ### 5 — Drawing-board rounds The reviewer (plan-review catalog) passes judgment on the plan; the orchestrator relays the -verdict and the developer's drawing-board feedback back into this session. **Convergence over +verdict and the architect's drawing-board feedback back into this session. **Convergence over rounds is expected and normal** — large, messy portfolios are explicitly NOT expected to be fixed in one shot; the iteration is the feature. Each round must shrink the finding set (the convergence -rule); the loop's hard cap is 3 full rounds, and **the drawing board with the developer IS this +rule); the loop's hard cap is 3 full rounds, and **the drawing board through the architect IS this loop's escalation**. Quo-vadis items — high-blast-radius truths such as two masters heavily -disagreeing on direction — go **straight to the developer** at the drawing board (the orchestrator -carries them; you flag them, unmistakably, at the top of the coherence findings). +disagreeing on direction — go **straight to the architect relay** at the drawing board (the +orchestrator carries them; you flag them, unmistakably, at the top of the coherence findings). ### 6 — Adopted-plan handover -When the developer accepts the plan, the orchestrator adopts it; your seat's work is done. **The +When the architect returns the accepted plan ruling, the orchestrator adopts it; your seat's work is done. **The artifact write is unconditional; the inbox is the delivery channel when the brief wires it** — otherwise your final playback message to the orchestrator carries the artifact ref. Then end. The orchestration task remains the sprint's standing scope: a new master added **in-sprint before implementation starts** re-opens re-evaluation (you @@ -166,8 +174,8 @@ and enters the next sprint's evaluation. dashboard-visible. - **Stdin push** — the orchestrator delivers round feedback into this hosted session; your replies are inbox rows or artifact revisions — never an untracked side channel. -- **Escalation** — to the **orchestrator**, which relays; quo-vadis truths are flagged for the - developer's drawing board. You never edit task docs to reflect a ruling — the orchestrator does. +- **Escalation** — to the **orchestrator**, which relays to the architect; quo-vadis truths are + flagged for the drawing board. You never edit task docs to reflect a ruling — the orchestrator does. ## Tool Surface (positive statement — this is all of it) diff --git a/.agents/skills/l-01-agent-lifecycles/roles/system-specialist.md b/.agents/skills/l-01-agent-lifecycles/roles/system-specialist.md new file mode 100644 index 00000000..54422560 --- /dev/null +++ b/.agents/skills/l-01-agent-lifecycles/roles/system-specialist.md @@ -0,0 +1,102 @@ +# Lifecycle — System Specialist + +> One provider-degradation investigation, one report before any fix. The system specialist is a +> backend operations seat spawned by the orchestrator after a `degradation-alert`; it does not +> replace the orchestrator's portfolio attention. + +## What This Seat Is + +The system specialist investigates provider-only degradation events: provider metrics, provider +current-state files, provider logs, Docker/container state through the existing provider tools, and +the durable degradation event that caused the alert. This iteration is provider-only. Sentry or a +future system monitor may replace or feed the detector later, but the response protocol remains: +detect -> report -> explicit orchestrator order -> fix or stop providers. + +This seat is **investigate-first**. It writes a durable report under the active master's +`notes/reports/` folder (or the orchestrator-designated reports folder when there is no active +master) before attempting any fix. It fixes only after the orchestrator explicitly orders a +specific remediation based on that report. + +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays system-specialist for its lifetime. A pasted brief for +another role is refused and escalated to the orchestrator via inbox. This seat never absorbs +orchestrator, manager, worker, curator, reviewer, strategist, designer, or architect work. + +## Intake + +Read the orchestrator brief and the degradation event first. Required inputs: + +- Degradation event id and event payload or event-log path. +- Current provider metrics/state paths. +- Provider logs or diagnostics paths. +- Report path. +- Whether this is investigation-only or an explicit fix order. + +If the brief lacks the event or report path, ask the orchestrator for one clarification via inbox +and stop. + +## Investigation Report + +Write the report before any fix order is executed. Use this shape: + +```md +# System-Specialist Report — + +## Event +- State transition: +- Affected stacks: +- Critical failsafe already ran: yes | no | n/a + +## Findings +- + +## Root Cause Hypothesis +- + +## Fixable In Session +- Verdict: yes | no | uncertain +- Reason: + +## Recommended Action +- + +## Boundaries +- Provider-only scope honored: yes +- No AR task/memory state mutated beyond this report: yes +``` + +## Fix Mode + +Only after an explicit orchestrator order: + +- Apply the ordered provider remediation with existing provider/runtime tools. +- Do not edit AR task docs, lifecycle state, memory onboarding, ledgers, or code. +- Do not start providers if the order is only to investigate or if managers are paused by a + degradation-alert. +- If the issue is not fixable in-session, report that and recommend `provider_watchers stop`. + +The orchestrator owns the final decision: fixable-in-session -> order a targeted fix; not fixable +-> stop providers before they can take the system down. + +## Comms + +- **Inbox** — receive the orchestrator order, return report/fix completion or escalation. +- **Escalation** — system-specialist -> orchestrator. Never go straight to the architect or + developer. + +## Knobs + +| Knob | Default | Notes | +| ------- | ------- | ----- | +| harness | claude | operational investigation benefits from strong tool/session ergonomics | +| model | fable | system diagnosis and report synthesis | +| effort | high | degradation triage is high-impact | +| launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | +| sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | +| promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste | +| tools | provider diagnostics + native reads + inbox | provider_status · provider_diagnostics · provider_watchers when explicitly ordered · logs/metrics reads · inbox | + +Settings.json `orchestration.roles.system-specialist` overrides these, and +`orchestration.rolesPerLevel..system-specialist` overrides per dispatch level (role-file +defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.agents/skills/l-01-agent-lifecycles/roles/worker.md b/.agents/skills/l-01-agent-lifecycles/roles/worker.md index f5c369b4..aa29e836 100644 --- a/.agents/skills/l-01-agent-lifecycles/roles/worker.md +++ b/.agents/skills/l-01-agent-lifecycles/roles/worker.md @@ -7,7 +7,7 @@ ## What This Seat Is **One per task leaf, short-lived, fresh session.** Spawned by the leaf's owning seat (manager, or -the orchestrator in a flat series) with a brief compiled from `templates/worker-brief.md`. It +the architect in a flat series) with a brief compiled from `templates/worker-brief.md`. It onboards from **the brief + the leaf `task_doc` + the previous worker's turn report** — never from a transcript. Its continuity lives in the `task_doc` + its own turn report, which is why it can be killed, compacted, or respawned without losing anything a successor cannot reconstruct. @@ -16,20 +16,30 @@ The worker builds; it does not manage lifecycle machinery. **Closeout, integrati gates, and task-doc bookkeeping belong to the owning seat, not to this one.** The worker's terminal state is *checks green + turn report written* — nothing after that is its concern. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays worker for its lifetime. A pasted brief for another +role is refused and escalated to the owning seat via inbox instead of rerouting this chat. Roles +expand horizontally into new chats; sub-agents drill vertically inside this worker seat for +read/search only. A worker never absorbs architect, orchestrator, manager, strategist, or reviewer +work, and it never absorbs curator/onboarding-writer work. + ## The Worker Loop ``` -brief -> orient -> build (edit + onboarding same-pass) -> checks green -> turn report -> end +brief -> orient -> build code -> checks green -> turn report -> curator memory pass by separate seat | +-- blocked or plan delta beyond blank-filling -> escalate to the owning seat ``` ### 1 — Intake -Read the brief fully, then the leaf spec / `task_doc` it names. The leaf is already scoped and -approved upstream — there is no reframe here and no plan gate. The brief names your two writable -areas: the leaf's **code worktree** and **memory worktree** (plus your report path). You edit -nothing outside them. +On a developer-declared takeover, first run `../SKILL.md`'s Developer-Declared Task-Seat Takeover +checklist so the dashboard chat is attached to this leaf. Then read the brief fully, then the leaf +spec / `task_doc` it names. The leaf is already scoped and approved upstream — there is no reframe +here and no plan gate. The brief names your two writable areas: the leaf's **code worktree** and +your report path. The memory worktree is context for the curator pass unless the brief explicitly +says otherwise. You edit nothing outside your named surfaces. ### 2 — Orient (paired reads before edits) @@ -44,12 +54,10 @@ nothing outside them. - Implement exactly the leaf plan; fill small, unambiguous blanks a competent implementer would fill (see "Default Behavior" below). -- **Refresh the matching onboarding in the same editing pass** per - `c-05-create-or-update-onboarding-files`: a changed source file's sidecar **body** is updated now; - a new file's sidecar is created; route overviews that need a genuine body update get one, and a - no-impact route gets the literal history form `- — No route impact: `. - Regenerate generated route indexes with a **local `build_route_indexes(...)`** invocation from the - memory worktree. +- Produce the builder input the downstream curator needs: changed paths, code-diff summary, tests, + and any route/onboarding observations that would help the memory pass. The curator, not the + worker, writes onboarding in the official manager -> builder -> reviewer -> curator closeout + chain. - **Never `git commit`.** Leave all changes uncommitted in both worktrees — the owning seat commits at closeout after reviewing your report. @@ -63,14 +71,17 @@ the report. A red check you cannot fix inside the leaf's scope is an escalation, Write `templates/turn-report.md` to the path the brief names (convention: `notes/reports/-worker-report.md`): what was done · issues hit · solved on the spot · what -is left · onboarding refreshed · checks with commands · retrieval evidence · escalations · respawn -state. **A missing report gets nudged.** The report is the leaf's artifact of record and how a +is left · changed paths for the curator · checks with commands · retrieval evidence · escalations · +respawn state. The report is the leaf's builder artifact of record and how a respawned successor onboards — write it even when blocked (with the Escalations section filled), -then end your turn. +then end your turn. **A missing report gets nudged by the supervisor sweep (HFX2-L2), never by a +seat-local watcher** — no owning seat, and no worker, hand-rolls its own polling loop over this +artifact; ending your turn once the report is written is safe, not a risk you have to cover for. ## Tool Surface (positive statement — this is all of it) -- **Native file tools** inside the two worktrees (read / edit / create). +- **Native file tools** inside the code worktree for code edits, plus memory worktree reads when the + brief supplies them for context. - **Read-only AR retrieval:** `read_ar_files`, `grepai_search`, `cgc_*`, `context_packet`. - **Shell** for the prescribed checks (use the interpreter paths the brief names — do not assume a `python` shim exists). @@ -85,17 +96,17 @@ lifecycle machinery never instantiates a lifecycle; that is the designed shape, When the harness offers sub-agents, use them for **read/search only**, scoped to the leaf (locate call sites, sweep onboarding): each writes durable notes and returns a compact summary. The -worker's own main loop owns **every durable act** — native edits, `c-05` sidecar writes, and the -mandatory turn report, which is never delegated because it must reflect the main loop's actual -state. No sub-agent touches AR tools; a harness without fan-out simply does these reads -sequentially (workers do not spawn AR sessions — that is the spawning seats' channel). +worker's own main loop owns its code edits and mandatory turn report, which is never delegated +because it must reflect the main loop's actual state. The curator owns onboarding writes. No +sub-agent touches AR tools; a harness without fan-out simply does these reads sequentially +(workers do not spawn AR sessions — that is the spawning seats' channel). ## Loop Position (when the leaf runs as a three-party loop) The owning seat scores each leaf into a tier at dispatch (loop doctrine: `../SKILL.md`, The Three-Party Loop). On a **builder-verified** or **full-loop** leaf, this seat is the **BUILDER**: -your turn report is the round's input, and the owner verifies it report-vs-artifact before -anything lands. Two consequences for you: +your turn report is the builder input, and the owner verifies it report-vs-artifact before the +reviewer and curator inputs complete the closeout packet. Two consequences for you: - **Fix rounds resume THIS session** — the same builder, with its context intact. Your round-2+ report **appends** to your report file rather than rewriting it, so the loop history stays @@ -108,18 +119,25 @@ anything lands. Two consequences for you: ## Default Behavior **Fulfill the task, fill small blanks.** No creative-liberty prompting in either direction. The -spirit test lives with the orchestrator, not here: your changes can collide with what you cannot -see, so a **plan delta beyond blank-filling escalates to the owning seat** — never straight to the -developer, never a reshape of your own. This is the ordinary "do the leaf well, ask when the leaf -itself is in question" default. +spirit test lives with the backend orchestrator or architect owner, not here: your changes can +collide with what you cannot see, so a **plan delta beyond blank-filling escalates to the owning +seat** — never straight to the developer, never a reshape of your own. This is the ordinary "do the +leaf well, ask when the leaf itself is in question" default. ## Comms - **Inbox** — receive dispatch/context; post escalations; agent-to-agent rows carry role metadata and a `messageKind` (`turn-report`, `nudge`, `escalation`, …), durable + dashboard-visible. -- **Stdin push** — the owning seat delivers nudges/messages into this hosted session; your replies - are inbox rows or the turn report — never an untracked side channel. -- **Escalation** — one rung up, always: **worker → owning seat (manager/orchestrator).** +- **Stdin push** — the L2 supervisor sweep's injector (HFX2-L3) delivers nudges/messages into this + hosted session on its own mechanical tick, in the owning seat's name — never the owning seat (or + you) watching/polling by hand. Your replies are inbox rows or the turn report — never an untracked + side channel. +- **Idle is safe** — once your turn report is written, ending your turn is correct; silence is + supervised (HFX2-L2 sweep + HFX2-L4 escalation ladder), not a gap you must cover by lingering or + self-nudging. **Watcher ban (uniform-mechanism ruling 2026-07-07):** never hand-roll your own + watcher — one mechanism, no per-seat variance. +- **Escalation** — one rung up, always: **worker → owning seat (manager/orchestrator/architect in + solo flat mode).** ## Knobs diff --git a/.agents/skills/l-01-agent-lifecycles/templates/curator-brief.md b/.agents/skills/l-01-agent-lifecycles/templates/curator-brief.md new file mode 100644 index 00000000..8217ce34 --- /dev/null +++ b/.agents/skills/l-01-agent-lifecycles/templates/curator-brief.md @@ -0,0 +1,84 @@ +# Template — Curator Brief + +The dispatch packet the **manager** (or the architect in a flat series) compiles for a **curator**, +spawned fresh per leaf after builder code exists and the reviewer verdict is available. **The brief +is the curator's entire session start** — it replaces the front half the spawner already ran. This +is the change-set feeding contract: the curator never infers a change set from transcript memory, +it is FED the landed change set, the leaf task doc, and notes/ as inputs. + +Spawn with `env={"AR_SPAWN_ROLE": "curator"}` and the **qualified** leaf key +`//`; together they claim the curator's `(leaf, role)` seat while the +session-start router and dashboard leaf rail engage. + +--- + +```md +ROLE BRIEF — curator + +# CURATOR BRIEF — · + +You are the CURATOR for leaf `` of master `` (repo: ). Your lifecycle is +`skills/l-01-agent-lifecycles/roles/curator.md`; this brief is your session start. Write the leaf's +memory pass from the inputs below, then stop. + +## Worktrees +- Code: `` (branch ``, base ``) — read-only for you. +- Memory: `` (branch ``) — your only write surface. + +## The landed change set (fed, not inferred) +- Code diff: `..` in the code worktree — . +- Memory diff (if any pre-existing memory-worktree changes carry forward): `..`. +- Counters: `` from the change-set the manager attached — + do not re-derive this from your own guess at "what probably changed." + +## Task inputs +- Leaf task doc: `` (read it first — objective, requirements, decision log). +- notes/: `` — the builder turn report + (`notes/reports/-worker-report.md`), the reviewer verdict when this leaf ran a loop, and + any other task-local notes naming a factual current-state clarification. + +## Routing rule (mgmt-L4 design — apply this before writing anything) +Route each piece of the change set and each notes/ item to the RIGHT onboarding home: +1. A concrete source file's own sidecar, when the change is about that file's behavior. +2. The nearest governing route-local overview, when the change is about route/package shape or + crosses several files in one route. +3. The repo entity catalog, only for a real load-bearing cross-layer entity change. +4. The L3 Operational-Notes target is LAST RESORT ONLY — use it when a finding is real but has no + file, route, or entity home; never as the default drop point for convenience. +Overview-dumping (writing everything into the nearest overview because it is easiest) is rejected. + +## Tool surface +- Native reads in the code worktree; native reads/edits in the memory worktree. +- `c-05-create-or-update-onboarding-files` skill workflows for sidecars and entity catalogs. +- Local `route_index_refresh`-equivalent (`build_route_indexes(...)`) from the memory worktree. +- Inbox for one clarification row back to if the fed change set is missing or + ambiguous — never invent a change set from memory. +- No `worktree_*`, `lifecycle_*`, `task_doc`, `gate_*`, `memory_quality_check`-mutating tools beyond + what your role file names, no code edits. + +## Checks (before you report) +- `git diff --check` in the memory worktree. +- Any onboarding/reference checks the brief or your role file names. + +## Memory-pass report (mandatory, last act) +Write `/-curator-report.md`: changed onboarding files (with which +change-set item or notes/ item each one routes to and why), route index results, reference checks, +blockers, and the exact commands run. This report — together with the builder's code and the +reviewer's verdict — is exactly the manager's three closeout inputs. +``` + +--- + +**Compiler notes for the manager.** + +- Fill every ``; a brief with an unresolved placeholder is not dispatchable. +- Pull the change-set counters/paths from the leaf's actual landed range (the leaf contract's + recorded base commit through the builder's current HEAD/worktree state) — do not hand the curator + a stale or guessed diff. +- Attach the builder turn report and (when the leaf ran a loop) the reviewer verdict as the notes/ + inputs; the curator does not re-request evidence that already exists in `notes/reports/`. +- Deliver as an echo-confirmed paste; only count delivery on a post-boot echo. +- This brief runs strictly AFTER builder code exists and the reviewer verdict (when the leaf tier + requires one) is available — never before, and never in place of either. diff --git a/.agents/skills/l-01-agent-lifecycles/templates/manager-brief.md b/.agents/skills/l-01-agent-lifecycles/templates/manager-brief.md index 10c37922..0d611d1a 100644 --- a/.agents/skills/l-01-agent-lifecycles/templates/manager-brief.md +++ b/.agents/skills/l-01-agent-lifecycles/templates/manager-brief.md @@ -3,7 +3,8 @@ The dispatch packet the orchestrator compiles for a manager taking one master. Like the worker brief, **this brief is the manager's entire session start** — it replaces the front half the orchestrator already ran. Spawn with `env={"AR_SPAWN_ROLE": "manager"}` and the **qualified** leaf -key of the master's coordination leaf (`//`). +key of the master's coordination leaf (`//`); together they claim the +manager's `(leaf, role)` seat. --- @@ -30,8 +31,25 @@ master's leaf loop to the master-exit seam, then hand over. ## Dispatch defaults - Worker spawns: `templates/worker-brief.md`, `env={"AR_SPAWN_ROLE": "worker"}`, qualified leaf - keys; knob overrides: . + keys; the environment role and qualified leaf together claim each worker's `(leaf, role)` seat; + knob overrides: . +- Leaf closeout chain: manager -> builder -> reviewer -> curator. The manager closes a leaf from + builder code + reviewer verdict + curator memory pass — never before the curator pass exists. +- Curator spawns: `../templates/curator-brief.md`, `env={"AR_SPAWN_ROLE": "curator"}`, fresh per + leaf with the qualified leaf key, so the environment role and qualified leaf claim the + curator's `(leaf, role)` seat; dispatch only after builder code and the reviewer verdict are + available. The brief FEEDS the landed change set (leaf contract's base-to-head range) + the leaf + task doc + notes/ — the curator routes each to the right onboarding home (specific sidecar or + governing overview; L3 Operational-Notes last-resort only) and writes onboarding only. - Concurrency: . +- Provider degradation: on `messageKind="degradation-alert"`, do not start provider setup, + provider watchers, watcher restarts, or `retry_provider_setup` until an all-clear. Managers have + no provider kill authority; provider stops and fixes route through the orchestrator and + system-specialist. +- Cleanup: `worktree_integrate` auto-lands a completed leaf's worker/reviewer seats into the + dashboard landed/archive group (config-gated, default ON). Use the landed archive cleanup button + for archived rows; use `session_retire` only for a stuck/abandoned worker/reviewer/curator seat of + YOUR OWN master — server policy refuses any other target. ## The exit - When all leaves have landed on your branch: spawn the master-exit reviewer diff --git a/.agents/skills/l-01-agent-lifecycles/templates/orchestration-task.md b/.agents/skills/l-01-agent-lifecycles/templates/orchestration-task.md index c3d4bb08..c1559030 100644 --- a/.agents/skills/l-01-agent-lifecycles/templates/orchestration-task.md +++ b/.agents/skills/l-01-agent-lifecycles/templates/orchestration-task.md @@ -1,10 +1,12 @@ # Orchestration-Task Template The artifact a **strategist** drafts for the **orchestrator** (`roles/strategist.md`) — the sprint -plan and the sprint scope. A strategist run producing this artifact is a **mandatory precondition -for any orchestrated run**; the orchestrator adopts the accepted draft into durable task form (the -strategist is a reader, not a mutator). It is written under the series/coordination `notes/` path -the brief names and revised across drawing-board rounds. +plan and the sprint scope. The architect proposes the strategist pass, and the strategist drafts +this artifact only after developer approval; the orchestrator adopts the accepted draft into +durable task form (the strategist is a reader, not a mutator). When the developer sanctions a +strategist skip, `roles/orchestrator.md` owns the alternate author-and-adopt path. It is written +under the series/coordination `notes/` path the brief names and revised across drawing-board +rounds. ## Rules diff --git a/.agents/skills/l-01-agent-lifecycles/templates/turn-report.md b/.agents/skills/l-01-agent-lifecycles/templates/turn-report.md index b771ada4..597f40fd 100644 --- a/.agents/skills/l-01-agent-lifecycles/templates/turn-report.md +++ b/.agents/skills/l-01-agent-lifecycles/templates/turn-report.md @@ -2,7 +2,8 @@ The **mandatory** artifact a worker writes at **every** hand-off (`roles/worker.md`). It is how the leaf's work survives the session's death and how a respawned successor onboards from **state, not the -transcript**. A missing turn report is nudged by the manager. +transcript**. A missing turn report is nudged by the HFX2-L2 supervisor sweep, never by a +manager watching for it (uniform-mechanism ruling 2026-07-07). ## Rules diff --git a/.agents/skills/l-01-agent-lifecycles/templates/worker-brief.md b/.agents/skills/l-01-agent-lifecycles/templates/worker-brief.md index db8f44d0..c91e9779 100644 --- a/.agents/skills/l-01-agent-lifecycles/templates/worker-brief.md +++ b/.agents/skills/l-01-agent-lifecycles/templates/worker-brief.md @@ -7,7 +7,8 @@ absorbed a series of real dispatch frictions (route-index leaks, attestation for keying, missing `python` shim), so deviate knowingly or not at all. Spawn with `env={"AR_SPAWN_ROLE": "worker"}` and the **qualified** leaf key -`//` so the session-start router and the dashboard leaf rail both engage. +`//`; together they claim the worker's `(leaf, role)` seat while the +session-start router and dashboard leaf rail engage. --- @@ -18,13 +19,14 @@ ROLE BRIEF — worker You are a WORKER for leaf `` of master `` (repo: ). Your lifecycle is `skills/l-01-agent-lifecycles/roles/worker.md`; this brief is your session start. Execute the leaf -completely, write your turn report, then stop. +code completely, write your builder turn report, then stop. Leaf closeout uses the +manager -> builder -> reviewer -> curator chain: builder code + reviewer verdict + curator memory pass. -## Worktrees (your ONLY writable areas) +## Worktrees (your code write area + memory context) - Code: `` (branch ``, base ``) -- Memory: `` +- Memory: `` (read/context for changed-path notes; the curator writes onboarding) - Plus your turn report at the path below. Nothing else. NEVER `git commit` — the owning seat - closes out after reviewing your report. + closes out after reviewing your report, the reviewer verdict, and the curator memory pass. ## Tool surface - Native file tools inside the two worktrees; shell for the checks below. @@ -46,18 +48,18 @@ files involved, the invariants that must hold, what NOT to touch.> - Full: — must exit 0. - `git diff --check` in both worktrees. -## Onboarding (same editing pass, per c-05) -- Changed source files: update the sidecar BODY now; new files: create the sidecar. -- Route overviews: genuine body update where routes changed; otherwise the newest history entry - uses the LITERAL form `- — No route impact: ` (timestamp first). -- Pin idiom for verification metadata: "Verification metadata pinned until closeout stamps the - commit." +## Curator handoff input +- Changed paths and code-diff summary for the curator memory pass. +- Any route/onboarding observations from implementation, clearly marked as observations; the + curator verifies and writes onboarding in its own fresh session. +- Pin idiom for any metadata note the curator needs: "Verification metadata pinned until closeout + stamps the commit." ## Turn report (mandatory, last act) Write `/-worker-report.md` following `skills/l-01-agent-lifecycles/templates/turn-report.md` — including exact check commands + -outcomes, the retrieval-evidence tally, and the respawn state. If blocked: fill Escalations and -stop — escalate to , never to the developer. +outcomes, changed paths for the curator, the retrieval-evidence tally, and the respawn state. If +blocked: fill Escalations and stop — escalate to , never to the developer. ``` --- diff --git a/.agents/skills/w-02-light-task-workflow/master-template.md b/.agents/skills/w-02-light-task-workflow/master-template.md index 42738c16..a807afd1 100644 --- a/.agents/skills/w-02-light-task-workflow/master-template.md +++ b/.agents/skills/w-02-light-task-workflow/master-template.md @@ -7,7 +7,7 @@ built to grow as the work unfolds. ## When to escalate to a series -The `l-01-agent-lifecycles` orchestrator lifecycle's `decide` step escalates a single task to a series once its size is apparent — the +The `l-01-agent-lifecycles` architect lifecycle's `decide` step escalates a single task to a series once its size is apparent — the implementation plan no longer fits on a single page, or the work splits into distinct slices that each deserve their own checklist and commit. You can also start single and escalate later: drop in the master `task.md` and move the existing plan into the first `NN_.md`. diff --git a/.claude/hooks/agents-remember-session-start.md b/.claude/hooks/agents-remember-session-start.md index cd999a73..a6613b7a 100644 --- a/.claude/hooks/agents-remember-session-start.md +++ b/.claude/hooks/agents-remember-session-start.md @@ -4,9 +4,9 @@ If `AR_SPAWN_ROLE` is set, or your first user message is a role brief from an orchestrating agent: **ignore this notice entirely — your brief is your session start.** -Otherwise you are the developer-facing session, i.e. the **orchestrator**: read +Otherwise you are the developer-facing session, i.e. the **architect**: read `ar-coordination/AGENTS.md`, then run your lifecycle at -`skills/l-01-agent-lifecycles/roles/orchestrator.md` — trust checkpoint before +`skills/l-01-agent-lifecycles/roles/architect.md` — trust checkpoint before relying on memory, `read_ar_files` (paired source+onboarding) until the build decision, retrieval-strategy tally as evidence, notify-and-stop at every developer hand-off. diff --git a/.claude/skills/c-05-create-or-update-onboarding-files/SKILL.md b/.claude/skills/c-05-create-or-update-onboarding-files/SKILL.md index 31ea0090..d8e39a82 100644 --- a/.claude/skills/c-05-create-or-update-onboarding-files/SKILL.md +++ b/.claude/skills/c-05-create-or-update-onboarding-files/SKILL.md @@ -16,6 +16,16 @@ Planning stays in task artifacts. This package defines how onboarding itself is Before maintaining onboarding, use `c-08-ar-coordination-context-resolver` to resolve the target repository's active coordination context. It must use the `Domain Documentation` category declared in the resolved `system/sources.md` for the onboarding slice being maintained, rather than assuming that adjacent onboarding alone is sufficient or hard-coding one particular documentation system into the skill. +**Seat routing:** in the manager -> builder -> reviewer -> curator chain (`l-01-agent-lifecycles` +`roles/curator.md`), onboarding create/update duty during leaf work belongs to the curator seat, not +the builder — the builder produces code and a turn report only. The curator runs this skill's +workflows from a change set (landed diff), the leaf task doc, and notes/ fed to it by the manager, +and routes each item to the right onboarding home (a concrete sidecar or the governing overview +whose subject it is; the L3 Operational-Notes target is last-resort only, never a default). The +strict 1-to-1 source mapping, governing-overview links, and metadata rules below are unchanged by +this routing — only the writing seat moved. A solo flat session with no separate curator seat runs +this skill itself, exactly as before. + When onboarding maintenance is happening inside a `c-09-git-worktree-manager` worktree, check `worktree_status` before starting memory entries. If its `freshness` block reports that the recorded code or memory source branch moved, diff --git a/.claude/skills/c-09-git-worktree-manager/SKILL.md b/.claude/skills/c-09-git-worktree-manager/SKILL.md index 692dec19..58a87184 100644 --- a/.claude/skills/c-09-git-worktree-manager/SKILL.md +++ b/.claude/skills/c-09-git-worktree-manager/SKILL.md @@ -1,6 +1,6 @@ --- name: c-09-git-worktree-manager -description: "Create, attach to, report on, integrate, finalize, and clean up Agents Remember worktree-backed tasks while preserving human approval gates and external-memory compatibility." +description: "Create, attach to, report on, integrate, finalize, and clean up Agents Remember worktree-backed tasks while preserving delegated/human approval gates and external-memory compatibility." --- # c-09-git-worktree-manager Git Worktree Manager @@ -75,11 +75,14 @@ The intended order is: integration branch and integrates back into it. For a nested master, create the child integration branch from the parent integration branch. 6. choose or review the task slug and workflow variables -7. **hand off** the **Worktree Intent Gate** (notify-and-continue) for explicit developer approval +7. establish the applicable worktree-start authority: for standalone/new work, hand off the + **Worktree Intent Gate** for explicit developer approval; for subordinate leaves/edges inside an + accepted orchestrated series, record the accepted-series authority and continue without a new + developer hand-off 8. create the durable task wrapper when one is needed 9. request the `worktree_start` MCP tool only after the task identity is stable, the correct landable `source_branch` is selected, external memory is clean, and - the developer approved the intent packet + the applicable authority has been recorded The Worktree Intent Gate must name: @@ -97,7 +100,7 @@ visible: leaf work branches integrate into the pushable integration/source branc recorded by their enclosure; protected targets are reached later through the repo's PR flow. -Run the applicable dry-run/preflight first, then **hand off**: call +For developer-gated starts, run the applicable dry-run/preflight first, then **hand off**: call `lifecycle_turn_end_notification(summary={…the intent packet + the approve/revise ask…})` as the **last tool call**, then deliver the intent packet as your final prose and **STOP / end your turn**. The notification sets the `awaiting-developer` lifecycle state, surfaces a @@ -106,6 +109,10 @@ dashboard or in the leaf's attached chat; the **first AR tool call of your next lifecycle (`running`), clears the attention item, and proceeds to `worktree_start` — you send no explicit `lifecycle_resume`. +For subordinate orchestrated-series starts, do the same dry-run/preflight, record the accepted +planner/series authority in the task decision log or worktree intent note, and continue. Do not add +a developer stop for every leaf worktree. + Parked fallback: the block-and-wait `lifecycle_gate` junction (plus the operator inbox and dashboard GateResponder) still works if you deliberately raise it for a durable, developer-attributed, mutation-blocking record: @@ -190,19 +197,27 @@ quality gate, memory content commit, ledger update, and ledger commit. For worktree-backed tasks, pass the leaf enclosure `series-contract.md` to `worktree_closeout_preview` / `worktree_closeout_apply`. The apply step records -the developer's explicit commit approval in the contract and updates the -contract closeout state after the code, memory, and ledger commits are created. +the applicable closeout authority in the contract and updates the contract closeout state after the +code, memory, and ledger commits are created. Worktree closeout stops if the recorded code or external-memory source branch moved since task start. ## Integration -Integration is explicitly human-gated and runs only after closeout completed. It lands the closed task branches back onto the recorded source branches and records the landed commits separately from the closeout commits. Orchestrated-run carve-out (ruled 2026-07-06): dependency-ordered leaf→master and master→super integrations ride the series' standing approval (the developer's portfolio-gate approval recorded in the planner master) — the developer hand-off concentrates at the super PR/carry-over gate per the `l-01-agent-lifecycles` loop/orchestrator doctrine; a raised durable `integration-approval` gate still awaits the developer. +Integration runs only after closeout completed and is authority-gated by context. It lands the +closed task branches back onto the recorded source branches and records the landed commits +separately from the closeout commits. In an accepted orchestrated run, dependency-ordered +leaf→master and master→super integrations ride the series' standing approval (the developer's +portfolio-gate approval recorded in the planner master) — the developer hand-off concentrates at +the super PR/carry-over gate per the `l-01-agent-lifecycles` loop/orchestrator doctrine. A raised +durable `integration-approval` gate still awaits the developer. On an orchestrated master's exit (master → super integration) the integrate step additionally enforces the delegated `master-handover-approval` seam: an undecided or policy-invalid handover gate addressed to the master (by `enclosure` = master task name) returns `handover-gate-blocked` instead of landing — decide the gate per the `l-01-agent-lifecycles` seam doctrine, then rerun. When no gate addresses the integrating master but open `master-handover-approval` gates exist elsewhere, integrate still proceeds and its result carries a `handover_gate_warning` naming them — treat it as a spelling check on the raised gate's `enclosure`. -Run `worktree_integrate(..., dry_run=true)` first, then **hand off**: call +Run `worktree_integrate(..., dry_run=true)` first. For subordinate accepted-series integrations, +record the standing series authority and then run the real integration without a developer stop. +For developer-gated integrations, **hand off**: call `lifecycle_turn_end_notification(summary={…the integration plan…})` as the **last tool call**, then deliver the integration preview as your final prose and **STOP**. The developer approves on the dashboard or in chat; the first AR tool call of your next turn auto-resumes @@ -228,9 +243,17 @@ After successful integration, complete any repo-specific landing tail first: pus ## Lifecycle Finalization And Cleanup -Lifecycle finalization is explicitly human-gated and runs only after closeout, integration, and any PR/carryover tail are complete. It proves the current parent-child branch edge, then removes the recorded code and memory worktrees, deletes local task branches only when Git can prove they are merged, removes empty worktree group folders when safe, records `cleanup: completed` in the contract, and updates task documents. - -Run `lifecycle_finalize_task(..., dry_run=true)` first, then **hand off**: call +Lifecycle finalization runs only after closeout, integration, and any PR/carryover tail are +complete, and its approval authority follows the same series boundary. For subordinate +accepted-series leaf/master edges, the owning manager/orchestrator may finalize and clean up after +the dry-run proves the landed edge. For final super→main cleanup, standalone work, or a deliberately +raised `cleanup-approval` gate, stop for developer approval. Finalization proves the current +parent-child branch edge, then removes the recorded code and memory worktrees, deletes local task +branches only when Git can prove they are merged, removes empty worktree group folders when safe, +records `cleanup: completed` in the contract, and updates task documents. + +Run `lifecycle_finalize_task(..., dry_run=true)` first. For subordinate accepted-series cleanup, +record the standing authority and run the real finalizer. For developer-gated cleanup, **hand off**: call `lifecycle_turn_end_notification(summary={…what cleanup removes…})` as the **last tool call**, then relay the landed-commit proof, cleanup plan, and task-document updates as your final prose and **STOP**. The developer approves on the dashboard or in chat; the first AR tool call of your next turn auto-resumes and runs @@ -277,11 +300,16 @@ then proceeds as usual, including closeout → integrate → finalize. 2. The `c-09-git-worktree-manager` skill does not initialize memory roots; use the `c-00-initialize-memory-repo` skill before starting external-memory worktrees. 3. Closeout belongs to the `c-12-closeout` skill; the `c-09-git-worktree-manager` skill only supplies worktree contract context. 4. The `c-09-git-worktree-manager` skill must not use divergent memory as semi-trusted reference context. -5. The `c-09-git-worktree-manager` skill must not bypass the `c-12-closeout` skill's explicit closeout approval gate. +5. The `c-09-git-worktree-manager` skill must not bypass the `c-12-closeout` skill's applicable + closeout authority gate. 6. The `c-09-git-worktree-manager` skill must not create closeout commits outside the `c-12-closeout` skill's code-memory-ledger sequence. 7. The `c-09-git-worktree-manager` skill must not call `worktree_start` until - the developer has approved the Worktree Intent Gate. -8. The `c-09-git-worktree-manager` skill must not move source branches during integration until replay/preflight has produced fast-forwardable code and memory commits and explicit integration approval exists. -9. The `c-09-git-worktree-manager` skill must not finalize or clean up without explicit cleanup/finalization approval. + the applicable authority has been recorded: developer-approved Worktree Intent Gate for + standalone/new work, or accepted-series authority for subordinate orchestrated work. +8. The `c-09-git-worktree-manager` skill must not move source branches during integration until + replay/preflight has produced fast-forwardable code and memory commits and applicable + integration authority exists. +9. The `c-09-git-worktree-manager` skill must not finalize or clean up without applicable + cleanup/finalization authority. 10. The `c-09-git-worktree-manager` skill must not treat squash-merged content as a normal landed edge. 11. The `c-08-ar-coordination-context-resolver` skill remains the facts-only resolver; the `c-09-git-worktree-manager` skill owns worktree and lifecycle mutation. diff --git a/.claude/skills/c-12-closeout/SKILL.md b/.claude/skills/c-12-closeout/SKILL.md index 2c7fe3ff..82c472a5 100644 --- a/.claude/skills/c-12-closeout/SKILL.md +++ b/.claude/skills/c-12-closeout/SKILL.md @@ -1,6 +1,6 @@ --- name: c-12-closeout -description: "Close out approved Agents Remember edits by preserving explicit commit approval, missing-onboarding checks, external-memory onboarding refresh, memory quality, ledger alignment, and no automatic push for worktree-backed tasks." +description: "Close out approved Agents Remember edits by preserving the applicable approval authority, missing-onboarding checks, external-memory onboarding refresh, memory quality, ledger alignment, and no automatic push for worktree-backed tasks." --- # c-12-closeout Closeout @@ -15,6 +15,18 @@ direct-checkout closeout path. Use the `c-09-git-worktree-manager` skill for worktree start, attach, status, integration, lifecycle finalization, and cleanup; use this skill for the closeout gate and code-memory-ledger commit order. +**Seat note (manager -> builder -> reviewer -> curator chain):** in that chain, the builder produces +code and a turn report only — it does not author onboarding. The dedicated curator seat +(`l-01-agent-lifecycles` `roles/curator.md`) runs the `c-05-create-or-update-onboarding-files` skill +as its own fresh pass, fed the leaf's landed change set, task doc, and notes/, BEFORE the owning +seat (the manager) runs this skill's closeout preview. Everywhere below that says "create" or +"refresh" onboarding, that authoring already happened in the curator's pass; the seat running +closeout **verifies** the curator's output against the checks in this skill, it does not author +onboarding inline to make a failing check pass. A check that still fails after the curator pass is a +closeout failure — respawn/rerun the curator, do not patch onboarding from the closeout seat. This +distinction does not apply outside that chain (e.g. a solo flat session with no separate curator +seat still runs `c-05-create-or-update-onboarding-files` itself before closing out). + ## MCP Tools Use the worktree closeout tools against the task contract: @@ -29,18 +41,31 @@ Worktree closeout records closeout state in the contract the `c-09-git-worktree-manager` skill owns later integration, lifecycle finalization, cleanup, and task-document completion. -## Approval Hand-off +## Approval Authority + +Closeout is always authority-gated, but the authority is contextual. + +For standalone work, final super-branch landing, or any closeout where the accepted task/series +authority is unclear, agents must request the matching preview tool first, relay the proposed code, +memory, and ledger commit messages to the developer, and ask for explicit commit approval. + +For subordinate work inside an accepted orchestrated series, the owning seat may apply closeout +under delegated series authority after the preview/checks are clean. Managers govern leaf commits; +the orchestrator governs manager/master edges and direct flat work when it is wearing the manager +or worker hat itself. Do not stop for the developer merely because closeout will create code, +memory, and ledger commits. The `intent_note` records the authority source, e.g. the accepted +planner/series task and the owning seat's review of the preview. -Closeout is explicitly human-gated. Agents must request the matching preview -tool first, relay the proposed code, memory, and ledger commit messages to the -developer, and ask for explicit commit approval. +Closeout still stops for the developer when the work reaches the final completed super branch / +PR-carryover gate, when a `closeout-approval` gate has been deliberately raised, when the change is +outside the accepted scope, when checks remain red outside the task, when onboarding/memory quality +cannot be repaired inside the leaf, or when a quo-vadis decision is required. -Real closeout uses the matching apply tool with an `intent_note`. The note -records the developer's explicit commit approval. Agents must not treat -implementation approval, a previous "looks good", or their own judgment as -commit approval. +Real closeout uses the matching apply tool with an `intent_note`. The note records the applicable +authority: either explicit developer commit approval or delegated accepted-series authority. Agents +must not treat a vague "looks good" or their own preference as authority. -The relay follows the `l-01-agent-lifecycles` orchestrator hand-off protocol: run the +For a developer-gated closeout, the relay follows the `l-01-agent-lifecycles` orchestrator hand-off protocol: run the preview/dry-run first, then call `lifecycle_turn_end_notification(summary={…the preview facts + the commit ask…})` as the **last tool call**, then deliver the preview facts and proposed messages as plain @@ -66,10 +91,11 @@ the dashboard and a `closeout-approval` gate is explicitly raised, closeout is * server-side through that durable gate, so a developer can approve from the cockpit and the mutating tool — not a UI button — is the security boundary. -`closeout-approval` **is** the commit gate — closeout is the single -commit-of-record for code, memory, and ledger, so there is no separate -`commit-approval` kind; every commit routes through this gate. The dashboard -junction uses the preview/dry-run -> chat report -> `lifecycle_gate` order above. +`closeout-approval` **is** the human commit gate when it is deliberately raised — closeout is the +single commit-of-record for code, memory, and ledger, so there is no separate `commit-approval` +kind. Subordinate orchestrated-series closeouts normally do not raise this gate; they use the +accepted-series authority recorded in the `intent_note`. The dashboard junction uses the +preview/dry-run -> chat report -> `lifecycle_gate` order above. How it binds: @@ -99,16 +125,19 @@ How it binds: Rules: -1. **Never self-approve.** A model-attributed approval is rejected by - enforcement. Wait for the developer's dashboard decision or Chat response, and - never pass your own judgment off as commit approval. +1. **Never self-approve a human-pinned gate.** A model-attributed approval is rejected by + enforcement. Wait for the developer's dashboard decision or chat response when a + `closeout-approval` gate exists, and never pass your own judgment off as developer approval. + Delegated-series closeout without a raised gate is different: it records the accepted series + authority and the owning seat's review in `intent_note`. 2. **Opening a gate is opt-in and deliberate.** Open a `closeout-approval` gate **only** when a developer is driving approval from the dashboard. Do **not** open one in a pure-chat session with no cockpit watching — an `open` gate blocks your own closeout until it is decided. -3. **Gateless lifecycles are unchanged.** With no `closeout-approval` gate the chat - commit gate (`intent_note` after an explicit "commit") governs exactly as before; - enforcement is additive, never a new requirement on every closeout. +3. **Gateless lifecycles use the applicable authority.** With no `closeout-approval` gate, + standalone/final work still needs explicit developer commit approval, while accepted + orchestrated-series subordinate work may proceed under delegated series authority. Enforcement is + additive, never a requirement to raise a gate on every closeout. 4. The closeout preview/apply payload carries a `closeout_gate` block (`enforced` / `permitted` / `gateId` / `reason`); relay it at the commit-approval gate so the developer sees whether a dashboard gate is open, approved, or absent. @@ -130,17 +159,22 @@ python -m agents_remember.memory_quality.integrity.check_missing_onboarding --co ``` The check only evaluates files that are new in the current checkout or -worktree, not the whole historical repository. If it reports missing -onboarding, create those sidecars through the `c-05-create-or-update-onboarding-files` skill before committing code. After -the code commit exists, refresh the new sidecars' verification metadata to that -commit during the normal post-code-commit memory refresh. +worktree, not the whole historical repository. In the manager -> builder -> +reviewer -> curator chain, this check is expected to already pass by the time the owning seat runs +it, because the curator's memory pass created those sidecars through the +`c-05-create-or-update-onboarding-files` skill before this precondition is checked; running the +check here confirms that pass, it is not the trigger to author onboarding from the closing seat. If +it still reports missing onboarding, do not create the sidecars inline — escalate to run (or rerun) +the curator's memory pass, then rerun this check. After the code commit exists, refresh the new +sidecars' verification metadata to that commit during the normal post-code-commit memory refresh. Changed (already-onboarded) source files have a parallel requirement: their sidecar content must be updated to approved current state before closeout. The closeout gate rejects any changed source file whose existing sidecar body was not modified in the current task, because advancing verification metadata over -stale content defeats the commit-hash-based drift check. Update changed sidecars -during implementation, not at the metadata-refresh step. +stale content defeats the commit-hash-based drift check. In the curator chain, changed sidecars are +updated during the curator's memory pass, not at the metadata-refresh step, and not by the builder +during implementation. The closeout worklist covers the working tree plus the leaf contract-recorded committed range: every path changed between the last verified commit (the @@ -159,11 +193,15 @@ deliberately through the `c-05-create-or-update-onboarding-files` skill. External-memory closeout order is: -1. run `check_missing_onboarding` against current additions -2. create missing onboarding for newly added eligible source files before committing code +1. run `check_missing_onboarding` against current additions (in the curator chain, this confirms the + curator's pass already covered them — it is not the cue to author onboarding here) +2. if onboarding is still missing, escalate to run/rerun the curator's memory pass through the + `c-05-create-or-update-onboarding-files` skill before committing code (solo flat sessions with no + separate curator seat create it directly) 3. commit code changes and capture `C2` plus its commit date 4. run the `c-02-memory-quality-control` skill's drift check against `C2` to produce the full memory update worklist -5. verify each changed source file's sidecar content was updated in this task, then refresh affected onboarding `lastVerifiedCommitHash` and `lastVerifiedCommitDate` to `C2`; a changed source file with an unmodified sidecar body fails the closeout instead of receiving a metadata-only refresh +5. verify each changed source file's sidecar content was updated in this task (by the curator's pass + in the chain above), then refresh affected onboarding `lastVerifiedCommitHash` and `lastVerifiedCommitDate` to `C2`; a changed source file with an unmodified sidecar body fails the closeout instead of receiving a metadata-only refresh 6. refresh affected repo entity catalog `git-blob-set-v1` fingerprints against `C2` when changed source paths are listed as entity evidence 7. refresh affected route overview `lastVerifiedCommitHash` / `lastVerifiedCommitDate` metadata to `C2` 8. refresh generated route indexes so `overview.index.json` matches the updated onboarding tree @@ -214,16 +252,20 @@ preview and apply payloads for the commit-approval relay. Worktree closeout also fails when the recorded code or external-memory source branch moved since task start. -Missing onboarding is the expected hard failure when the implementation/update -pass did not produce a required onboarding file. The next step is to run the `c-05-create-or-update-onboarding-files` skill -for that source file, then rerun the closeout preview. +Missing onboarding is the expected hard failure when the required onboarding file was not produced — +in the manager -> builder -> reviewer -> curator chain that means the curator's memory pass did not +cover it. The next step is to run (or rerun) the curator's `c-05-create-or-update-onboarding-files` +pass for that source file, then rerun the closeout preview; a solo flat session with no separate +curator seat runs that skill itself. ## Boundaries 1. The `c-12-closeout` skill owns closeout approval and code-memory-ledger commit sequencing. 2. The `c-12-closeout` skill does not create worktrees, integrate worktrees, finalize lifecycles, or clean up worktrees. 3. The `c-12-closeout` skill does not initialize memory roots; use the `c-00-initialize-memory-repo` skill. -4. The `c-12-closeout` skill must not commit without explicit commit approval after a closeout preview. +4. The `c-12-closeout` skill must not commit without the applicable authority after a closeout + preview: explicit developer commit approval for standalone/final work, or recorded delegated + series authority for subordinate accepted-series work. 5. The `c-12-closeout` skill must not create a memory content commit whose affected onboarding metadata still points at pre-closeout code. 6. The `c-12-closeout` skill must not create a memory content commit before route overview metadata, generated route indexes, and `memory_quality_check` are clean for the new code commit. 7. The `c-12-closeout` skill must not push automatically. diff --git a/.claude/skills/c-13-install-and-onboard/SKILL.md b/.claude/skills/c-13-install-and-onboard/SKILL.md index d583fd13..e52d9f57 100644 --- a/.claude/skills/c-13-install-and-onboard/SKILL.md +++ b/.claude/skills/c-13-install-and-onboard/SKILL.md @@ -152,8 +152,8 @@ preference): 4. **Harness preference + role knobs** (`orchestration.spawn.harness`, per-role `orchestration.roles.`, per-level `orchestration.rolesPerLevel..`) - which installed harness - `spawn_agent_session` uses when the spawning seat passes none, per-role - harness/model/effort overrides, and per-LEVEL overrides + `spawn_agent_session` uses when no role/level knob supplies one, per-role + harness/model/effort settings, and per-LEVEL settings (leaf|master|portfolio) for tiered economics (e.g. a cheap leaf reviewer, a smarter master-seam reviewer). Harness values must be known ids: the builtin registry (`claude`, `codex`, `pi`) or an `orchestration.harnesses` @@ -163,8 +163,10 @@ preference): `ultracode`); mention the FREE-FORM escape hatch for anything outside the vocabularies - `launchArgs` (verbatim argv), `sessionCommands` (pasted before the brief), `promptKeywords` (prepended to the brief) - never - validated, recorded in spawn provenance. Default: detection-gated (the - first detected harness). The full spawn-surface manual is + validated, recorded in spawn provenance. Ordinary spawning seats cannot + pass `harness`/`model`/`effort`, launch/session spend controls, or harness-native + spend/endpoint env keys directly; settings are the spend surface. Default: + detection-gated (the first detected harness). The full spawn-surface manual is `docs/reference/harnesses.md`. If the developer wants to skip the interview, confirm the seeded defaults diff --git a/.claude/skills/l-01-agent-lifecycles/SKILL.md b/.claude/skills/l-01-agent-lifecycles/SKILL.md index 1d390618..4094072d 100644 --- a/.claude/skills/l-01-agent-lifecycles/SKILL.md +++ b/.claude/skills/l-01-agent-lifecycles/SKILL.md @@ -1,6 +1,6 @@ --- name: l-01-agent-lifecycles -description: "The agent lifecycles: one lifecycle per agent type, under one roof. Routes every session by exactly three conditions (spawn-role env -> role brief -> otherwise orchestrator), carries the minimal lifecycle frame (the six lifecycle signals every session shares), and houses the self-contained per-role lifecycles (orchestrator, designer, strategist, manager, worker, adversarial reviewer) plus the report-template library and the reviewer criteria catalogs. A developer-facing session IS the orchestrator; solo work is the degenerate portfolio. Supersedes and replaces both l-01-session-job-lifecycle and l-02-agent-orchestration." +description: "The agent lifecycles: one lifecycle per agent type, under one roof. Routes every session by exactly three conditions (spawn-role env -> fresh role brief -> otherwise free-chat launcher), carries the minimal lifecycle frame (the six lifecycle signals every session shares), and houses the self-contained per-role lifecycles (architect, orchestrator, designer, strategist, manager, worker, curator, system-specialist, adversarial reviewer) plus the report-template library and the reviewer criteria catalogs. Solo work is the degenerate portfolio. Supersedes and replaces both l-01-session-job-lifecycle and l-02-agent-orchestration." --- # l-01-agent-lifecycles — The Agent Lifecycles @@ -15,42 +15,128 @@ lifecycle, and no role reads another role's file. 1. **`AR_SPAWN_ROLE` is set** (spawn env, injected by `spawn_agent_session`) → run `roles/.md`. Nothing else in this file's "developer session" material applies to you. (`designer` here means the same design hat in a separate chair — see `roles/designer.md`.) -2. **Else: the first user message is a role brief** — a `templates/*-brief.md`-shaped dispatch or +2. **Else: the first user message is a role brief in a fresh session** — a `templates/*-brief.md`-shaped dispatch or a first line of the form `ROLE BRIEF — ` from an orchestrating agent → run that role's lifecycle. The brief is your session start; a workspace session-start notice is not addressed to you. -3. **Else** (a developer opened this session) → you are the **orchestrator**: run - `roles/orchestrator.md`. Solo work is the degenerate portfolio — the same three jobs with hats - collapsed (the orchestrator wears the manager hat in flat runs and builds hands-on at session - scale); the task doc still comes first. +3. **Else** (a developer opened this session) → you are the developer-facing **free chat** — a + launcher, not a role seat (ruled 2026-07-09). Research-only questions are answered inline with + no role taken. The moment the ask is role-shaped (a sprint, a task, any durable change), do NOT + assume the architect role in this chat: **spawn the architect into its own chat** + (`spawn_agent_session` with `AR_SPAWN_ROLE=architect`; the profile — harness/model/effort — + comes from `orchestration.roles.architect` in settings, never from this session's guess) and + point the developer at that chat. A clean startup with the settings-owned profile removes all + profile ambiguity. The spawned architect runs `roles/architect.md` and owns the developer + conversation from there. There is no fourth entry, and the edge cases are decided: an **unresolvable `AR_SPAWN_ROLE` value** (no matching `roles/.md`) falls through to condition 2 (the brief); a role-env session **whose brief never arrives** announces itself on the inbox and waits — it never -improvises a task; `AR_SPAWN_ROLE=orchestrator` is valid only as a takeover chair (the Profile -check (takeover) in `roles/orchestrator.md`, The Event Loop) — the developer still talks to **one** orchestrator. Orchestrated -fan-out (spawning managers/workers at scale) begins only on an explicit developer request (e.g. -*"orchestrate these masters"*) — no agent promotes itself into a spawning seat. +improvises a task; `AR_SPAWN_ROLE=orchestrator` is valid only as a spawned backend seat or a +backend takeover chair — the developer still talks to the **architect**, not the orchestrator. +The spool-up chain is fixed and self-driving (ruled 2026-07-09): free chat spawns the +**architect**; the architect spawns the **orchestrator** for portfolio execution; the +orchestrator spawns **managers** per the approved plan and the concurrency settings; managers +spawn their **workers**. No seat waits to be told "spawn this, spawn that" — each level spawns +its next level from the plan. Only two spool-up decisions ever go back to the developer, both as +questions the agent raises itself: whether to run a **strategist** pass (proposed, never +auto-run), and whether to take the **short root** (solo, no orchestration) when the work looks +tiny — see `roles/architect.md`. One exception to the no-cross-reading rule above: **a seat that WEARS a hat runs that hat's file -as its own** — the orchestrator always for `roles/designer.md`, and in flat runs for -`roles/manager.md` (the hat-collapse rule). +as its own** — the architect may wear `roles/designer.md`, and in solo/flat runs may wear backend +or build hats (the hat-collapse rule). A spawned role seat never wears another role's hat. + +## Developer-Declared Task-Seat Takeover + +When the developer says *"you are the orchestrator/manager/worker for task X"* (or equivalent), +that is a **task-seat takeover**, not a loose role hint. The named task leaf is the seat. Before +analysis, profile checks, spawning, or implementation, open the named task doc and attach the +current dashboard chat to that leaf. + +Operational checklist: + +1. Resolve the named task to the **qualified** leaf key `//` and the + lifecycle role this seat is claiming. +2. Use the dashboard terminal catalog session id for this chat — not `CLAUDE_CODE_SESSION_ID`, not + `CODEX_THREAD_ID`. +3. Call `attach_terminal_session_to_leaf` with that qualified leaf key and the claimed role. +4. Rename the session to the seat label the developer expects. +5. Verify the terminal catalog and dashboard row show that exact `(qualified leaf key, seat role)` + binding before continuing. + +If no dashboard terminal catalog session id can be found, record the blocker and ask for the +missing attachment path. Do not claim the seat is attached until the catalog/dashboard row proves +it. After the attachment is verified, continue with the lifecycle selected above. + +## Developer Clarification Triage + +When the developer clarifies a rule, boundary, or desired behavior during an active task, decide +whether it is **current implementation** or **future queue** before writing only a note. Read the +active queue first: the current leaf, parent/master, neighboring leaves, decision log, open +questions, and in-flight branch state. The question is not whether a note is useful; it is whether +the developer is effectively steering the work already in hand. + +Treat it as current implementation when queue context and closeness point at the active change: it +names the same task/leaf/master, resolves a defect exposed by the current work, or improves the +same doctrine or code path. A small change that plainly fits the current diff is a strong signal +for immediate implementation even if the developer phrases it as "maybe" or "we can wrap this in." +In that case, extend the current task surface/decision log enough to make the added scope visible +and implement it now. + +Treat it as future queue when it names a later release, a separate subsystem, a large scope jump, +work whose correctness depends on another unfinished master, or a change that would reorder +already-running leaves. Record the item in the right durable queue or ask the owning seat to plan it +later. If the intent is genuinely ambiguous after reading the queue, ask the developer directly +whether they want immediate implementation or a queued note. Do not silently downgrade a +close/current/small change into future speak, and do not silently expand the active leaf when the +fit is unclear. ## The Role Registry | Role | Seat | Lifecycle file | | --- | --- | --- | -| **orchestrator** | the developer-facing session; first coordination leaf of an orchestrated series | `roles/orchestrator.md` | -| **designer** | a HAT the orchestrator pulls inline (front of the pipeline or mid-flight; separate chair optional) | `roles/designer.md` | -| **strategist** | the sprint planner, SPAWN-FIRST; a strategist run is a **mandatory precondition for any orchestrated run** — its deliverable is the orchestration task (sprint plan + scope); spawn value `strategist` | `roles/strategist.md` | +| **architect** | the developer-facing owner seat; design conversation, decision-item relay, and drawing board | `roles/architect.md` | +| **orchestrator** | spawned backend portfolio/orchestration seat; never developer-facing | `roles/orchestrator.md` | +| **designer** | a HAT the architect pulls inline (front of the pipeline or mid-flight; separate chair optional) | `roles/designer.md` | +| **strategist** | the sprint planner, SPAWN-FIRST when the developer approves the architect's propose-first question; its deliverable is the orchestration task draft (sprint plan + scope); spawn value `strategist` | `roles/strategist.md` | | **manager** | one coordination leaf per master; drives that master's leaf loop | `roles/manager.md` | | **worker** | one leaf worktree, short-lived, fresh session | `roles/worker.md` | +| **curator** | fresh per leaf after builder/reviewer; writes onboarding only from task docs, notes, and code diff | `roles/curator.md` | +| **system-specialist** | backend provider-degradation investigator; report first, fixes only after explicit orchestrator order; spawn value `system-specialist` | `roles/system-specialist.md` | | **adversarial reviewer** | short-lived, spawned at the two seams (master-exit, super-exit) and as any three-party loop's reviewer seat (criteria catalogs bound per review type); spawn value `reviewer` | `roles/reviewer.md` | The **lenses** (bug · feature · triage · research — `lenses.md`) are how the scoping seats -(orchestrator, designer) read a piece of work; a dispatched role never picks a lens — its brief +(architect, designer, backend orchestrator) read a piece of work; a dispatched role never picks a lens — its brief already carries the flavor. +## Role-Seat Immutability (dashboard-owned sessions) + +When the dashboard owns a session, its role is fixed for the session lifetime. Roles expand +**horizontally** by spawning new, individually addressable chats; sub-agents drill **vertically** +inside one seat's context for deeper analysis. A dashboard-owned session that already has a role +refuses a pasted role brief instead of silently rerouting itself; it escalates the mismatch to its +owner via the inbox. Router condition 2 applies only to fresh sessions. Sessions not owned by the +dashboard follow the host harness's ordinary rules. + +Hat-collapse is sanctioned only for the owner/developer-facing architect seat in solo or flat +runs. Spawned role seats never absorb another role brief and never become a different role in +place. + +## Minimal Decision-Item Relay + +The ARCHITECT/ORCHESTRATOR split uses the existing operator inbox now. No full queue schema or +dashboard reform is introduced here. + +- Backend seats post one `messageKind: decision-item` inbox row at a time to the architect. The row + states what is being decided, the options, the consequences, and the durable evidence refs. +- The architect presents one item at the developer's pace, records the ruling in the durable task + surface (`openQuestions` / decision logs, with notes for analysis), and returns one + `messageKind: decision-ruling` inbox row to the backend seat. +- If the item is underspecified, the architect sends a single clarification row back instead of + guessing. The backend does not open a second item until the active item has a durable ruling or + clarification state. + ## The Minimal Frame (the only machinery every session shares) Every session in a managed repo may be a **lifecycle**: six signals — `lifecycle_start` · @@ -72,19 +158,34 @@ handles a lifecycle id** — identity is server-side, anchored in the worktree c Rules: a tool call outside any lifecycle is **dropped, never misattributed**; `paused` is system-owned. **A spawned role that never touches mutating AR tools simply never instantiates a lifecycle — that is correct, not a violation.** A spawned role runs its **own** lifecycle when it -runs one; it never adopts its spawner's. The session↔leaf association is the catalog binding made -at spawn (the **qualified** leaf key `//`), not lifecycle adoption. +runs one; it never adopts its spawner's. The session↔task-seat association is the catalog binding +made at spawn — the pair of **qualified** leaf key `//` and seat role — +not lifecycle adoption. Different roles may coexist on one leaf; only a second live owner of the +same `(qualified leaf key, seat role)` pair collides. + +**Notify-and-stop is safe by design (HFX2-L1..L4, landed):** ending a turn on +`lifecycle_turn_end_notification` — or simply stopping once your artifact is written and nothing is +pending — is never a liveness gap. Silence is supervised: the HFX2-L2 supervisor sweep evaluates +every expected artifact/signal on its own mechanical tick and the HFX2-L4 escalation ladder +(renudge → skip-level → architect custody/architect attention, then respawn) handles inactivity. **No role watches, +polls, or nudges on its own initiative — that is a banned seat-local watcher (uniform-mechanism +ruling 2026-07-07).** Every role's own liveness duty inverts to *passive*: you will be woken with +your pending signals; process and ack every item before ending your turn again. ## Shared Invariants (every role can count on these) - **Continuity lives in the `task_doc` + durable artifacts, never in transcripts** — which is why short-lived workers and reviewers are safe, and why every seat writes its artifact of record. -- **Escalation ladder: worker → manager → orchestrator → developer.** No rung is skipped, ever. +- **Escalation ladder:** worker → manager → orchestrator → architect → developer; system-specialist + → orchestrator. No rung is skipped, ever. Each role file states only its own rung. - **Observability:** coordination seats are `task_doc` leaves with attached chats; the developer can walk into any seat at any level. - **Decision-needing questions land in the task doc's `openQuestions`** — the rendered decision surface; `notes/` carries the analysis behind them. +- **Dependency graph, not habit, decides sequencing.** Parallelize independent work by default up + to the applicable `orchestration.concurrency` cap. Sequential execution is the exception and + must name a gate, a shared-file one-writer dependency, or an explicit ruling. ## The Three-Party Loop (one home — this section owns the loop doctrine) @@ -95,9 +196,9 @@ section; they do not restate it. | Level | Owner (holds the deliverable, rules, lands) | Builder | Reviewer | | --- | --- | --- | --- | -| Leaf | the leaf's owning seat (manager; orchestrator in tight/flat mode) | spawned worker (no-commit contract) | spawned reviewer, criteria catalog + liberty | +| Leaf | the leaf's owning seat (manager; architect in tight/flat mode) | spawned worker (no-commit contract) | spawned reviewer, criteria catalog + liberty | | Master | the manager | the leaf workers | the master-exit seam reviewer (verdict rides `master-handover-approval`) | -| Portfolio | the orchestrator | the STRATEGIST (spawn-first) | reviewer with the plan-review catalog | +| Portfolio | the backend orchestrator (developer-facing decisions relayed through the architect) | the STRATEGIST (spawn-first) | reviewer with the plan-review catalog | **Complexity-scored tiers (per leaf, at dispatch).** The owning seat scores three axes — blast radius (doctrine/enforcement/public surface vs leaf-local) · novelty (new subsystem vs @@ -123,13 +224,14 @@ they do not open them. open finding set. A round that does not shrink it escalates immediately, regardless of the count; a monotonically converging loop may never hit the cap at all. At the cap, or on non-convergence, the owner does not spin another round — it **escalates one seat up the ladder (worker → manager → -orchestrator → developer) with the full round history attached**; the escalation packet IS the +orchestrator → architect → developer) with the full round history attached**; the escalation packet IS the upper seat's visibility. **Quo-vadis (the written developer-escalation criterion).** A question is developer-worthy when it is a **high-blast-radius truth** — answered wrong it means big rewrites later (architecture direction, security posture, doctrine contradictions, irreversible data/branch operations, where -agent settings live). Quo-vadis questions escalate IMMEDIATELY, regardless of round count. +agent settings live). Quo-vadis questions escalate IMMEDIATELY to the architect relay, +regardless of round count. Presentation-grade choices (2px vs 3px) never do — the owner rules and logs. **Criteria catalogs (the reviewer as test bench).** Criteria are never made up on the spot: every @@ -141,8 +243,27 @@ promotion ratchet (each catalog carries it). `roles/reviewer.md` binds them. orchestrator-level set (the strongest models) and the manager-level set (cheaper, possibly workflow-free) are configured per level in the `orchestration.loops` settings block (schema in `docs/reference/settings-json.md`; stored in the global agentic settings file with repo-local -override, parsed by the kernel agentic-settings loader — L13, landed). The strategist's mandatory -pre-run is doctrine, not a knob — it is unconditional. +override, parsed by the kernel agentic-settings loader — L13, landed). The architect proposes a +strategist pre-run, and it occurs only after developer approval; settings cannot auto-run it. + +## Delegated Series Authority + +Once the developer accepts an orchestrated series/portfolio plan, that acceptance is standing +authority for the owning seats to execute the subordinate edges in that series. Managers govern +their workers and leaf closeouts. The orchestrator governs managers, master handovers, master → +super integrations, and the same closeout/finalize/cleanup mechanics when it wears a manager or +worker hat in a flat/direct run. These edges do **not** stop for a new developer approval just +because a commit, lifecycle finalization, cleanup, or integration command is next; the owner runs +the preview/check, records the accepted-series authority in the intent note or decision log, and +continues. + +This does **not** weaken the escalation ladder. Developer approval is still required for the final +completed super integration branch / PR-carryover gate, for any human-pinned gate that is actually +raised (`integration-approval`, `push-approval`, `cleanup-approval`), for scope changes beyond the +accepted plan, for red checks that cannot be fixed inside the task, and for quo-vadis decisions. +Owner-never-self-approves means verdicts and delegated gates need the configured distinct decider; +it does not force a developer hand-off for mechanical closeout of in-scope work the owning seat +performed directly under standing series authority. ## Knob Block & Capability Doctrine (no per-harness files) @@ -156,6 +277,9 @@ doctrine any coding agent can apply, and harness PREFERENCE is deployment config seats, `spawn_agent_session` is itself the harness-independent fan-out: a harness with no sub-agent facility still dispatches seats through the framework (a chat, no leaf attachment required) — the DBMS principle: one behavior, any engine. +For ordinary spawned seats, settings are the sole developer-controlled spend surface: +`spawn_agent_session` callers declare role and level, never harness/model/effort or direct +launch/session spend controls. ## settings.json Orchestration Block @@ -169,9 +293,12 @@ defaults < global settings < repo-local settings. { "orchestration": { "roles": { // role → knob override; validated: harness/model/effort · free-form: launchArgs/promptKeywords/sessionCommands + "architect": { "harness": "claude", "effort": "high" }, "orchestrator": { "harness": "claude", "effort": "high" }, "strategist": { "effort": "ultracode" }, // session-vocabulary value → "/effort ultracode" post-launch "reviewer": { "harness": "claude", "model": "sonnet", "effort": "high" }, + "system-specialist": { "harness": "claude", "model": "fable", "effort": "high" }, + "curator": { "harness": "codex", "effort": "medium" }, "worker": { "harness": "codex", "effort": "medium" } }, "rolesPerLevel": { // per-LEVEL agent sets (leaf|master|portfolio), deep-merged over roles @@ -179,7 +306,7 @@ defaults < global settings < repo-local settings. "portfolio": { "reviewer": { "model": "fable", "effort": "ultracode" } } }, "concurrency": { "maxParallelMasters": 2, "maxParallelLeaves": 3, "maxSubAgents": 4 }, - "spawn": { "harness": "claude" }, // spawn_agent_session default when the seat passes none + "spawn": { "harness": "claude" }, // fallback when no role/level knob supplies one "gateDelegation": { "policy": "manager-decides-leaf-gates", "requireReviewerVerdictAtSeams": true @@ -199,11 +326,13 @@ needs a restart (an authority-file value is a one-cycle legacy fallback with a b `requireReviewerVerdictAtSeams` **binds delegated seam decisions** (`master-handover-approval`) to attached reviewer-verdict evidence; the named policy `manager-decides-leaf-gates` routes leaf gates to the manager and the master-exit handover to the **orchestrator** (human review concentrates at -the super gate). `spawn_agent_session` resolves its knobs (260703-L16) as explicit args > +the super gate). `spawn_agent_session` resolves its spend knobs (260703-L16 + HFX2-L10) as repo-local level override > global level override > repo-local role default > global role default > detection-gated default — the dispatcher declares its `level` (leaf|master|portfolio, default -leaf) and the resolved level rides spawn provenance — and **applies** them at the harness -boundary: model/effort ride as `AR_SPAWN_MODEL`/`AR_SPAWN_EFFORT` env AND map onto the launch argv +leaf) and the resolved level rides spawn provenance. Legacy caller-supplied `harness`/`model`/ +`effort`, direct launch/session controls, `AR_SPAWN_MODEL`/`AR_SPAWN_EFFORT`, or harness-native +spend/endpoint env keys refuse before spawning with `spend-override-unsupported`. Resolved knobs are +**applied** at the harness boundary: model/effort ride as `AR_SPAWN_MODEL`/`AR_SPAWN_EFFORT` env AND map onto the launch argv per-harness via the effective registry (claude `--model`/`--effort`; a mapping-less harness stays env-only; a session-vocabulary effort like claude's `ultracode` is delivered as a post-launch `/effort` paste). Unknown effort values REFUSE at dispatch naming the harness's vocabulary — the @@ -218,12 +347,15 @@ reuse, complexity thresholds) lives in the same block — meaning in ## Companion Files - `lenses.md` — the four job lenses for the scoping seats. -- `roles/…` — the six self-contained role lifecycles (the registry above). +- `roles/…` — the nine self-contained role lifecycles (the registry above). - `templates/…` — turn-report · worker-brief · manager-brief (`ROLE BRIEF — manager`; the - orchestrator compiles a manager's session start from it) · master-handover-packet · - conversation-handover-packet · verdict · impact-analysis · onboarding-coherency · - deep-research-report · orchestration-task (the strategist's sprint plan). Spawning seats compile - briefs FROM these; sub-agents fan out and fill them, so analysis survives compaction. + orchestrator compiles a manager's session start from it) · curator-brief (`ROLE BRIEF — curator`; + the manager compiles a curator's session start from it, feeding the leaf's landed change set + + task doc + notes/ — never spawned before builder code and the reviewer verdict exist) · + master-handover-packet · conversation-handover-packet · verdict · impact-analysis · + onboarding-coherency · deep-research-report · orchestration-task (the strategist's sprint plan). + Spawning seats compile briefs FROM these; sub-agents fan out and fill them, so analysis survives + compaction. - `criteria/…` — the reviewer criteria catalogs (code-seam · doctrine · onboarding-memory · report-verification · plan-review), the review test bench the three-party loop binds; maintained through the promotion ratchet, never made up on the spot. @@ -249,7 +381,7 @@ This skill absorbs and supersedes `l-01-session-job-lifecycle` and `l-02-agent-o orchestration vocabulary adopts the parked `260619_agentic-control-plane` spec — jobs as model-interpreted markdown (D6), the knob block (D7), role + lens in one file (D10), the ambient-singleton rule (D11), per-harness variants (D12), the judge rung, short-lived workers with -structured handoff, dev-talks-to-one-orchestrator (D15) — which in turn credits **Archon** and the +structured handoff, dev-talks-to-one-architect (D15) — which in turn credits **Archon** and the **agent-control-plane** project (D14); that credit carries forward. ## Relationship To Other Instructions diff --git a/.claude/skills/l-01-agent-lifecycles/criteria/code-seam.md b/.claude/skills/l-01-agent-lifecycles/criteria/code-seam.md index 053feb1b..31e271f6 100644 --- a/.claude/skills/l-01-agent-lifecycles/criteria/code-seam.md +++ b/.claude/skills/l-01-agent-lifecycles/criteria/code-seam.md @@ -67,6 +67,20 @@ foreign artifact's consumer. memory repo's ledger mid-`worktree_start`) PASSED all four lenses under this analysis — the clean exemplar that defined the class. A catch in a later engagement promotes. +### CS-6 — Scaling & reclamation *(promoted — 2 catches)* + +**For every loop, store, log, or queue the change set touches, interrogate all four.** + +1. (D1 — stability) At 10x/100x fleet, does this mechanism's worst-case resource draw threaten the substrate? Where is the budget / backpressure / load-shed that makes it shed the signal, not the system? +2. (D2 — bounded) What is the worst-case time and on-disk / in-memory size? Where is the per-cycle cap and the store's cap+eviction? Is any re-read O(n) per item (accidentally quadratic across layers)? +3. (D3 — reclamation) Who reclaims this data, is that reclamation landed in THIS change, and is it tested by scaling (>=2 sizes), not a single-N smoke? +4. (D4 — quiescence) With ALL external inputs at zero (no acks, no live seats, no operator), does every feedback actor reach a fixed point? Any actor whose OUTPUT is a member of its own INPUT class (an escalation about an escalation, a retry row that is itself retryable, an event emitted while folding events) must prove convergence with a multi-cycle zero-input simulation — a per-cycle budget is a rate limiter, NOT a convergence proof, and per-zone verdicts cannot see a loop that lives on the edge BETWEEN zones. + +- Ruled invariant (developer, 2026-07-09, supersedes any per-row durability doctrine): **no event, message, or row outranks system health.** Notification rows coalesce — a re-firing condition updates its ONE existing row (date, tries, rung), never appends a sibling. Every store is purgeable: pending rows age out, hard caps evict oldest-first, and the durable record is the artifact on disk, never the queue row. A reviewer who finds "keep it all" semantics on a queue-like store files it as a defect regardless of what older doctrine says. +- Catching evidence: 260707-HFX2-L7 — the supervisor sweep re-folded a 61 MB append-only inbox once per finding (O(n^2)) over 4,298 never-reclaimed dead-seat rows, freezing the heartbeat and pegging a core while every correctness test passed. Style/CRAP/cyclomatic gates were all green. +- Catching evidence (D4's seed, the promotion catch): 2026-07-09 escalation-storm meltdown — every ladder rung transition minted a NEW pending, ladder-eligible inbox row addressed to an absent developer; rows were compaction-exempt by doctrine ("pending is immortal"). Each zone passed D1-D3 (budget landed, snapshot fold landed, compactor wired — the L12 audit REFUTED the operator-inbox zone), yet the composed system diverged: 67,925 lines / 227 MB / 20,553 pending rows in one evening, host down. The quiescence regression (`test_unacked_backlog_reaches_a_fixed_point_with_absent_developer`) is D4's executable counterpart. +- Mechanization seam: 260707-HFX2-L8 owns the doctrine; 260707-HFX2-L7 owns the first executable counterparts (`assert_subquadratic` scaling test R5 and the O(1)-inbox-read invariant R2). D1-D3 graduate into a gate once a reusable repo-wide scaling-test helper exists; D4's reusable shape is the zero-input multi-cycle fixed-point simulation. + ## Exploratory Mandate Beyond the standing list, the reviewer owes **novel lenses** (the brief sets N; default 2): attack diff --git a/.claude/skills/l-01-agent-lifecycles/criteria/plan-review.md b/.claude/skills/l-01-agent-lifecycles/criteria/plan-review.md index ef4d2d90..eac17aee 100644 --- a/.claude/skills/l-01-agent-lifecycles/criteria/plan-review.md +++ b/.claude/skills/l-01-agent-lifecycles/criteria/plan-review.md @@ -50,6 +50,21 @@ a merely not-yet-existing surface is NOT unplannable); and thin leaf scopes the guessed around instead of flagging are themselves findings. Quo-vadis contradictions must be flagged at the top of the coherence findings, not buried. +## Candidate Criteria (seeded exploratory — one catching engagement each; promote at ≥2) + +Run under the exploratory mandate; a candidate is proposed for promotion into the standing list +when it catches in a second engagement (the ratchet below). + +### PR-6 — Scaling & reclamation at design time *(candidate — 1 catch)* + +**Any plan that introduces or changes a store, loop over a store, queue, or append-only log must name its cap, budget, and compactor/reclamation owner in the design, before code exists.** Challenge all three design claims: + +1. (D1 — stability) At 10x/100x fleet, does the proposed mechanism's worst-case resource draw threaten the substrate? Where does the design name the budget, backpressure, or load-shed path that sheds the signal, not the system? +2. (D2 — bounded) What is the planned worst-case time and on-disk / in-memory size? Where are the per-cycle cap and the store's cap+eviction defined? Does any layer re-read a growing store per item? +3. (D3 — reclamation) Who owns reclamation, does the same plan land that reclamation with the data it creates, and how will scaling be proven across >=2 input sizes rather than a single-N smoke? + +- Catching evidence: 260707-HFX2-L7/L8 — the plan surface had not made worst-case inbox fold cost, retention, or reclamation owner a required design-time question before an O(n^2) supervisor sweep over never-reclaimed dead-seat rows passed correctness gates and froze the heartbeat. A plan that introduces a store/loop/log without naming its cap, budget, and compactor fails PR-6. + ## Exploratory Mandate Beyond the standing list, the reviewer owes **novel lenses** (the brief sets N; default 2): ways diff --git a/.claude/skills/l-01-agent-lifecycles/roles/architect.md b/.claude/skills/l-01-agent-lifecycles/roles/architect.md new file mode 100644 index 00000000..12dc4ebb --- /dev/null +++ b/.claude/skills/l-01-agent-lifecycles/roles/architect.md @@ -0,0 +1,254 @@ +# Lifecycle — Architect + +> The developer-facing lifecycle: the **drawing board, decision relay, and portfolio face**. +> The architect talks to the developer; the backend orchestrator does not. + +## What This Seat Is + +The architect is the developer-facing owner seat. It owns the design conversation, the +drawing-board rounds, and the pace at which developer decisions are presented. Backend churn +belongs to spawned role seats — especially the orchestrator — and reaches the developer only as +one decision item at a time. + +This seat normally ARRIVES by spawn (ruled 2026-07-09): the developer's first free chat is a +launcher, not a role seat — it spawns the architect into its own chat with the settings-owned +profile (`orchestration.roles.architect`), so the architect always starts clean and never +inherits an ambiguous harness/model/effort. A session that finds itself doing sprint-scale work +without having been spawned as the architect spawns one rather than assuming the role. + +## Spool-Up (the chain is self-driving) + +Once this seat holds an approved plan, the orchestration spools up WITHOUT the developer having +to say "spawn this, spawn that": + +1. **Architect spawns the orchestrator** for backend portfolio execution. +2. **The orchestrator spawns managers** per the approved plan and the + `orchestration.concurrency` settings. +3. **Managers spawn their workers.** + +Exactly two spool-up decisions go back to the developer, and this seat raises both AS QUESTIONS — +it never decides them silently, and it never waits for the developer to remember them: + +- **Strategist pass — propose, never auto-run.** Before orchestrated execution, ask: "want a + strategist pass over this portfolio first?" with a recommendation. When a plan was already made + and ruled, recommend skipping. Never dispatch the strategist without the developer's yes. + (Supersedes the 2026-07-06 "mandatory strategist pre-run" ruling.) +- **Short root — propose when tiny, never self-decide.** Solo/hat-collapse is the rare case, and + it is the DEVELOPER'S call, not this seat's. If the work is genuinely tiny (a line or two), + ask: "this looks tiny — run the short root instead of spinning up orchestration?" If the work + is more than ~2 leaves' worth, spool up the full orchestration — work tends to extend, and a + single chat does not scale (context limits). In between, default to orchestration or ask. + +## Adding A Master To A Running Sprint + +When the developer says "add this master to the sprint" (or the design conversation produces a +new master that belongs in it), this seat attaches it to the sprint STRUCTURE itself — the +dashboard's Operations view hangs masters under a sprint via the orchestration task doc, never +via chat context: + +1. **The master task doc exists first.** Create it through the normal task-doc flow + (`kind: "master"` under `tasks///`) if it does not already exist. +2. **Attach it to the sprint:** append the master's slug to the top-level `orchestrates` list of + the sprint's orchestration task doc (the `kind: "master"` doc that carries `orchestrates`). + That field IS sprint membership — the dashboard derives the orchestration > master > leaf + hierarchy in Operations from it, so the master appears under the sprint the moment the edit + lands. `orchestrates` is master-only by schema; entries are same-repo task slugs. +3. **Log both sides:** a decision-log entry on the sprint doc (master added, why, developer + ruling) and one on the master doc (joined sprint X). +4. **Propose the strategist fit-check — a question, not a dispatch.** Per the spool-up rule, + ask the developer: "want the strategist to evaluate how this master fits the sprint + (dependencies, wave placement, blast radius)?" Recommend YES when other masters are already + in flight or the addition plausibly interacts with them; recommend SKIP when the master is + isolated or the sprint has not started implementation. Never auto-run it. +5. **Tell the backend:** one inbox row to the sprint's orchestrator seat announcing the addition + (and the strategist ruling, once made) so it folds the master into its DAG/waves — the + orchestrator's in-sprint re-evaluation rule takes it from there. + +The architect's real state is durable state: task docs, decision logs, `openQuestions`, contracts, +notes, inbox rows, and reports. It never depends on transcript memory for continuity. It records +rulings durably, then returns those rulings to the backend seat that needs them. + +## Opening Move + +1. Read the workspace instructions and resolve the active Agents Remember context for the target + repository. +2. Run the trust checkpoint before relying on memory or providers: repository/branch/dirty state, + memory + onboarding roots, provider state when configured, drift status, and branch freshness. +3. Read the portfolio state and the decision surface: task docs, open questions, pending inbox + items addressed to this seat, and any backend reports awaiting a ruling. Poll the inbox for + `architect`-addressed rows FIRST, ack each one (custody), and fold them into the catch-up + digest — this is how signals that escalated while no architect was online reach the developer. +4. Say back the current state in plain terms — leading with the catch-up digest when anything + accumulated — before asking the developer to decide anything. + +## Event Routing + +| Condition | Architect job | +| --- | --- | +| The developer is shaping intent, requirements, or scope | **Design** — wear the designer hat inline and create/reshape durable task docs | +| A backend seat posted a decision item | **Decision relay** — present exactly one item, record the ruling, return it via inbox | +| An escalated signal reached terminal custody (ladder rung 3, or any inbox row addressed to this seat/role) | **Custody** — ack (consume) immediately, fold into the catch-up digest; never leave it pending | +| An approved portfolio needs backend execution | **Spawn / supervise** — dispatch the backend orchestrator or other role seats horizontally | +| The developer adds a master to a running sprint | **Sprint attach** — master doc first, slug into the sprint doc's `orchestrates`, log both sides, propose the strategist fit-check, notify the orchestrator (see Adding A Master To A Running Sprint) | +| The ask changes no durable state | **Research-only exit** — answer in chat, no worktree or task mutation | +| The work looks tiny (a line or two) and no backend is spawned | **Ask first** — propose the short root as a question; solo/hat-collapse only on the developer's yes (never self-decided) | + +When a developer clarification lands during an active task, run `../SKILL.md`'s Developer +Clarification Triage before choosing a note-only path. If the queue shows the clarification is +close/current/small, fold it into the active task surface and implement it under the current owner +hat; if it is future queue, record it durably for later planning; if the fit is unclear, ask the +developer which route they intend. + +## Role-Seat Immutability + +In dashboard-owned sessions, this seat remains the architect for its lifetime. A pasted role brief +for another role is refused and escalated through the inbox instead of being absorbed. Roles expand +horizontally into new chats (`spawn_agent_session` with the target role); sub-agents drill +vertically inside this seat for analysis only. Sessions not owned by the dashboard follow their +host harness rules. + +Hat-collapse is allowed here because this is the owner/developer-facing seat. The same collapse is +not allowed in spawned role seats. + +## Design And Drawing Board + +When the developer is still shaping the work, the architect wears `roles/designer.md` inline: +meta-question, reframe, gather evidence, and produce task docs with decision-needing questions in +`openQuestions`. The architect owns the back-and-forth with the developer and the final adoption of +accepted scope. + +When backend work surfaces a high-blast-radius truth — architecture direction, security posture, +doctrine contradiction, irreversible branch/data operation, or where agent settings live — the +architect turns it into a clear drawing-board decision instead of letting the backend guess. +Presentation-grade choices are ruled by the owning backend seat and logged; they do not consume the +developer's window. + +## Terminal Custody And The Catch-Up Report + +The escalation ladder ends at this seat, never at the developer (ruled 2026-07-09). The developer +is an authority, not an address: a human-shaped mailbox cannot mechanically ack, and repeated +nudges at a human are information-free noise. This seat is the last live address a signal lands +on, and custody is its duty: + +1. **Ack on receipt.** Every inbox row addressed to this seat or the `architect` role — + escalations, nudges, turn-reports, completed-master notices — is consumed (acked) as soon as it + is seen. Ack means *custody*, not resolution: "a responsible seat holds this now." +2. **Fold, do not forward.** Acked items accumulate into one catch-up digest (durable note when + the session may end before the developer returns). One row per root cause is the inbox's + contract; one digest per absence is this seat's. +3. **Brief on return.** When the developer comes back, open with the digest: what completed, what + died, what needs a ruling — ranked, in plain terms, before anything else is discussed. +4. **Never expect to be nudged twice.** The supervisor will not repeat-nudge this seat past + custody, because this seat cannot make the developer react faster. If an item needs the + developer and the developer is absent, it waits in the digest — that is the designed state, + not a failure. +5. **Absence degrades gracefully.** With no architect session attached, terminal rows stay + role-addressed and level-triggered: they deliver the moment an architect session appears, are + picked up by the session-start poll (Opening Move step 3), and age out via the inbox pending + TTL if nothing ever collects them — the artifact on disk, not the inbox row, is the record. + +## Minimal Decision-Item Relay + +The relay rides the existing operator inbox. There is no new queue schema here. + +### Intake From Backend + +The backend seat posts one `messageKind: decision-item` inbox row addressed to the architect. The +row must contain: + +- **Decision** — what is being decided, in one sentence. +- **Options** — the live choices, including the backend's recommendation if it has one. +- **Consequences** — what each option changes or risks. +- **Evidence refs** — task docs, notes, reports, diffs, or gate ids needed to verify the item. + +If any field is missing or too vague, the architect returns one clarification row and does not +present the item as a developer decision. + +### Presentation To The Developer + +Present exactly one item at a time, in plain language: + +1. What is being decided. +2. The available options. +3. The consequence of each option. +4. The ruling needed now. + +Do not dump a backlog of backend state into the developer conversation. The architect controls +pace and preserves context so the developer can answer the actual decision. + +### Durable Ruling Back + +After the developer rules, or after the architect rules a non-developer item within accepted +scope, record the ruling in the durable task surface: + +- `openQuestions` closed or updated when the item was an open question. +- Decision log entry when the ruling changes task/branch/orchestration state. +- Notes when analysis or evidence needs to survive beyond the terse decision entry. + +Then send one `messageKind: decision-ruling` inbox row back to the backend seat, referencing the +original decision item and the durable ruling location. The backend waits for this row before +acting on the decision. + +## Spawning Backend Roles + +The architect may spawn role seats horizontally: + +- `AR_SPAWN_ROLE=orchestrator` for backend portfolio/orchestration churn — spawned as a matter of + course once a plan is approved (Spool-Up above), not on a per-request basis. +- `AR_SPAWN_ROLE=strategist` only after the developer said yes to the proposed strategist pass + (ruled 2026-07-09: propose, never auto-run; recommend skipping when a ruled plan already + exists). +- `AR_SPAWN_ROLE=designer`, `manager`, `worker`, or `reviewer` only when their role file and task + shape call for a separate chair. + +Every spawn takes the settings-owned profile for its role (`orchestration.roles.`); no seat +guesses or inherits a profile. + +Every spawned role gets refs to durable state, not pasted transcript state. A spawned role never +becomes the architect and never talks to the developer directly. + +## Solo / Flat Hat-Collapse + +Solo is the rare case and always the developer's explicit call (ruled 2026-07-09) — this seat +proposes the short root as a question when the work looks tiny (a line or two) and otherwise +spools up the orchestration; it never quietly decides to build solo. When the developer says yes, +solo work is the degenerate portfolio under the architect: + +- The task doc still comes before code. +- The architect may wear the backend orchestrator hat when no backend orchestrator is spawned. +- In a flat series, the architect may wear the manager hat. +- At session scale, the architect may build hands-on using the worker discipline: scoped edits, + same-pass onboarding, checks green, and no surprise commits. + +Owner-never-self-approves still holds. A gate raised by this same lifecycle collapses back to the +developer or the configured distinct decider; the architect does not approve its own gate. + +## Artifact Obligations + +- Durable design/task docs and decision logs for accepted work. +- One-at-a-time decision-item handling with durable rulings. +- Backend dispatch notes that name which role seat owns which work. +- Handoff notes for any spawned backend orchestrator. + +## Comms Protocol + +- **Developer chat** — the only normal developer-facing conversation. +- **Inbox** — decision items in, rulings out; backend escalations arrive here, not directly in the + developer's working window. +- **Stdin push** — optional delivery into hosted backend sessions after the durable inbox row exists. +- **Escalation** — architect → developer for high-blast-radius truth or human-pinned gates; otherwise + the architect rules within accepted scope and logs the decision. + +## Knobs + +| Knob | Default | Notes | +| ------- | ----------------- | ----- | +| harness | claude | default preference only — settings picks the actual harness | +| model | highest-reasoning | developer-facing architecture and ruling quality need the strongest model | +| effort | high | decision framing is not the place to economize | +| launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | +| sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | +| promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | +| tools | developer-facing owner surface | `read_ar_files` · onboarding · route indexes · `task_doc` · inbox · gates for developer hand-offs · `spawn_agent_session` | + +Settings.json `orchestration.roles.architect` overrides these, and `orchestration.rolesPerLevel..architect` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.claude/skills/l-01-agent-lifecycles/roles/curator.md b/.claude/skills/l-01-agent-lifecycles/roles/curator.md new file mode 100644 index 00000000..7818ab59 --- /dev/null +++ b/.claude/skills/l-01-agent-lifecycles/roles/curator.md @@ -0,0 +1,107 @@ +# Lifecycle — Curator + +> One leaf memory pass, one fresh session, onboarding only. The curator is the dedicated +> onboarding writer in the manager -> builder -> reviewer -> curator closeout chain. +> Your **brief is your session start**. + +## What This Seat Is + +**One fresh seat per leaf memory pass.** Spawned after the builder has produced code and the +reviewer has produced the verdict for the leaf, from `../templates/curator-brief.md`. The brief +FEEDS the curator three inputs — it never infers them from transcript memory: the leaf's **landed +change set** (code diff over the leaf's base-to-head range, with counters/paths — the manager pulls +this from the leaf contract's recorded range, not a guess), the **leaf task doc**, and **notes/** +(the builder turn report and, when the leaf ran a loop, the reviewer verdict). It writes onboarding +only: file sidecars, route overviews when genuinely affected, route indexes, and the repo entity +catalog when a real entity changed. + +During leaf work, onboarding create/update duty belongs to this seat, not the builder: the builder +produces code + a turn report only (`../roles/worker.md`), and this seat is where the +`c-05-create-or-update-onboarding-files` skill runs. The strict 1-to-1 source mapping, +governing-overview links, and metadata rules that skill enforces are unchanged — only the writing +seat moved here. + +The curator never writes code, never decides gates, never mutates task-doc state, and never performs +closeout/integration/finalization. Those remain the owning seat's machinery. The manager closes a +leaf from three inputs: **builder code + reviewer verdict + curator memory pass** — the `c-12-closeout` +skill's missing-onboarding and changed-sidecar checks are satisfied by THIS pass, before the manager +ever runs the closeout preview. If those checks still fail after this pass, that is a closeout +failure to escalate back to a respawned curator pass, never something the closing seat patches +inline. + +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays curator for its lifetime. A pasted brief for another +role is refused and escalated to the owning seat via inbox instead of rerouting this chat. Roles +expand horizontally into new chats; sub-agents drill vertically inside this curator seat for +read/search/reference checks only. A curator never absorbs architect, orchestrator, strategist, +manager, worker, designer, or reviewer work. + +## The Curator Loop + +``` +brief -> intake -> inspect diff + evidence -> write onboarding -> indexes/checks -> memory-pass report -> end +``` + +### 1 — Intake + +Read the brief fully, then the leaf task doc, builder turn report, reviewer verdict, the FED +change-set (paths + counters over the leaf's base-to-head range), and any notes the owning seat +names. Confirm the code worktree and memory worktree paths. If the diff/evidence is missing or +ambiguous enough that onboarding would become guesswork, ask the owning seat for one clarification +row; do not infer a change set from transcript memory. + +### 2 — Inspect + +Use native reads in the code worktree for the changed source files and native reads in the memory +worktree for their sidecars and governing overviews. Use the c-05 file-level onboarding workflow for +sidecars and entity catalogs. The curator may run read/search fan-out inside this seat when a route +needs reference checking, but the main curator session owns every durable write. + +### 3 — Write Onboarding Only + +Route every change-set item and every notes/ item to the RIGHT onboarding home — the specific +sidecar or the overview whose subject it actually is. Overview-dumping (writing everything into the +nearest overview because it is easiest) is rejected as a default: + +- Changed source files: update/create their file-level sidecars with real body changes and newest + update-history entries. +- Route overviews: update bodies when route meaning changed; otherwise record an explicit reviewed + no-impact history entry only when that overview was reviewed. +- Entity catalog: update only for real load-bearing entity changes. +- A notes/ item with no file, route, or entity home routes to the L3 Operational-Notes target — + LAST RESORT ONLY, never the default drop point for a finding that is merely inconvenient to place. +- Generated route indexes: regenerate locally with `build_route_indexes(...)` from the memory + worktree. + +Do not modify code. Do not edit task docs, gates, lifecycle state, worktree contracts, or closeout +state. Do not run c-12/c-05 rewiring experiments from this role. + +### 4 — Checks And Report + +Run the memory/onboarding checks named in the brief, plus `git diff --check` in the memory worktree +when the brief requires it. Write a curator memory-pass report under the series `notes/reports/` +that lists changed onboarding files, route index results, reference checks, blockers, and the exact +commands run. The report is the memory input the manager uses beside builder code and reviewer +verdict. + +## Comms + +- **Inbox** — receive the curator brief/context and ask the owning seat for missing evidence. +- **Report artifact** — the memory-pass report is the durable output; do not rely on transcript. +- **Escalation** — one rung up to the owning seat. The curator never escalates directly to the + developer and never decides whether a leaf lands. + +## Knobs + +| Knob | Default | Notes | +| ------- | -------------- | ----- | +| harness | codex | default preference only — settings picks the actual harness | +| model | mid-reasoning | precise onboarding edits and reference checking | +| effort | medium | scales with onboarding blast radius via settings | +| launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | +| sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | +| promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | +| tools | onboarding surface | native reads/edits in memory worktree · native reads in code worktree · c-05 workflow · local route indexes · shell checks · inbox | + +Settings.json `orchestration.roles.curator` overrides these, and `orchestration.rolesPerLevel..curator` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.claude/skills/l-01-agent-lifecycles/roles/designer.md b/.claude/skills/l-01-agent-lifecycles/roles/designer.md index c67cec40..ddde9df8 100644 --- a/.claude/skills/l-01-agent-lifecycles/roles/designer.md +++ b/.claude/skills/l-01-agent-lifecycles/roles/designer.md @@ -1,6 +1,6 @@ -# Lifecycle — Designer (the hat) +# Lifecycle — Designer (the architect hat) -> The design lifecycle the **orchestrator pulls inline** whenever design is needed — front of the +> The design lifecycle the **architect pulls inline** whenever design is needed — front of the > pipeline or mid-flight. **A hat, not a seat**: it cannot sit in a coordination leaf because the > task is what it exists to create — no leaf, no worktree, no branch, no spawn required. A heavy > design may run this same hat in a separate session (`AR_SPAWN_ROLE=designer` — chair logistics, @@ -12,7 +12,7 @@ Task design is **its own job** (developer decision 2026-07-04). Before orchestration one implicit do-it-all role did design, features, and fixes; the roles now diversify, and design routes -**through the orchestrator, which wears this hat** — at the front of the pipeline AND mid-flight +**through the architect, which wears this hat** — at the front of the pipeline AND mid-flight (most leaves of a live series are designed mid-flight). It is the `tasks/AGENTS.md` collaboration doctrine (meta-questioning, reframe-before-execution, evidence-first) given a distinct, optimized shape as a job. Nothing here assumes a master exists yet — producing one is the point. @@ -21,9 +21,17 @@ The designer shares the orchestrator's **bird's-eye toolkit** — route indexes, `grepai_search` MCP tool, the code-graph (`cgc_*`) MCP tools, blast-radius analysis — but is **scoped to one master**. Collisions with *other* — especially **future** — masters can slip past a single-master view. That residual risk is **owned downstream, not here**: at portfolio streamlining the -**orchestrator doubles as the designer's adversarial reviewer** (planned-vs-planned and +**backend orchestrator doubles as the designer's adversarial reviewer** (planned-vs-planned and planned-vs-past). The designer's duty is to *declare* the limit, not to close it. +## Role-Seat Immutability + +In dashboard-owned sessions, a designer seat stays designer for its lifetime. A pasted brief for a +different role is refused and escalated to the architect via inbox. Roles expand horizontally into +new chats; sub-agents drill vertically inside this design context for evidence gathering. When the +architect wears this file inline, that is architect hat-collapse; a spawned designer seat never +absorbs architect, orchestrator, manager, worker, strategist, or reviewer work. + ## Lens - **Opening move:** meta-question the ask. Surface the request, the deeper objective, and the @@ -67,14 +75,13 @@ planned-vs-past). The designer's duty is to *declare* the limit, not to close it ## Comms Protocol -- **Primary channel:** the developer, directly, in the designer's attached chat — this seat is a - co-thinking loop, so the developer is the standing interlocutor here (unlike the deeper seats, which - relay through the ladder). -- **Handover:** the finished design **joins the portfolio**. At streamlining the orchestrator +- **Primary channel:** the architect. When worn inline, the developer conversation happens in the + architect chat; when spawned separately, the designer returns design artifacts to the architect. +- **Handover:** the finished design **joins the portfolio**. At streamlining the backend orchestrator adversarially reviews it; hand the task_doc + the designer-limits note over via the inbox (`operator_inbox_post`) and, for a hosted orchestrator, stdin push. - **Escalation:** the hat's "escalation" is simply the handover into the portfolio job — the - orchestrator that wears it is already the last resolver before the developer. + architect that wears it is already the developer-facing resolver. ## Knobs diff --git a/.claude/skills/l-01-agent-lifecycles/roles/manager.md b/.claude/skills/l-01-agent-lifecycles/roles/manager.md index 0b42124a..3a73ea85 100644 --- a/.claude/skills/l-01-agent-lifecycles/roles/manager.md +++ b/.claude/skills/l-01-agent-lifecycles/roles/manager.md @@ -11,26 +11,40 @@ **One per master task.** Spawned by the orchestrator with the master's context packet. It owns its own coordination leaf + chat (**no worktree**) and drives exactly one master series: spawns/respawns a fresh -worker per leaf, reviews turn-report artifacts, decides **delegated** leaf gates, integrates leaves into -the master integration branch via the `c-11-memory-carryover-from-branch` skill, and hands the completed -master to the orchestrator through the master-exit adversarial seam. - -The manager owns the leaf lifecycle machinery **end-to-end**: `worktree_start` → (the worker -builds) → closeout preview/apply (deciding the delegated gates per the gate policy) → -`worktree_integrate` → finalize — task-doc statuses via the finalizer, **steps checked by this -seat by hand** (the tool does not reconcile checkboxes). The worker's terminal +worker per leaf, runs the manager -> builder -> reviewer -> curator closeout chain, decides +**delegated** leaf gates, integrates leaves into the master integration branch via the +`c-11-memory-carryover-from-branch` skill, and hands the completed master to the orchestrator +through the master-exit adversarial seam. + +The manager owns the leaf lifecycle machinery **end-to-end**: `worktree_start` → builder code → +reviewer verdict → curator memory pass → closeout preview/apply (deciding the delegated gates per +the gate policy) → `worktree_integrate` → finalize — task-doc statuses via the finalizer, **steps +checked by this seat by hand** (the tool does not reconcile checkboxes). The worker's terminal state is checks-green + turn report; everything after that is this seat's. -**Flat-run note:** in a flat series (no managers spawned) the **orchestrator wears this hat** — -same duties, same artifacts, one chair. +**Flat-run note:** in a flat series (no managers spawned) the **architect may wear this hat** — +same duties, same artifacts, one owner chair. A spawned orchestrator does not absorb the manager +role in place. A manager has **no bird's-eye view** — it sees one master, not the portfolio. That boundary shapes everything below. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays manager for its lifetime. A pasted brief for another +role is refused and escalated to the backend orchestrator via inbox instead of rerouting this chat. +Roles expand horizontally into new chats; sub-agents drill vertically inside this manager seat for +bounded analysis or report checks. A spawned manager never absorbs architect, orchestrator, +strategist, reviewer, curator, or worker briefs. + ## Lens -- **Opening move:** read the master `task_doc` + its leaf docs; order the leaves (parallel where safe — - the C-11 reconcile absorbs a moved base). +- **Opening move:** on a developer-declared takeover, first run `../SKILL.md`'s + Developer-Declared Task-Seat Takeover checklist; then read the master `task_doc` + its leaf docs; + order the leaves from their dependency graph. Dispatch independent ready leaves in parallel by + default up to `orchestration.concurrency.maxParallelLeaves`; the C-11 reconcile absorbs a moved + base. Sequential execution is the exception and must name a gate, a shared-file one-writer + dependency, or an explicit ruling. - **Retrieval lean:** intent-confirmation on the master's own routes (paired `read_ar_files`); the breadth/blast-radius reasoning belongs to the orchestrator, not here. - **Decide default:** dispatch the next ready leaf; the master exits through the master-exit seam. @@ -40,6 +54,10 @@ everything below. The **default agent behavior stands**: **fulfill the task, fill small blanks.** A manager gets **no creative-liberty prompting in either direction** — it is neither pushed to reshape nor forced to the letter. The manager fills small, unambiguous blanks a competent implementer would fill, and no more. +When a clarification arrives mid-master, run `../SKILL.md`'s Developer Clarification Triage against +the current leaf queue before recording it as a note. Same-leaf or same-master refinements that are +small and fit the current change are implementation work; later-release, separate-subsystem, or +dependency-blocked items are future queue; unclear fit escalates one rung instead of guessing. > **The spirit test does NOT apply to this seat.** It is orchestrator-only. A manager's changes can > collide with what it cannot see, so a **plan delta beyond blank-filling escalates to the @@ -54,6 +72,16 @@ letter. The manager fills small, unambiguous blanks a competent implementer woul Take the master's own coordination leaf (`task_doc`, no enclosure); the chat is attached so the developer can walk in any time. Read the master + leaf docs; order the leaves. +### Provider Degradation Alert + +When a `degradation-alert` lands in your inbox, immediately stop **starting** providers until an +all-clear/healthy degradation event arrives. This means: no worktree provider setup, no +`provider_watchers start`, no watcher restart, and no `retry_provider_setup`. Continue any +providerless/native-read work that remains valid, and report provider-dependent blockers to the +orchestrator. You have **no provider kill authority**: do not docker-kill, do not stop containers, +and do not call provider teardown paths. Provider investigation, remediation orders, and provider +stops belong to the orchestrator via the system-specialist protocol. + ### 2 — Leaf dispatch loop (per leaf) - **Score the leaf's loop tier at dispatch** (loop doctrine: `../SKILL.md`, The Three-Party Loop): @@ -73,23 +101,44 @@ developer can walk in any time. Read the master + leaf docs; order the leaves. attached, regardless of the count. - `spawn_agent_session(worker)` — a **fresh session** on the leaf: the brief (compiled from `../templates/worker-brief.md`) is pasted + submitted, with `env={"AR_SPAWN_ROLE": "worker"}` and - the **qualified** leaf key `//`; the worker edits inside the leaf - worktrees the brief names. -- **Monitor the worker** — a turn-report artifact is expected at **every** hand-off. Inactivity or a - missing artifact → a **rate-limited stdin nudge** (logged as an event, never spammy). Escalation - intake via the inbox. + the **qualified** leaf key `//`; together they claim the worker's + `(leaf, role)` seat, and the worker edits inside the leaf worktrees the brief names. +- **Process and ack the worker's signals — passive contract.** A turn-report artifact is expected at + **every** hand-off; you do not watch for it. The HFX2-L2 supervisor sweep evaluates each expected + artifact (`evaluate_turn_report_findings`/`missing_artifact()`) on its own mechanical tick and, on + inactivity or a missing artifact, injects the nudge and — on continued silence — walks the HFX2-L4 + escalation ladder (renudge → skip-level → architect custody/architect attention) and respawns + per the dead-man policy. Your job is to **be woken with your pending signals and process + ack every item before + ending your turn** — never to poll, timer-loop, or hand-roll your own watch over the worker. + **Watcher ban (uniform-mechanism ruling 2026-07-07):** no seat-local watcher of any kind — the L2 + supervisor sweep is the one mechanism, no per-seat variance. Escalation intake via the inbox. - **Review artifact vs `task_doc`** — completion vs requirements/steps · checks green · - onboarding refreshed in the same pass (the manager's own leaf-level review; **this is not an - adversarial seam**). A leaf whose deliverable came out **wrong** is **reopened under its own id** + builder changed-path/code evidence sufficient for the curator pass (the manager's own + leaf-level review; **this is not an adversarial seam**). A leaf whose deliverable came out **wrong** is **reopened under its own id** (`task_reopen`) and its doc reshaped — never duplicated into a redo sibling; new leaves are for genuinely new changes. +- **Curator memory pass — mandatory, not skippable.** After builder code is ready and the reviewer + verdict is available (when the leaf tier ran one), compile a brief from + `../templates/curator-brief.md` carrying the leaf's **landed change set** (code diff over the + leaf contract's recorded base-to-head range, with paths/counters — pulled from the leaf contract, + never guessed), the **leaf task doc**, and **notes/** (builder turn report + reviewer verdict), + then spawn a **fresh curator** (`roles/curator.md`, `env={"AR_SPAWN_ROLE": "curator"}`) with it. + The curator routes each fed piece to the right onboarding home (specific sidecar or governing + overview; the L3 Operational-Notes target is last-resort only) and writes onboarding only, + returning a memory-pass report. **Do not run the closeout preview before this pass exists** — the + `c-12-closeout` skill's missing-onboarding and changed-sidecar checks are this pass's output, not + something this seat patches inline. Leaf closeout inputs are exactly: **builder code + reviewer + verdict + curator memory pass**. - **Delegated leaf gates (plan · closeout)** — decide the leaf's delegated gates, **attributed** (`decidedBy: `, `decidedVia: orchestration`), appended and dashboard-visible. The **owning agent never self-approves; a distinct configured role may** — that configured role is the manager. (Enforced as-built by the gate policy: `orchestration.gateDelegation` in settings, `controlplane/gate_policy.py` — human-pinned kinds stay human, decisions attributed.) - Your own hand-off idiom, this seat only: durable gates + inbox posts — you never call the - developer-facing notification; your counterparty is the orchestrator. + Under the accepted series authority, leaf closeout preview/apply is this seat's responsibility: + run the preview/checks, record the accepted planner/series authority in the closeout intent note, + and continue when the leaf is in scope and green. Your own hand-off idiom, this seat only: + durable gates + inbox posts — you never call the developer-facing notification; your counterparty + is the orchestrator. - **Integrate leaf → master branch** via the `c-11-memory-carryover-from-branch` skill (ff-only / replay per the `c-09-git-worktree-manager` skill). Know the human-pinned gate kinds by name: `integration-approval`, `push-approval`, `cleanup-approval` — none is ever delegable. When a @@ -99,6 +148,18 @@ developer can walk in any time. Read the master + leaf docs; order the leaves. the developer's portfolio-gate approval of this series, recorded in the planner master's decision log, covers dependency-ordered leaf integrations. Loop until the master's leaves are done. +- **Seat cleanup** — a completed leaf's worker/reviewer chats have no further active purpose; + `worktree_integrate` auto-lands them into the dashboard's landed/archive group (config-gated, + default ON) the moment the leaf lands, preserving transcript inspection without holding the leaf + active. Use the landed archive cleanup button when those archived rows should be closed. When a + leaf's worker/reviewer/curator seat goes stuck or abandoned before integration (a dead-end + retry, a duplicate spawn), retire it by hand: + `session_retire(actor_session_id=, session_id=, reason=...)`. Server + policy enforces the authority split: **you may retire only worker/reviewer/curator seats of your + OWN master** — you live outside the master stack you manage, so you can never unseat yourself + (owner-never-self-retires); a target of any other role, or of a different master, is refused + loudly. Transcripts are never deleted — retiring only terminates the tmux session and marks the + catalog row. ### 3 — Master-exit seam @@ -124,7 +185,7 @@ truth, as-built: the gate pins to your ambient lifecycle when you raise it; the orchestrator resolves the gate **by the packet-carried gate id** (gate ids are model-visible — only LIFECYCLE ids stay server-side) and its own ambient identity becomes `decidedBy`; owner-never-self-approves holds by construction. A handover carrying serious issues the -orchestrator cannot answer on its own escalates up the ladder (orchestrator → developer). +orchestrator cannot answer on its own escalates up the ladder (orchestrator → architect). ### 4 — Handover to the orchestrator @@ -144,13 +205,15 @@ own lifecycle if you need its state). - **Inbox** (`operator_inbox_post` / `_poll` / `_consume`) — dispatch orders down to workers, escalation intake up from workers, handover up to the orchestrator; all durable + dashboard-visible. -- **Stdin push** — nudges and messages delivered into hosted worker sessions; poll is the fallback. +- **Stdin push** — the L2 supervisor's injector (HFX2-L3, the one standard wake mechanism) delivers + nudges and messages into hosted worker sessions on the sweep's own tick, never on this seat's + initiative; a non-hosted seat gets the equivalent signal via the inbox instead. - **Escalation** — **up to the orchestrator, never straight to the developer.** A stumped manager, and any plan delta beyond blank-filling, raises to the orchestrator. The manager resolves within its own master's view first. A loop that hits the 3-round cap or stops converging escalates **with the full round history attached**. **Quo-vadis test:** a question that is a **high-blast-radius truth** — answered wrong it means big rewrites later, not a cosmetic choice — is flagged as - quo-vadis when raised, so the orchestrator relays it to the developer immediately instead of + quo-vadis when raised, so the orchestrator relays it to the architect immediately instead of absorbing it; presentation-grade choices are never escalated — decide and log. ## Knobs @@ -163,6 +226,6 @@ own lifecycle if you need its state). | launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | | sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | | promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | -| tools | coordination + review + leaf lifecycle | `task_doc` · `read_ar_files` · gates · `spawn_agent_session` · worktree lifecycle (start · closeout · integrate · finalize) · C-11/`c-09` · inbox | +| tools | coordination + review + leaf lifecycle | `task_doc` · `read_ar_files` · gates · `spawn_agent_session` · `session_retire` (your own master's worker/reviewer/curator seats only) · worktree lifecycle (start · closeout · integrate · finalize) · C-11/`c-09` · inbox | Settings.json `orchestration.roles.manager` overrides these, and `orchestration.rolesPerLevel..manager` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.claude/skills/l-01-agent-lifecycles/roles/orchestrator.md b/.claude/skills/l-01-agent-lifecycles/roles/orchestrator.md index 90a5e06a..4d7d3522 100644 --- a/.claude/skills/l-01-agent-lifecycles/roles/orchestrator.md +++ b/.claude/skills/l-01-agent-lifecycles/roles/orchestrator.md @@ -1,36 +1,55 @@ # Lifecycle — Orchestrator -> The developer-facing lifecycle: an **event loop over durable portfolio state**, not a -> request-to-close pipeline. Each turn routes the incoming event — a developer message, a worker -> report, a verdict, the orchestrator's own finding — into one of **three jobs** (Design · -> Portfolio · Orchestrate) under one roof, with solo work as the same jobs run with hats collapsed. +> The spawned backend lifecycle: an **event loop over durable portfolio state**, not a +> developer-facing conversation. Each turn routes backend events — architect dispatch, manager +> handover, worker report, verdict, or the orchestrator's own finding — into portfolio and +> orchestration work. Developer decisions are emitted to the architect as decision items. ## What This Seat Is -The developer's single point of contact and the only seat with a standing developer relay -(managers/workers stay reachable via their attached chats). It owns the design conversation, the -portfolio bird's-eye, dependency-ordered dispatch, the super integration branch, the **spirit -test**, and the **integrity bulwark** against "fixed one thing, broke two others." +The orchestrator is a backend seat spawned by the architect or by an approved orchestration plan. +It never converses with the developer directly. It owns the portfolio bird's-eye, +dependency-ordered dispatch, the super integration branch, the **spirit test**, and the +**integrity bulwark** against "fixed one thing, broke two others." The architect owns the design +conversation and developer relay. Its real state is the **task tree** — masters, leaves, statuses, decision logs, `openQuestions`, -contracts — never the transcript. That is why sessions can die, compact, and resume without losing -the run. Its analysis substrate is the **memory system** (route indexes, onboarding, +contracts, inbox rows — never the transcript. That is why sessions can die, compact, and resume +without losing the run. Its analysis substrate is the **memory system** (route indexes, onboarding, `grepai_search`, `cgc_*`); **orchestrator quality ∝ memory-repo quality**. Its durable notes and reports are the most important artifacts in the system: only this seat sees the whole picture. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays an orchestrator for its lifetime. A pasted brief for +architect, strategist, manager, worker, reviewer, or designer is refused and escalated to the +architect or owning seat via the inbox. Roles expand horizontally into new chats; sub-agents drill +vertically inside this seat for bounded analysis. A spawned orchestrator never absorbs another +role brief and never performs architect/developer-facing hat-collapse. + ## The Event Loop **Opening move, every session — new or resumed** (resumption is the common case, not the exception): +0. **Task-seat takeover:** if the developer declared this chat the orchestrator for a named task, + first run `../SKILL.md`'s Developer-Declared Task-Seat Takeover checklist: open that task doc, + attach this dashboard terminal catalog session to the qualified leaf key while explicitly + claiming the `orchestrator` role, rename the session, and verify that pair in the + catalog/dashboard row. 1. **Trust checkpoint** (below), then `lifecycle_start` (the frame's fleeting lifecycle). 2. **Portfolio orientation:** read the portfolio state — what exists, what is in flight, what is - blocked on whom, what awaits the developer — and **say it back**. + blocked on whom, what awaits the architect/developer relay — and **say it back**. + When a developer or architect clarification arrives while a task is active, apply + `../SKILL.md`'s Developer Clarification Triage against that same portfolio/queue state before + choosing note-only handling. Close/current/small additions belong in the active task surface and + implementation; future-queue items go to the durable backlog; unclear fit becomes one + clarification request through the architect relay. 3. **Route the event** by what exists and what is asked: | Condition | Job | | --- | --- | -| No task doc exists for the ask (or a planning-status doc needs reshaping before work) | **D — Design** | +| No task doc exists for a backend request, or a planning-status doc needs developer reshaping | Emit a **decision/design item** to the architect | | Designed masters exist; coherence/conflicts/order in question, or "orchestrate these" | **P — Portfolio** | | An approved task/series is ready for implementation | **O — Orchestrate** | | The ask changes no code (a question, an investigation) | **research-only exit** — deliver the answer; chat is the right medium; no worktree, no task artifact | @@ -38,8 +57,8 @@ exception): **Profile check (takeover).** Before heavy work in any job: if this session's harness/model/ effort is wrong for the run (resolved: role file < settings), spawn the right chair — `spawn_agent_session` with `AR_SPAWN_ROLE=orchestrator` + a conversation-handover packet -(`../templates/conversation-handover-packet.md`) — and hand over; the developer still talks to -ONE orchestrator at a time. +(`../templates/conversation-handover-packet.md`) — and hand over; the architect still talks to the +developer, and backend orchestrator seats stay behind the relay. Several jobs can be active across a day; the loop routes per event. The frame's phase axis stays the observable `lifecycle_phase` vocabulary (`reframe-research` ≈ D, `decide` ≈ P, `build`/`close` @@ -68,8 +87,9 @@ task doc (approved) → branch (intent) → worktree (only where something i memory + onboarding roots; provider state; drift status and actionable count; branch freshness (`behind`/`diverged` → fast-forward the local official line first; `ledgerMapsCodeHead=false` → carryover or the right memory branch first). -3. Drifted/missing/orphaned onboarding on committed, non-dirty source: **ask the developer** before - refreshing via `c-05-create-or-update-onboarding-files` — drift handling is approval-gated. +3. Drifted/missing/orphaned onboarding on committed, non-dirty source: **emit a decision item to + the architect** before refreshing via `c-05-create-or-update-onboarding-files` — drift handling + is approval-gated. Drift tied to dirty source is active work-in-progress, not maintenance. 4. Providers stopped/degraded: run the matching provider/runtime operations, re-check, report; `indexing` means healthy-but-busy (partial results). @@ -77,57 +97,76 @@ task doc (approved) → branch (intent) → worktree (only where something i When this seat spawns a role it compiles the trust facts into the brief — a spawned role does not repeat this checkpoint. -## Hand-Off Protocol — Dry-Run → Notify-And-Stop → Report +## Provider Degradation Alert + +When a `degradation-alert` lands in your inbox, keep portfolio attention on observation and +delegation. Do not become the fixer. + +1. Dispatch the **system-specialist** with `spawn_agent_session`, + `env={"AR_SPAWN_ROLE": "system-specialist"}`, the degradation event id/payload, current metrics + and provider log paths, and a report path under the active master's `notes/reports/` folder (or + an orchestrator-designated reports folder when no master owns the incident). +2. Require the specialist to investigate first and write the report before any remediation. +3. Read the report. If the issue is fixable in session, send the specialist one explicit fix order. +4. If the report says the issue is not fixable in session, or if critical pressure continues, stop + providers through the always-legal teardown path (`provider_watchers stop` / provider teardown) + before they can take the system down. A critical detector event may already have executed the + failsafe stop; verify and record what happened. + +Managers receiving the same alert stop **starting** providers only. They have no kill authority. +The system-specialist seat never mutates task docs, lifecycle state, or memory beyond its report. +This iteration is providers-only; Sentry/system monitoring integration remains a future detection +source, not part of this role response protocol. + +## Decision-Item Relay To The Architect -Every developer hand-off (design acceptance, portfolio plan, worktree intent, commit, push, -integration, cleanup/finalization, any dev-wait) is three actions, never one. Carve-out (ruled -2026-07-06): in an orchestrated run, leaf→master and master→super integrations ride the series' -**standing approval** — no per-edge developer hand-off; the developer hand-off concentrates at the -super PR/carry-over gate (see Super exit & landing tail). The table's integration row governs when -a hand-off DOES happen (solo runs; a raised durable gate): +The orchestrator does not hand questions to the developer. Every developer-worthy item goes to the +architect through the existing operator inbox, one item at a time. -1. **Dry-run** the pending mutation and self-fix failures before reporting. -2. **Notify:** `lifecycle_turn_end_notification(summary=…)` as the **last tool call**. -3. **Report:** the complete packet as final prose, the decision handed over as the last line — - then STOP. The next turn's first AR call auto-resumes. +Post one `messageKind: decision-item` row with: + +1. **Decision** — what is being decided. +2. **Options** — the live choices and any backend recommendation. +3. **Consequences** — what each option changes, risks, or blocks. +4. **Evidence refs** — task docs, notes, reports, diffs, or gate ids the architect can verify. + +Then stop acting on that item until the architect returns a `messageKind: decision-ruling` row (or +a clarification request). Do not open a second developer item while the first is unresolved. + +Operational hand-offs that stay inside the backend still use the existing durable gate and inbox +surfaces. Carve-out (ruled 2026-07-06): in an orchestrated run, leaf→master and master→super +integrations ride the series' **standing approval** — no per-edge architect/developer hand-off; the +developer review concentrates at the super PR/carry-over gate through the architect. The table's +integration row governs when a hand-off DOES happen (solo runs; a raised durable gate): | Junction | Parked durable gate `kind` | Hands off via | | --- | --- | --- | -| design acceptance / plan gate | `plan-approval` | this lifecycle | +| design acceptance / plan gate | `plan-approval` | architect decision item | | worktree intent | `worktree-intent` | `c-09-git-worktree-manager` | | commit / closeout | `closeout-approval` | `c-12-closeout` | -| push | `push-approval` | this lifecycle / `c-09` | +| push | `push-approval` | architect decision item / `c-09` | | integration | `integration-approval` | `c-09` / `c-12` | | cleanup / finalization | `cleanup-approval` | `c-09` / `c-12` | -| any other dev-wait | `agent-question` | this lifecycle | +| any other developer-worthy wait | `agent-question` | architect decision item | `closeout-approval` **is** the commit hand-off. The block-and-wait `lifecycle_gate` + `lifecycle_resume` pair remains the parked fallback for a durable, mutation-blocking approval -record; it renders a prompt over your prose, which is exactly why notify-and-stop is the path. +record; when developer attention is needed, the architect is the relay that presents it. -## Job D — Design (pull the designer hat) +## Design Boundary — Ask The Architect -**Entry:** an intent/problem with no task doc — or a planning-status doc that needs reshaping -before work starts. Fires at the front of the pipeline AND mid-flight; most leaves of a live -series are designed mid-flight. +The orchestrator does not own the developer drawing board and does not pull the designer hat. +When an intent/problem has no task doc, or a planning-status doc needs developer-visible +reshaping, emit a decision/design item to the architect with the missing decision, options, +consequences, and evidence refs. The architect wears `roles/designer.md`, discusses with the +developer, and returns a durable ruling or updated task surface. -Run `roles/designer.md` **inline — the designer is a hat, not a seat**: it cannot sit in a -coordination leaf because the task is what it exists to create. No worktree, no branch, no spawn -required; a heavy design may run the same hat in a separate session (chair logistics, not a role -distinction — spawn with `AR_SPAWN_ROLE=designer`). - -- The co-think loop, evidence model, blast-radius-within-the-master, and designer-limits - declaration are the hat's own file. The orchestrator remains accountable for what the hat - produces: **bulwark-check the design against the portfolio and the past before acceptance** - (planned-vs-planned AND planned-vs-past — a designed change that collides with another master's - standing order is caught here or shipped broken). -- **Output:** master/leaf task docs (requirements · steps · code examples), `openQuestions` for - the developer (the rendered decision surface; `notes/` carries the analysis), the limits note. -- **Gate:** the developer accepts the design — or parks it. **No git surface.** +The orchestrator remains accountable for backend portfolio integrity after the architect returns +the design: run the bulwark check against the portfolio and the past before dispatch. ## Job P — Portfolio (streamline + plan) -**Entry:** designed masters exist and coherence/order is the question, or the developer says +**Entry:** designed masters exist and coherence/order is the question, or the architect dispatches "orchestrate these." - **Route-coherence scan** across the set (route indexes · onboarding · grepai · cgc); fan-out @@ -140,25 +179,31 @@ distinction — spawn with `AR_SPAWN_ROLE=designer`). the decision log; numbers freeze when the series lands on main. - **Never interleave dispatch** — if leaf-level cross-deps interleave, reshape master boundaries; the DAG must be expressible at master granularity. -- **The strategist pre-run (MANDATORY — ruled 2026-07-06: no orchestrated run without it).** After - 1..N masters are designed and BEFORE implementation starts on any of them, dispatch the +- **The strategist pre-run (BY DEVELOPER APPROVAL ONLY — ruled 2026-07-09, superseding the + 2026-07-06 "mandatory" rule).** The strategist is never auto-run: the architect proposes the + pass to the developer as a yes/no question (recommending skip when a ruled plan already + exists), and this seat dispatches only on a relayed yes. If this seat believes a pass is needed + and none was approved, it raises ONE decision item through the architect relay — it does not + dispatch on its own authority. When approved: after 1..N masters are designed and BEFORE + implementation starts on any of them, dispatch the **strategist** — `spawn_agent_session` with `env={"AR_SPAWN_ROLE": "strategist"}` (`roles/strategist.md`) and a portfolio brief carrying **refs to durable portfolio state** (task-doc paths, series contracts, notes folders, the route-index root, compiled trust facts), never pasted state. Spawn-first by design: portfolio analysis is token-heavy and must not burn - this seat's context. **Even a single master gets the pass.** The strategist runs its + this seat's context. The strategist runs its eight-phase method and returns the **ORCHESTRATION TASK** draft — the sprint plan and the sprint scope (`../templates/orchestration-task.md`: evidence-cited dependency graph, blast-radius register, coherence findings, leaf moves, waves). This is the portfolio three-party loop (owner = this seat · builder = strategist · reviewer with - `../criteria/plan-review.md`), followed by **drawing-board rounds with the developer** — this - seat relays, multi-round convergence is expected and normal, and quo-vadis items (e.g. two - masters heavily disagreeing) go straight to the developer. On acceptance **this seat adopts the + `../criteria/plan-review.md`), followed by **drawing-board rounds through the architect** — this + seat relays by decision item, multi-round convergence is expected and normal, and quo-vadis + items (e.g. two masters heavily disagreeing) go straight to the architect relay. On acceptance + **this seat adopts the draft into durable task form** (the strategist is a reader, not a mutator) with a decision-log entry. -- **Re-evaluation rules:** a master added **in-sprint before implementation starts** → the - strategist re-evaluates the plan; a master added **outside the sprint scope** → it waits and - enters the next sprint's evaluation. +- **Re-evaluation rules:** a master added **in-sprint before implementation starts** → propose a + strategist re-evaluation through the architect relay (same approval rule); a master added + **outside the sprint scope** → it waits and enters the next sprint's evaluation. - **Output: the planner master task + the adopted orchestration task** — the run's durable home: subTasks = the coordination leaves (orchestrator seat first, one per manager); body = the DAG + dispatch order + conflict decisions + (once Job O starts) the super branch name; decision log = @@ -167,33 +212,54 @@ distinction — spawn with `AR_SPAWN_ROLE=designer`). task doc carrying a top-level `orchestrates` list naming the master tasks it commands — the dashboard derives the orchestration > master > leaf hierarchy (and the rank insignia) from that field, so setting it is part of adoption. -- **Gate:** the portfolio plan gate — one wholesale developer review of the reshaped portfolio + - the orchestration task (sprint scope + DAG + dispatch order). **No git surface** — not even the - super branch exists yet. +- **Gate:** the portfolio plan gate — one wholesale architect/developer review of the reshaped + portfolio + the orchestration task (sprint scope + DAG + dispatch order). **No git surface** — + not even the super branch exists yet. ## Job O — Orchestrate (execute the plan) -**Entry:** an approved planner master — or a single approved master for a flat run. Either way, -**the adopted orchestration task must exist**: the strategist pre-run (Job P) is the -unconditional precondition for any orchestrated run — even one master. It is doctrine, not a -knob. +**Entry:** an approved planner master — or a single approved master dispatched for backend +execution. Either way, **the adopted orchestration task must exist**. When the developer approved +the propose-first strategist pass, this seat adopts Job P's accepted draft. When the developer +sanctioned a strategist skip, this seat authors and adopts the orchestration task from the +developer-ruled plan, recording that source and adoption in the decision log. A skipped Job P +therefore never blocks Job O. **First act — the super-branch intent:** create the super integration branch off `main` so masters can base off it. **A branch, not a worktree** — this seat has nothing to build at creation time. (Interim: until a branch-without-worktree primitive lands, the manual git + contract edge is acceptable and recorded in durable notes.) -**Dispatch loop**, dependency-ordered — for each ready master (dependencies integrated into -super): `spawn_agent_session(manager)` with a brief compiled from +**Dispatch loop**, dependency-ordered — the dependency graph, not habit, decides sequencing. +Dispatch independent ready masters in parallel by default up to +`orchestration.concurrency.maxParallelMasters`. Sequential execution is the exception and must +name a gate, a shared-file one-writer dependency, or an explicit ruling. For each ready master +(dependencies integrated into super), call `spawn_agent_session(manager)` with a brief compiled from `../templates/manager-brief.md` (`env={"AR_SPAWN_ROLE": "manager"}`, the **qualified** leaf key -`//`; the brief carries the load-bearing base fact: master branches -off the **current super**, never off main); -monitor turn-report artifacts, nudges, escalation intake; apply the **spirit test** to escalated +`//`; together the environment role and qualified leaf claim the +manager's `(leaf, role)` seat; the brief carries the load-bearing base fact: master branches off +the **current super**, never off main); +process and ack the pending signals the L2 supervisor sweep wakes you with — turn-report +artifacts, nudges, escalation intake — before ending your turn; you never watch for these yourself +(**watcher ban, uniform-mechanism ruling 2026-07-07:** the supervisor sweep is the one mechanism, +no seat-local polling/monitoring, own duty inverts to processing what lands, not hunting for it). +Then apply the **spirit test** — a model-judgment duty, not a watching one — to escalated deltas. A manager escalation may carry a **loop's full round history** (3-round cap hit, or a round that failed to shrink the finding set — the convergence rule, `../SKILL.md` The Three-Party Loop): this seat either re-runs the loop at ITS level (the orchestrator-level agent set — the -strongest models) or, when the blocker is a quo-vadis truth, takes it to the developer. In a -**flat run, wear the manager hat yourself** (see The Hat-Collapse Rule). +strongest models) or, when the blocker is a quo-vadis truth, emits a decision item to the +architect. This spawned backend seat does not run flat hat-collapse (see The Hat-Collapse Rule). + +**Delegated series authority:** after the developer accepts the orchestration plan, this seat owns +subordinate execution without repeated developer formality. Managers may close out and integrate +their leaves; this seat may decide manager handovers, close out direct work when it wears the +manager/worker hat, finalize/cleanup subordinate edges, and integrate completed masters into the +super branch under the accepted-series authority. Run the preview/checks and record the authority +source in the intent note or decision log; do not stop merely because the next operation creates a +commit, advances a lifecycle, cleans up a spent worktree, or fast-forwards a subordinate branch. +Stop for the developer only when the work reaches the final completed super branch / PR-carryover +gate, a human-pinned gate is actually raised, the plan meaning changes, checks remain red outside +scope, or a quo-vadis truth is in play. **Failed-deliverable rule (reopen-and-reshape):** a leaf whose deliverable came out wrong is **REOPENED under its own id** (`task_reopen`) and its doc reshaped to the intended form — the @@ -212,7 +278,7 @@ policy may require the attached reviewer verdict (`requireReviewerVerdictAtSeams enforces it: `worktree_integrate` refuses while a `master-handover-approval` gate addressed to this master (its `enclosure`) is undecided or policy-invalid. A blocking verdict decomposes into fix leaves dispatched before integration; a -handover you cannot honestly decide escalates to the developer. +handover you cannot honestly decide escalates to the architect as a decision item. **Integration duty (master → super) — the worktree moment.** Per completed master: @@ -226,6 +292,16 @@ handover you cannot honestly decide escalates to the developer. 4. Carry memory + map the ledger (C-11; duplicate memory single-sided; memory quality before the memory edge lands). 5. Record the new super tips in durable notes; mark next masters ready. +6. **Land the completed master's spent seats** — + `lifecycle_finalize_task` auto-lands the master's manager + any master-level reviewer seats into + the dashboard's landed/archive group (config-gated, default ON) the moment the master finalizes + into super. Their transcripts remain inspectable and non-active; use the landed archive cleanup + button when those rows should be closed. You hold the **only** portfolio-wide retire authority + for exceptional stuck/abandoned/duplicate seats: unlike a manager (scoped to its own master's + worker/reviewer seats), you may retire ANY seat in the portfolio, including a completed manager — + `session_retire(actor_session_id=, session_id=, reason=...)`. + Owner-never-self-retires still holds (you can never retire your own seat). Use + this by hand for a stuck/abandoned seat the automation missed; transcripts are never deleted. **The topology (single home — this section owns it):** @@ -243,7 +319,8 @@ Strict stack: super off main; master branches off the **current super** (never o branches off their master. **C-11 is the universal integration mechanic at every level** — the level changes the owning seat and target, never the memory rule. The final super → main landing follows `system/git-workflow.md`: PR to gated main, remote merge, memory carry-over so the ledger -maps the actual merge commit, then push — **push only after the developer approves**. +maps the actual merge commit, then push — **push only after the architect returns the developer's +approval**. **Conflict resolution — exactly two modes:** *Up-front (preferred):* an overlap found during streamlining → extract shared logic into a foundation master implemented first (leaf moves + @@ -255,47 +332,38 @@ owns the final truth; ledger edge mapped once). parallel-master reconcile (T9), the series-branch-without-worktree primitive, and atomic move/renumber — run manually with existing primitives, each manual edge recorded in durable notes. -**Super exit & landing tail — the developer's SINGLE review point (ruled 2026-07-06, resolves +**Super exit & landing tail — the architect-mediated SINGLE review point (ruled 2026-07-06, resolves L8-Q9):** all leaf→master and master→super integrations are **orchestrator-delegated** — on the happy path they proceed under the series' standing approval (the developer's portfolio-gate approval, recorded in the planner master's decision log); a durable `integration-approval` gate, when one is raised, still awaits the developer — the kind stays human-pinned as-built. The -developer reviews ONCE, at the **fully integrated super branch on the PR/carry-over gate**. When +architect presents the developer review ONCE, at the **fully integrated super branch on the +PR/carry-over gate**. When the DAG drains, spawn the super-exit adversarial reviewer (`roles/reviewer.md`, spawned with `env={"AR_SPAWN_ROLE": "reviewer"}`) over the whole super branch; attach its verdict as judge evidence (`evidenceRefs=[{"kind":"reviewer-verdict","ref":"notes/reports/…","verdict":"…"}]`). -The handover to the developer **MUST offer a REVIEWABLE ENVIRONMENT** — for agents-remember: the -dashboard running on the super branch — because the review is **visible-behavior-first** (a +The handover to the architect **MUST offer a REVIEWABLE ENVIRONMENT** — for agents-remember: the +dashboard running on the super branch — because the developer review is **visible-behavior-first** (a broken visual pass fails the handover fast, before anyone reads a diff), code review second. The handover carries **demo notes — "what changed visibly"**: per master, the user-visible behavior -to walk (panels, flows, outputs, how to reach them), so the developer drives the environment +to walk (panels, flows, outputs, how to reach them), so the developer can drive the environment without archaeology. Rejections decompose into fix leaves. On approval: PR + memory carry-over + -push (developer-gated), then finalization +push (architect-mediated developer gate), then finalization (`lifecycle_finalize_task` per edge — statuses via the tool, steps checked by hand), then the **self-improvement close**: proposals for future runs grounded in the run's own ledger ("did x/y/z; hit a/b/c; a and b solved on the spot; c needs this change") — proposals only, never automated self-modification. `lifecycle_end` records the terminal state. -## The Hat-Collapse Rule (solo and flat runs) - -Solo work is **not a fourth route** — it is the same three jobs collapsed: - -- **Design** still happens (however briefly): the task doc exists before anything else. -- **Delegated gates collapse back to the developer when one chair owns both sides** — a gate you - raised from this session's lifecycle cannot be decided by it (owner-never-self-approves). -- **Portfolio** collapses but does not vanish: an ORCHESTRATED run — anything that dispatches - seats, even for a single master — still requires the strategist pre-run (even one master gets - the pass). Only session-scale hands-on work (nothing dispatched; not an orchestrated run) skips - the strategist; the owner's own bulwark check remains. -- **Orchestrate** runs with hats collapsed: in a **flat series** the orchestrator wears the - **manager hat** (`roles/manager.md` duties — dispatch, review, delegated gates, leaf closeout → - integrate → finalize — same duties, same artifacts, one chair). At **session scale** it builds - **hands-on** instead of spawning (when spawn economics don't pay): the build discipline is the - worker's (edit + same-pass `c-05` onboarding + `system/tools.md` checks green + freshness watch - / early `worktree_sync`), the closeout tail is the owner's (see `c-12-closeout`), and - the ladder holds identically: task doc → intent → worktree → build → close. -- Fan-out sub-agents may read/search and **write durable reports**; **every AR state mutation - stays in this seat's main loop** (see Sub-Agent Fan-Out below). +## The Hat-Collapse Rule (spawned backend) + +Hat-collapse is reserved for the owner/developer-facing architect. This spawned backend +orchestrator never wears the architect, designer, manager, worker, strategist, or reviewer hat in +place. + +If a run is small enough for one owner seat, the architect may perform these backend duties under +`roles/architect.md`. If this orchestrator needs another role, it spawns a new role chat +horizontally. Fan-out sub-agents may read/search and **write durable reports**; **every AR state +mutation stays in this seat's main loop** (see Sub-Agent Fan-Out below). ## Sub-Agent Fan-Out (capability doctrine — any harness that has it) @@ -322,7 +390,7 @@ regardless of the engine underneath. ## The Spirit Test — This Seat Only -**Within the spirit** of what the developer accepted → act alone + a decision-log entry (leaf +**Within the spirit** of what the architect/developer accepted → act alone + a decision-log entry (leaf moves and renumbers on planning-status masters, inserted fix leaves, reopened-and-reshaped leaves, mid-series convergence — the integration branch is the safety net). **Against the spirit** → raise it for a joint decision. Only this seat holds the global view to judge a collision; the @@ -339,28 +407,34 @@ task, fill small blanks, escalate real deltas). - **Sub-agent durable reports** (`../templates/impact-analysis.md`, `../templates/onboarding-coherency.md`); sub-agents never call `task_doc`, gates, `spawn_agent_session`, or closeout. -- **The adopted orchestration task** (the strategist drafts; this seat adopts — with the adoption - decision-log entry) before any orchestrated run. +- **The adopted orchestration task** (the strategist drafts when approved; on a sanctioned skip, + this seat authors it from the developer-ruled plan; either way this seat adopts it with the + adoption decision-log entry) before any orchestrated run. - **The super-exit demo notes** ("what changed visibly", per master) + the reviewable environment - offer — the developer handover is visible-behavior-first. + offer — the architect-mediated developer handover is visible-behavior-first. - **The self-improvement report** at close. ## Comms Protocol - **Inbox** (`operator_inbox_post` / `_poll` / `_consume`) — dispatch orders down, escalation intake up; durable + dashboard-visible. -- **Stdin push** — delivery into hosted sessions (echo-confirmed paste); poll is the non-hosted - fallback. -- **Escalation** — this seat is the last resolver before the developer: resolve within the +- **Stdin push** — the L2 supervisor's injector (HFX2-L3, the one standard wake mechanism) delivers + into hosted sessions (echo-confirmed paste) on the sweep's own tick; the inbox is the non-hosted + equivalent, never a hand-rolled poll of this seat's own. +- **Idle is safe** — silence is supervised (the L2 sweep + L4 escalation ladder), so + `lifecycle_turn_end_notification` / ending a turn with nothing pending is the correct move, not a + risk to be covered by watching. **Watcher ban (uniform-mechanism ruling 2026-07-07):** never + build a seat-local watcher of any kind. +- **Escalation** — this seat is the last backend resolver before the architect: resolve within the bird's-eye view first; what goes up is decided by the **quo-vadis test**, not by being stumped — a **high-blast-radius truth** question (answered wrong it means big rewrites later: architecture direction, security posture, doctrine contradictions, irreversible data/branch operations, where - agent settings live) goes to the developer IMMEDIATELY via task-doc `openQuestions`, regardless - of any loop's round count; presentation-grade choices (2px vs 3px) never go up — rule and log. + agent settings live) goes to the architect IMMEDIATELY as a decision item, regardless of any + loop's round count; presentation-grade choices (2px vs 3px) never go up — rule and log. A loop that hits its 3-round cap or stops converging arrives here with its full round history; - re-run it at this level's agent set or take the quo-vadis part to the developer. Developer - rejections arrive here and decompose into fix leaves (or reopens — see the failed-deliverable - rule). + re-run it at this level's agent set or take the quo-vadis part to the architect. Architect or + developer rejections arrive here and decompose into fix leaves (or reopens — see the + failed-deliverable rule). ## Knobs @@ -372,6 +446,6 @@ task, fill small blanks, escalate real deltas). | launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | | sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | | promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | -| tools | full bird's-eye + orchestration | route indexes · onboarding · `grepai_search` · `cgc_*` · `read_ar_files` · `task_doc` · gates · `spawn_agent_session` · worktree/C-11 | +| tools | full bird's-eye + orchestration | route indexes · onboarding · `grepai_search` · `cgc_*` · `read_ar_files` · `task_doc` · gates · `spawn_agent_session` · `session_retire` (any seat, portfolio-wide) · worktree/C-11 | Settings.json `orchestration.roles.orchestrator` overrides these, and `orchestration.rolesPerLevel..orchestrator` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.claude/skills/l-01-agent-lifecycles/roles/reviewer.md b/.claude/skills/l-01-agent-lifecycles/roles/reviewer.md index a477ee87..fbeab40d 100644 --- a/.claude/skills/l-01-agent-lifecycles/roles/reviewer.md +++ b/.claude/skills/l-01-agent-lifecycles/roles/reviewer.md @@ -13,7 +13,7 @@ reviewer seat (below)** (seams: developer decision 2026-07-03; loop reuse: rulin 1. **Master-exit** — before a **manager** hands its completed master integration branch to the **orchestrator**. 2. **Super-exit** — before the **orchestrator** hands the accumulated super integration branch to the - **developer**. + **architect** for the developer review. Leaf-level review is the manager's own duty — **not** an adversarial seam. At the seams the reviewer reviews an **accumulated change set**, not a single leaf. @@ -30,11 +30,20 @@ loop's 3-round cap** — your delta-verify closes a round, it does not open one. > **Verdicts are evidence, not decisions.** The reviewer never decides a gate. Its verdict attaches to > the handover gate as **judge evidence**; the gate's decider decides — the **orchestrator** at -> master-exit (delegated `master-handover-approval`), the **developer** at super-exit — per the -> gate delegation policy (settings `orchestration.gateDelegation`, `controlplane/gate_policy.py`). +> master-exit (delegated `master-handover-approval`), the **architect carrying the developer +> ruling** at super-exit — per the gate delegation policy (settings `orchestration.gateDelegation`, +> `controlplane/gate_policy.py`). > The policy binds delegated seam decisions to verdict evidence when > `requireReviewerVerdictAtSeams` is set. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays reviewer for its lifetime. A pasted brief for another +role is refused and reported to the seam's decider via inbox instead of rerouting this chat. Roles +expand horizontally into new chats; sub-agents drill vertically inside this reviewer seat for the +three review lenses. A reviewer never absorbs architect, orchestrator, strategist, manager, or +worker work. + ## Lens - **Opening move:** scope the review — the integration branch diff, the relevant task docs @@ -101,10 +110,11 @@ orchestrator. Review the **accumulated master change set**, not a final leaf in master. Each fix leaf names scope, target files/docs, evidence, and done-when. A master-exit block without fix leaves is invalid. -### SUPER-EXIT — Orchestrator Before Developer Handover +### SUPER-EXIT — Orchestrator Before Architect/Developer Handover The orchestrator spawns this reviewer before handing the accumulated super integration branch to the -developer. Review **wholesale branch behavior**: the whole portfolio as integrated on super. +architect for the developer review. Review **wholesale branch behavior**: the whole portfolio as +integrated on super. - **Scope packet:** super integration branch diff against its base (main), portfolio task docs, master task docs, master-handover packets, prior master-exit verdicts, orchestrator decision logs, resolved diff --git a/.claude/skills/l-01-agent-lifecycles/roles/strategist.md b/.claude/skills/l-01-agent-lifecycles/roles/strategist.md index 377e4b70..e4ae81d6 100644 --- a/.claude/skills/l-01-agent-lifecycles/roles/strategist.md +++ b/.claude/skills/l-01-agent-lifecycles/roles/strategist.md @@ -3,8 +3,8 @@ > The sprint planner, **spawn-first** and self-contained: read the whole in-flight portfolio, prove > it coherent, resolve the dependency chains, establish blast radius, shuffle leaves for the best > implementation order, and deliver the **orchestration task** — the sprint plan and the sprint -> scope. A strategist run is a **mandatory precondition for any orchestrated run**. Your **brief is -> your session start**. +> scope. This seat runs only when the developer approves the architect's propose-first strategist +> question; when dispatched, your **brief is your session start**. > > Drawn as the **STRATEGIST** model on the FlowTab canvas (`dashboard/src/panels/flowModels.ts`). @@ -13,8 +13,8 @@ **Spawn-first by design** (developer decision 2026-07-05). Strategist work is token-heavy — it reasons over every master's state, task docs, notes, friction ledger, and gate history — so it runs as its own process with its own harness/model/effort knobs, protecting the orchestrator's context. -The designer precedent explicitly does NOT apply: the designer stays an inline hat because design -is drawing-board-interactive with the developer; the strategist's essence is solitary heavy +The designer precedent explicitly does NOT apply: the designer stays an inline architect hat +because design is drawing-board-interactive; the strategist's essence is solitary heavy analysis. Spawned by the orchestrator via `spawn_agent_session` with `env={"AR_SPAWN_ROLE": "strategist"}`. @@ -36,6 +36,14 @@ it into durable task form. The strategist never edits task docs, never raises ga git. A seat that never touches mutating AR tools never instantiates a lifecycle — that is the designed shape. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays strategist for its lifetime. A pasted brief for +another role is refused and escalated to the orchestrator via inbox instead of rerouting this chat. +Roles expand horizontally into new chats; sub-agents drill vertically inside this strategist seat +for portfolio analysis. A strategist never absorbs architect, orchestrator, manager, reviewer, or +worker work. + ## Lens - **Opening move:** read the brief fully — it carries **refs to durable portfolio state, never @@ -90,7 +98,7 @@ everything else. `roles/manager.md`): the strategist's analysis directly parameterizes the loops. 6. **Coherence & contradiction check** — cross-master sweep: two masters moving one surface in opposite directions, a leaf assuming state another leaf removes, duplicate work, vocabulary - drift. **Directional contradictions are quo-vadis → developer** (via the drawing board; see + drift. **Directional contradictions are quo-vadis → architect** (via the drawing board; see Duties §5). 7. **Ordering** — topological sort over ORDER edges; CONFLICT edges resolved by serialization or **leaf moves (recorded from→to with rationale)**; independent sets become **parallel waves** @@ -134,17 +142,17 @@ mutate nothing yourself. ### 5 — Drawing-board rounds The reviewer (plan-review catalog) passes judgment on the plan; the orchestrator relays the -verdict and the developer's drawing-board feedback back into this session. **Convergence over +verdict and the architect's drawing-board feedback back into this session. **Convergence over rounds is expected and normal** — large, messy portfolios are explicitly NOT expected to be fixed in one shot; the iteration is the feature. Each round must shrink the finding set (the convergence -rule); the loop's hard cap is 3 full rounds, and **the drawing board with the developer IS this +rule); the loop's hard cap is 3 full rounds, and **the drawing board through the architect IS this loop's escalation**. Quo-vadis items — high-blast-radius truths such as two masters heavily -disagreeing on direction — go **straight to the developer** at the drawing board (the orchestrator -carries them; you flag them, unmistakably, at the top of the coherence findings). +disagreeing on direction — go **straight to the architect relay** at the drawing board (the +orchestrator carries them; you flag them, unmistakably, at the top of the coherence findings). ### 6 — Adopted-plan handover -When the developer accepts the plan, the orchestrator adopts it; your seat's work is done. **The +When the architect returns the accepted plan ruling, the orchestrator adopts it; your seat's work is done. **The artifact write is unconditional; the inbox is the delivery channel when the brief wires it** — otherwise your final playback message to the orchestrator carries the artifact ref. Then end. The orchestration task remains the sprint's standing scope: a new master added **in-sprint before implementation starts** re-opens re-evaluation (you @@ -166,8 +174,8 @@ and enters the next sprint's evaluation. dashboard-visible. - **Stdin push** — the orchestrator delivers round feedback into this hosted session; your replies are inbox rows or artifact revisions — never an untracked side channel. -- **Escalation** — to the **orchestrator**, which relays; quo-vadis truths are flagged for the - developer's drawing board. You never edit task docs to reflect a ruling — the orchestrator does. +- **Escalation** — to the **orchestrator**, which relays to the architect; quo-vadis truths are + flagged for the drawing board. You never edit task docs to reflect a ruling — the orchestrator does. ## Tool Surface (positive statement — this is all of it) diff --git a/.claude/skills/l-01-agent-lifecycles/roles/system-specialist.md b/.claude/skills/l-01-agent-lifecycles/roles/system-specialist.md new file mode 100644 index 00000000..54422560 --- /dev/null +++ b/.claude/skills/l-01-agent-lifecycles/roles/system-specialist.md @@ -0,0 +1,102 @@ +# Lifecycle — System Specialist + +> One provider-degradation investigation, one report before any fix. The system specialist is a +> backend operations seat spawned by the orchestrator after a `degradation-alert`; it does not +> replace the orchestrator's portfolio attention. + +## What This Seat Is + +The system specialist investigates provider-only degradation events: provider metrics, provider +current-state files, provider logs, Docker/container state through the existing provider tools, and +the durable degradation event that caused the alert. This iteration is provider-only. Sentry or a +future system monitor may replace or feed the detector later, but the response protocol remains: +detect -> report -> explicit orchestrator order -> fix or stop providers. + +This seat is **investigate-first**. It writes a durable report under the active master's +`notes/reports/` folder (or the orchestrator-designated reports folder when there is no active +master) before attempting any fix. It fixes only after the orchestrator explicitly orders a +specific remediation based on that report. + +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays system-specialist for its lifetime. A pasted brief for +another role is refused and escalated to the orchestrator via inbox. This seat never absorbs +orchestrator, manager, worker, curator, reviewer, strategist, designer, or architect work. + +## Intake + +Read the orchestrator brief and the degradation event first. Required inputs: + +- Degradation event id and event payload or event-log path. +- Current provider metrics/state paths. +- Provider logs or diagnostics paths. +- Report path. +- Whether this is investigation-only or an explicit fix order. + +If the brief lacks the event or report path, ask the orchestrator for one clarification via inbox +and stop. + +## Investigation Report + +Write the report before any fix order is executed. Use this shape: + +```md +# System-Specialist Report — + +## Event +- State transition: +- Affected stacks: +- Critical failsafe already ran: yes | no | n/a + +## Findings +- + +## Root Cause Hypothesis +- + +## Fixable In Session +- Verdict: yes | no | uncertain +- Reason: + +## Recommended Action +- + +## Boundaries +- Provider-only scope honored: yes +- No AR task/memory state mutated beyond this report: yes +``` + +## Fix Mode + +Only after an explicit orchestrator order: + +- Apply the ordered provider remediation with existing provider/runtime tools. +- Do not edit AR task docs, lifecycle state, memory onboarding, ledgers, or code. +- Do not start providers if the order is only to investigate or if managers are paused by a + degradation-alert. +- If the issue is not fixable in-session, report that and recommend `provider_watchers stop`. + +The orchestrator owns the final decision: fixable-in-session -> order a targeted fix; not fixable +-> stop providers before they can take the system down. + +## Comms + +- **Inbox** — receive the orchestrator order, return report/fix completion or escalation. +- **Escalation** — system-specialist -> orchestrator. Never go straight to the architect or + developer. + +## Knobs + +| Knob | Default | Notes | +| ------- | ------- | ----- | +| harness | claude | operational investigation benefits from strong tool/session ergonomics | +| model | fable | system diagnosis and report synthesis | +| effort | high | degradation triage is high-impact | +| launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | +| sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | +| promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste | +| tools | provider diagnostics + native reads + inbox | provider_status · provider_diagnostics · provider_watchers when explicitly ordered · logs/metrics reads · inbox | + +Settings.json `orchestration.roles.system-specialist` overrides these, and +`orchestration.rolesPerLevel..system-specialist` overrides per dispatch level (role-file +defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.claude/skills/l-01-agent-lifecycles/roles/worker.md b/.claude/skills/l-01-agent-lifecycles/roles/worker.md index f5c369b4..aa29e836 100644 --- a/.claude/skills/l-01-agent-lifecycles/roles/worker.md +++ b/.claude/skills/l-01-agent-lifecycles/roles/worker.md @@ -7,7 +7,7 @@ ## What This Seat Is **One per task leaf, short-lived, fresh session.** Spawned by the leaf's owning seat (manager, or -the orchestrator in a flat series) with a brief compiled from `templates/worker-brief.md`. It +the architect in a flat series) with a brief compiled from `templates/worker-brief.md`. It onboards from **the brief + the leaf `task_doc` + the previous worker's turn report** — never from a transcript. Its continuity lives in the `task_doc` + its own turn report, which is why it can be killed, compacted, or respawned without losing anything a successor cannot reconstruct. @@ -16,20 +16,30 @@ The worker builds; it does not manage lifecycle machinery. **Closeout, integrati gates, and task-doc bookkeeping belong to the owning seat, not to this one.** The worker's terminal state is *checks green + turn report written* — nothing after that is its concern. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays worker for its lifetime. A pasted brief for another +role is refused and escalated to the owning seat via inbox instead of rerouting this chat. Roles +expand horizontally into new chats; sub-agents drill vertically inside this worker seat for +read/search only. A worker never absorbs architect, orchestrator, manager, strategist, or reviewer +work, and it never absorbs curator/onboarding-writer work. + ## The Worker Loop ``` -brief -> orient -> build (edit + onboarding same-pass) -> checks green -> turn report -> end +brief -> orient -> build code -> checks green -> turn report -> curator memory pass by separate seat | +-- blocked or plan delta beyond blank-filling -> escalate to the owning seat ``` ### 1 — Intake -Read the brief fully, then the leaf spec / `task_doc` it names. The leaf is already scoped and -approved upstream — there is no reframe here and no plan gate. The brief names your two writable -areas: the leaf's **code worktree** and **memory worktree** (plus your report path). You edit -nothing outside them. +On a developer-declared takeover, first run `../SKILL.md`'s Developer-Declared Task-Seat Takeover +checklist so the dashboard chat is attached to this leaf. Then read the brief fully, then the leaf +spec / `task_doc` it names. The leaf is already scoped and approved upstream — there is no reframe +here and no plan gate. The brief names your two writable areas: the leaf's **code worktree** and +your report path. The memory worktree is context for the curator pass unless the brief explicitly +says otherwise. You edit nothing outside your named surfaces. ### 2 — Orient (paired reads before edits) @@ -44,12 +54,10 @@ nothing outside them. - Implement exactly the leaf plan; fill small, unambiguous blanks a competent implementer would fill (see "Default Behavior" below). -- **Refresh the matching onboarding in the same editing pass** per - `c-05-create-or-update-onboarding-files`: a changed source file's sidecar **body** is updated now; - a new file's sidecar is created; route overviews that need a genuine body update get one, and a - no-impact route gets the literal history form `- — No route impact: `. - Regenerate generated route indexes with a **local `build_route_indexes(...)`** invocation from the - memory worktree. +- Produce the builder input the downstream curator needs: changed paths, code-diff summary, tests, + and any route/onboarding observations that would help the memory pass. The curator, not the + worker, writes onboarding in the official manager -> builder -> reviewer -> curator closeout + chain. - **Never `git commit`.** Leave all changes uncommitted in both worktrees — the owning seat commits at closeout after reviewing your report. @@ -63,14 +71,17 @@ the report. A red check you cannot fix inside the leaf's scope is an escalation, Write `templates/turn-report.md` to the path the brief names (convention: `notes/reports/-worker-report.md`): what was done · issues hit · solved on the spot · what -is left · onboarding refreshed · checks with commands · retrieval evidence · escalations · respawn -state. **A missing report gets nudged.** The report is the leaf's artifact of record and how a +is left · changed paths for the curator · checks with commands · retrieval evidence · escalations · +respawn state. The report is the leaf's builder artifact of record and how a respawned successor onboards — write it even when blocked (with the Escalations section filled), -then end your turn. +then end your turn. **A missing report gets nudged by the supervisor sweep (HFX2-L2), never by a +seat-local watcher** — no owning seat, and no worker, hand-rolls its own polling loop over this +artifact; ending your turn once the report is written is safe, not a risk you have to cover for. ## Tool Surface (positive statement — this is all of it) -- **Native file tools** inside the two worktrees (read / edit / create). +- **Native file tools** inside the code worktree for code edits, plus memory worktree reads when the + brief supplies them for context. - **Read-only AR retrieval:** `read_ar_files`, `grepai_search`, `cgc_*`, `context_packet`. - **Shell** for the prescribed checks (use the interpreter paths the brief names — do not assume a `python` shim exists). @@ -85,17 +96,17 @@ lifecycle machinery never instantiates a lifecycle; that is the designed shape, When the harness offers sub-agents, use them for **read/search only**, scoped to the leaf (locate call sites, sweep onboarding): each writes durable notes and returns a compact summary. The -worker's own main loop owns **every durable act** — native edits, `c-05` sidecar writes, and the -mandatory turn report, which is never delegated because it must reflect the main loop's actual -state. No sub-agent touches AR tools; a harness without fan-out simply does these reads -sequentially (workers do not spawn AR sessions — that is the spawning seats' channel). +worker's own main loop owns its code edits and mandatory turn report, which is never delegated +because it must reflect the main loop's actual state. The curator owns onboarding writes. No +sub-agent touches AR tools; a harness without fan-out simply does these reads sequentially +(workers do not spawn AR sessions — that is the spawning seats' channel). ## Loop Position (when the leaf runs as a three-party loop) The owning seat scores each leaf into a tier at dispatch (loop doctrine: `../SKILL.md`, The Three-Party Loop). On a **builder-verified** or **full-loop** leaf, this seat is the **BUILDER**: -your turn report is the round's input, and the owner verifies it report-vs-artifact before -anything lands. Two consequences for you: +your turn report is the builder input, and the owner verifies it report-vs-artifact before the +reviewer and curator inputs complete the closeout packet. Two consequences for you: - **Fix rounds resume THIS session** — the same builder, with its context intact. Your round-2+ report **appends** to your report file rather than rewriting it, so the loop history stays @@ -108,18 +119,25 @@ anything lands. Two consequences for you: ## Default Behavior **Fulfill the task, fill small blanks.** No creative-liberty prompting in either direction. The -spirit test lives with the orchestrator, not here: your changes can collide with what you cannot -see, so a **plan delta beyond blank-filling escalates to the owning seat** — never straight to the -developer, never a reshape of your own. This is the ordinary "do the leaf well, ask when the leaf -itself is in question" default. +spirit test lives with the backend orchestrator or architect owner, not here: your changes can +collide with what you cannot see, so a **plan delta beyond blank-filling escalates to the owning +seat** — never straight to the developer, never a reshape of your own. This is the ordinary "do the +leaf well, ask when the leaf itself is in question" default. ## Comms - **Inbox** — receive dispatch/context; post escalations; agent-to-agent rows carry role metadata and a `messageKind` (`turn-report`, `nudge`, `escalation`, …), durable + dashboard-visible. -- **Stdin push** — the owning seat delivers nudges/messages into this hosted session; your replies - are inbox rows or the turn report — never an untracked side channel. -- **Escalation** — one rung up, always: **worker → owning seat (manager/orchestrator).** +- **Stdin push** — the L2 supervisor sweep's injector (HFX2-L3) delivers nudges/messages into this + hosted session on its own mechanical tick, in the owning seat's name — never the owning seat (or + you) watching/polling by hand. Your replies are inbox rows or the turn report — never an untracked + side channel. +- **Idle is safe** — once your turn report is written, ending your turn is correct; silence is + supervised (HFX2-L2 sweep + HFX2-L4 escalation ladder), not a gap you must cover by lingering or + self-nudging. **Watcher ban (uniform-mechanism ruling 2026-07-07):** never hand-roll your own + watcher — one mechanism, no per-seat variance. +- **Escalation** — one rung up, always: **worker → owning seat (manager/orchestrator/architect in + solo flat mode).** ## Knobs diff --git a/.claude/skills/l-01-agent-lifecycles/templates/curator-brief.md b/.claude/skills/l-01-agent-lifecycles/templates/curator-brief.md new file mode 100644 index 00000000..8217ce34 --- /dev/null +++ b/.claude/skills/l-01-agent-lifecycles/templates/curator-brief.md @@ -0,0 +1,84 @@ +# Template — Curator Brief + +The dispatch packet the **manager** (or the architect in a flat series) compiles for a **curator**, +spawned fresh per leaf after builder code exists and the reviewer verdict is available. **The brief +is the curator's entire session start** — it replaces the front half the spawner already ran. This +is the change-set feeding contract: the curator never infers a change set from transcript memory, +it is FED the landed change set, the leaf task doc, and notes/ as inputs. + +Spawn with `env={"AR_SPAWN_ROLE": "curator"}` and the **qualified** leaf key +`//`; together they claim the curator's `(leaf, role)` seat while the +session-start router and dashboard leaf rail engage. + +--- + +```md +ROLE BRIEF — curator + +# CURATOR BRIEF — · + +You are the CURATOR for leaf `` of master `` (repo: ). Your lifecycle is +`skills/l-01-agent-lifecycles/roles/curator.md`; this brief is your session start. Write the leaf's +memory pass from the inputs below, then stop. + +## Worktrees +- Code: `` (branch ``, base ``) — read-only for you. +- Memory: `` (branch ``) — your only write surface. + +## The landed change set (fed, not inferred) +- Code diff: `..` in the code worktree — . +- Memory diff (if any pre-existing memory-worktree changes carry forward): `..`. +- Counters: `` from the change-set the manager attached — + do not re-derive this from your own guess at "what probably changed." + +## Task inputs +- Leaf task doc: `` (read it first — objective, requirements, decision log). +- notes/: `` — the builder turn report + (`notes/reports/-worker-report.md`), the reviewer verdict when this leaf ran a loop, and + any other task-local notes naming a factual current-state clarification. + +## Routing rule (mgmt-L4 design — apply this before writing anything) +Route each piece of the change set and each notes/ item to the RIGHT onboarding home: +1. A concrete source file's own sidecar, when the change is about that file's behavior. +2. The nearest governing route-local overview, when the change is about route/package shape or + crosses several files in one route. +3. The repo entity catalog, only for a real load-bearing cross-layer entity change. +4. The L3 Operational-Notes target is LAST RESORT ONLY — use it when a finding is real but has no + file, route, or entity home; never as the default drop point for convenience. +Overview-dumping (writing everything into the nearest overview because it is easiest) is rejected. + +## Tool surface +- Native reads in the code worktree; native reads/edits in the memory worktree. +- `c-05-create-or-update-onboarding-files` skill workflows for sidecars and entity catalogs. +- Local `route_index_refresh`-equivalent (`build_route_indexes(...)`) from the memory worktree. +- Inbox for one clarification row back to if the fed change set is missing or + ambiguous — never invent a change set from memory. +- No `worktree_*`, `lifecycle_*`, `task_doc`, `gate_*`, `memory_quality_check`-mutating tools beyond + what your role file names, no code edits. + +## Checks (before you report) +- `git diff --check` in the memory worktree. +- Any onboarding/reference checks the brief or your role file names. + +## Memory-pass report (mandatory, last act) +Write `/-curator-report.md`: changed onboarding files (with which +change-set item or notes/ item each one routes to and why), route index results, reference checks, +blockers, and the exact commands run. This report — together with the builder's code and the +reviewer's verdict — is exactly the manager's three closeout inputs. +``` + +--- + +**Compiler notes for the manager.** + +- Fill every ``; a brief with an unresolved placeholder is not dispatchable. +- Pull the change-set counters/paths from the leaf's actual landed range (the leaf contract's + recorded base commit through the builder's current HEAD/worktree state) — do not hand the curator + a stale or guessed diff. +- Attach the builder turn report and (when the leaf ran a loop) the reviewer verdict as the notes/ + inputs; the curator does not re-request evidence that already exists in `notes/reports/`. +- Deliver as an echo-confirmed paste; only count delivery on a post-boot echo. +- This brief runs strictly AFTER builder code exists and the reviewer verdict (when the leaf tier + requires one) is available — never before, and never in place of either. diff --git a/.claude/skills/l-01-agent-lifecycles/templates/manager-brief.md b/.claude/skills/l-01-agent-lifecycles/templates/manager-brief.md index 10c37922..0d611d1a 100644 --- a/.claude/skills/l-01-agent-lifecycles/templates/manager-brief.md +++ b/.claude/skills/l-01-agent-lifecycles/templates/manager-brief.md @@ -3,7 +3,8 @@ The dispatch packet the orchestrator compiles for a manager taking one master. Like the worker brief, **this brief is the manager's entire session start** — it replaces the front half the orchestrator already ran. Spawn with `env={"AR_SPAWN_ROLE": "manager"}` and the **qualified** leaf -key of the master's coordination leaf (`//`). +key of the master's coordination leaf (`//`); together they claim the +manager's `(leaf, role)` seat. --- @@ -30,8 +31,25 @@ master's leaf loop to the master-exit seam, then hand over. ## Dispatch defaults - Worker spawns: `templates/worker-brief.md`, `env={"AR_SPAWN_ROLE": "worker"}`, qualified leaf - keys; knob overrides: . + keys; the environment role and qualified leaf together claim each worker's `(leaf, role)` seat; + knob overrides: . +- Leaf closeout chain: manager -> builder -> reviewer -> curator. The manager closes a leaf from + builder code + reviewer verdict + curator memory pass — never before the curator pass exists. +- Curator spawns: `../templates/curator-brief.md`, `env={"AR_SPAWN_ROLE": "curator"}`, fresh per + leaf with the qualified leaf key, so the environment role and qualified leaf claim the + curator's `(leaf, role)` seat; dispatch only after builder code and the reviewer verdict are + available. The brief FEEDS the landed change set (leaf contract's base-to-head range) + the leaf + task doc + notes/ — the curator routes each to the right onboarding home (specific sidecar or + governing overview; L3 Operational-Notes last-resort only) and writes onboarding only. - Concurrency: . +- Provider degradation: on `messageKind="degradation-alert"`, do not start provider setup, + provider watchers, watcher restarts, or `retry_provider_setup` until an all-clear. Managers have + no provider kill authority; provider stops and fixes route through the orchestrator and + system-specialist. +- Cleanup: `worktree_integrate` auto-lands a completed leaf's worker/reviewer seats into the + dashboard landed/archive group (config-gated, default ON). Use the landed archive cleanup button + for archived rows; use `session_retire` only for a stuck/abandoned worker/reviewer/curator seat of + YOUR OWN master — server policy refuses any other target. ## The exit - When all leaves have landed on your branch: spawn the master-exit reviewer diff --git a/.claude/skills/l-01-agent-lifecycles/templates/orchestration-task.md b/.claude/skills/l-01-agent-lifecycles/templates/orchestration-task.md index c3d4bb08..c1559030 100644 --- a/.claude/skills/l-01-agent-lifecycles/templates/orchestration-task.md +++ b/.claude/skills/l-01-agent-lifecycles/templates/orchestration-task.md @@ -1,10 +1,12 @@ # Orchestration-Task Template The artifact a **strategist** drafts for the **orchestrator** (`roles/strategist.md`) — the sprint -plan and the sprint scope. A strategist run producing this artifact is a **mandatory precondition -for any orchestrated run**; the orchestrator adopts the accepted draft into durable task form (the -strategist is a reader, not a mutator). It is written under the series/coordination `notes/` path -the brief names and revised across drawing-board rounds. +plan and the sprint scope. The architect proposes the strategist pass, and the strategist drafts +this artifact only after developer approval; the orchestrator adopts the accepted draft into +durable task form (the strategist is a reader, not a mutator). When the developer sanctions a +strategist skip, `roles/orchestrator.md` owns the alternate author-and-adopt path. It is written +under the series/coordination `notes/` path the brief names and revised across drawing-board +rounds. ## Rules diff --git a/.claude/skills/l-01-agent-lifecycles/templates/turn-report.md b/.claude/skills/l-01-agent-lifecycles/templates/turn-report.md index b771ada4..597f40fd 100644 --- a/.claude/skills/l-01-agent-lifecycles/templates/turn-report.md +++ b/.claude/skills/l-01-agent-lifecycles/templates/turn-report.md @@ -2,7 +2,8 @@ The **mandatory** artifact a worker writes at **every** hand-off (`roles/worker.md`). It is how the leaf's work survives the session's death and how a respawned successor onboards from **state, not the -transcript**. A missing turn report is nudged by the manager. +transcript**. A missing turn report is nudged by the HFX2-L2 supervisor sweep, never by a +manager watching for it (uniform-mechanism ruling 2026-07-07). ## Rules diff --git a/.claude/skills/l-01-agent-lifecycles/templates/worker-brief.md b/.claude/skills/l-01-agent-lifecycles/templates/worker-brief.md index db8f44d0..c91e9779 100644 --- a/.claude/skills/l-01-agent-lifecycles/templates/worker-brief.md +++ b/.claude/skills/l-01-agent-lifecycles/templates/worker-brief.md @@ -7,7 +7,8 @@ absorbed a series of real dispatch frictions (route-index leaks, attestation for keying, missing `python` shim), so deviate knowingly or not at all. Spawn with `env={"AR_SPAWN_ROLE": "worker"}` and the **qualified** leaf key -`//` so the session-start router and the dashboard leaf rail both engage. +`//`; together they claim the worker's `(leaf, role)` seat while the +session-start router and dashboard leaf rail engage. --- @@ -18,13 +19,14 @@ ROLE BRIEF — worker You are a WORKER for leaf `` of master `` (repo: ). Your lifecycle is `skills/l-01-agent-lifecycles/roles/worker.md`; this brief is your session start. Execute the leaf -completely, write your turn report, then stop. +code completely, write your builder turn report, then stop. Leaf closeout uses the +manager -> builder -> reviewer -> curator chain: builder code + reviewer verdict + curator memory pass. -## Worktrees (your ONLY writable areas) +## Worktrees (your code write area + memory context) - Code: `` (branch ``, base ``) -- Memory: `` +- Memory: `` (read/context for changed-path notes; the curator writes onboarding) - Plus your turn report at the path below. Nothing else. NEVER `git commit` — the owning seat - closes out after reviewing your report. + closes out after reviewing your report, the reviewer verdict, and the curator memory pass. ## Tool surface - Native file tools inside the two worktrees; shell for the checks below. @@ -46,18 +48,18 @@ files involved, the invariants that must hold, what NOT to touch.> - Full: — must exit 0. - `git diff --check` in both worktrees. -## Onboarding (same editing pass, per c-05) -- Changed source files: update the sidecar BODY now; new files: create the sidecar. -- Route overviews: genuine body update where routes changed; otherwise the newest history entry - uses the LITERAL form `- — No route impact: ` (timestamp first). -- Pin idiom for verification metadata: "Verification metadata pinned until closeout stamps the - commit." +## Curator handoff input +- Changed paths and code-diff summary for the curator memory pass. +- Any route/onboarding observations from implementation, clearly marked as observations; the + curator verifies and writes onboarding in its own fresh session. +- Pin idiom for any metadata note the curator needs: "Verification metadata pinned until closeout + stamps the commit." ## Turn report (mandatory, last act) Write `/-worker-report.md` following `skills/l-01-agent-lifecycles/templates/turn-report.md` — including exact check commands + -outcomes, the retrieval-evidence tally, and the respawn state. If blocked: fill Escalations and -stop — escalate to , never to the developer. +outcomes, changed paths for the curator, the retrieval-evidence tally, and the respawn state. If +blocked: fill Escalations and stop — escalate to , never to the developer. ``` --- diff --git a/.claude/skills/w-02-light-task-workflow/master-template.md b/.claude/skills/w-02-light-task-workflow/master-template.md index 42738c16..a807afd1 100644 --- a/.claude/skills/w-02-light-task-workflow/master-template.md +++ b/.claude/skills/w-02-light-task-workflow/master-template.md @@ -7,7 +7,7 @@ built to grow as the work unfolds. ## When to escalate to a series -The `l-01-agent-lifecycles` orchestrator lifecycle's `decide` step escalates a single task to a series once its size is apparent — the +The `l-01-agent-lifecycles` architect lifecycle's `decide` step escalates a single task to a series once its size is apparent — the implementation plan no longer fits on a single page, or the work splits into distinct slices that each deserve their own checklist and commit. You can also start single and escalate later: drop in the master `task.md` and move the existing plan into the first `NN_.md`. diff --git a/.codex/hooks/agents-remember-session-start.md b/.codex/hooks/agents-remember-session-start.md index cd999a73..a6613b7a 100644 --- a/.codex/hooks/agents-remember-session-start.md +++ b/.codex/hooks/agents-remember-session-start.md @@ -4,9 +4,9 @@ If `AR_SPAWN_ROLE` is set, or your first user message is a role brief from an orchestrating agent: **ignore this notice entirely — your brief is your session start.** -Otherwise you are the developer-facing session, i.e. the **orchestrator**: read +Otherwise you are the developer-facing session, i.e. the **architect**: read `ar-coordination/AGENTS.md`, then run your lifecycle at -`skills/l-01-agent-lifecycles/roles/orchestrator.md` — trust checkpoint before +`skills/l-01-agent-lifecycles/roles/architect.md` — trust checkpoint before relying on memory, `read_ar_files` (paired source+onboarding) until the build decision, retrieval-strategy tally as evidence, notify-and-stop at every developer hand-off. diff --git a/.codex/skills/c-05-create-or-update-onboarding-files/SKILL.md b/.codex/skills/c-05-create-or-update-onboarding-files/SKILL.md index 31ea0090..d8e39a82 100644 --- a/.codex/skills/c-05-create-or-update-onboarding-files/SKILL.md +++ b/.codex/skills/c-05-create-or-update-onboarding-files/SKILL.md @@ -16,6 +16,16 @@ Planning stays in task artifacts. This package defines how onboarding itself is Before maintaining onboarding, use `c-08-ar-coordination-context-resolver` to resolve the target repository's active coordination context. It must use the `Domain Documentation` category declared in the resolved `system/sources.md` for the onboarding slice being maintained, rather than assuming that adjacent onboarding alone is sufficient or hard-coding one particular documentation system into the skill. +**Seat routing:** in the manager -> builder -> reviewer -> curator chain (`l-01-agent-lifecycles` +`roles/curator.md`), onboarding create/update duty during leaf work belongs to the curator seat, not +the builder — the builder produces code and a turn report only. The curator runs this skill's +workflows from a change set (landed diff), the leaf task doc, and notes/ fed to it by the manager, +and routes each item to the right onboarding home (a concrete sidecar or the governing overview +whose subject it is; the L3 Operational-Notes target is last-resort only, never a default). The +strict 1-to-1 source mapping, governing-overview links, and metadata rules below are unchanged by +this routing — only the writing seat moved. A solo flat session with no separate curator seat runs +this skill itself, exactly as before. + When onboarding maintenance is happening inside a `c-09-git-worktree-manager` worktree, check `worktree_status` before starting memory entries. If its `freshness` block reports that the recorded code or memory source branch moved, diff --git a/.codex/skills/c-09-git-worktree-manager/SKILL.md b/.codex/skills/c-09-git-worktree-manager/SKILL.md index 692dec19..58a87184 100644 --- a/.codex/skills/c-09-git-worktree-manager/SKILL.md +++ b/.codex/skills/c-09-git-worktree-manager/SKILL.md @@ -1,6 +1,6 @@ --- name: c-09-git-worktree-manager -description: "Create, attach to, report on, integrate, finalize, and clean up Agents Remember worktree-backed tasks while preserving human approval gates and external-memory compatibility." +description: "Create, attach to, report on, integrate, finalize, and clean up Agents Remember worktree-backed tasks while preserving delegated/human approval gates and external-memory compatibility." --- # c-09-git-worktree-manager Git Worktree Manager @@ -75,11 +75,14 @@ The intended order is: integration branch and integrates back into it. For a nested master, create the child integration branch from the parent integration branch. 6. choose or review the task slug and workflow variables -7. **hand off** the **Worktree Intent Gate** (notify-and-continue) for explicit developer approval +7. establish the applicable worktree-start authority: for standalone/new work, hand off the + **Worktree Intent Gate** for explicit developer approval; for subordinate leaves/edges inside an + accepted orchestrated series, record the accepted-series authority and continue without a new + developer hand-off 8. create the durable task wrapper when one is needed 9. request the `worktree_start` MCP tool only after the task identity is stable, the correct landable `source_branch` is selected, external memory is clean, and - the developer approved the intent packet + the applicable authority has been recorded The Worktree Intent Gate must name: @@ -97,7 +100,7 @@ visible: leaf work branches integrate into the pushable integration/source branc recorded by their enclosure; protected targets are reached later through the repo's PR flow. -Run the applicable dry-run/preflight first, then **hand off**: call +For developer-gated starts, run the applicable dry-run/preflight first, then **hand off**: call `lifecycle_turn_end_notification(summary={…the intent packet + the approve/revise ask…})` as the **last tool call**, then deliver the intent packet as your final prose and **STOP / end your turn**. The notification sets the `awaiting-developer` lifecycle state, surfaces a @@ -106,6 +109,10 @@ dashboard or in the leaf's attached chat; the **first AR tool call of your next lifecycle (`running`), clears the attention item, and proceeds to `worktree_start` — you send no explicit `lifecycle_resume`. +For subordinate orchestrated-series starts, do the same dry-run/preflight, record the accepted +planner/series authority in the task decision log or worktree intent note, and continue. Do not add +a developer stop for every leaf worktree. + Parked fallback: the block-and-wait `lifecycle_gate` junction (plus the operator inbox and dashboard GateResponder) still works if you deliberately raise it for a durable, developer-attributed, mutation-blocking record: @@ -190,19 +197,27 @@ quality gate, memory content commit, ledger update, and ledger commit. For worktree-backed tasks, pass the leaf enclosure `series-contract.md` to `worktree_closeout_preview` / `worktree_closeout_apply`. The apply step records -the developer's explicit commit approval in the contract and updates the -contract closeout state after the code, memory, and ledger commits are created. +the applicable closeout authority in the contract and updates the contract closeout state after the +code, memory, and ledger commits are created. Worktree closeout stops if the recorded code or external-memory source branch moved since task start. ## Integration -Integration is explicitly human-gated and runs only after closeout completed. It lands the closed task branches back onto the recorded source branches and records the landed commits separately from the closeout commits. Orchestrated-run carve-out (ruled 2026-07-06): dependency-ordered leaf→master and master→super integrations ride the series' standing approval (the developer's portfolio-gate approval recorded in the planner master) — the developer hand-off concentrates at the super PR/carry-over gate per the `l-01-agent-lifecycles` loop/orchestrator doctrine; a raised durable `integration-approval` gate still awaits the developer. +Integration runs only after closeout completed and is authority-gated by context. It lands the +closed task branches back onto the recorded source branches and records the landed commits +separately from the closeout commits. In an accepted orchestrated run, dependency-ordered +leaf→master and master→super integrations ride the series' standing approval (the developer's +portfolio-gate approval recorded in the planner master) — the developer hand-off concentrates at +the super PR/carry-over gate per the `l-01-agent-lifecycles` loop/orchestrator doctrine. A raised +durable `integration-approval` gate still awaits the developer. On an orchestrated master's exit (master → super integration) the integrate step additionally enforces the delegated `master-handover-approval` seam: an undecided or policy-invalid handover gate addressed to the master (by `enclosure` = master task name) returns `handover-gate-blocked` instead of landing — decide the gate per the `l-01-agent-lifecycles` seam doctrine, then rerun. When no gate addresses the integrating master but open `master-handover-approval` gates exist elsewhere, integrate still proceeds and its result carries a `handover_gate_warning` naming them — treat it as a spelling check on the raised gate's `enclosure`. -Run `worktree_integrate(..., dry_run=true)` first, then **hand off**: call +Run `worktree_integrate(..., dry_run=true)` first. For subordinate accepted-series integrations, +record the standing series authority and then run the real integration without a developer stop. +For developer-gated integrations, **hand off**: call `lifecycle_turn_end_notification(summary={…the integration plan…})` as the **last tool call**, then deliver the integration preview as your final prose and **STOP**. The developer approves on the dashboard or in chat; the first AR tool call of your next turn auto-resumes @@ -228,9 +243,17 @@ After successful integration, complete any repo-specific landing tail first: pus ## Lifecycle Finalization And Cleanup -Lifecycle finalization is explicitly human-gated and runs only after closeout, integration, and any PR/carryover tail are complete. It proves the current parent-child branch edge, then removes the recorded code and memory worktrees, deletes local task branches only when Git can prove they are merged, removes empty worktree group folders when safe, records `cleanup: completed` in the contract, and updates task documents. - -Run `lifecycle_finalize_task(..., dry_run=true)` first, then **hand off**: call +Lifecycle finalization runs only after closeout, integration, and any PR/carryover tail are +complete, and its approval authority follows the same series boundary. For subordinate +accepted-series leaf/master edges, the owning manager/orchestrator may finalize and clean up after +the dry-run proves the landed edge. For final super→main cleanup, standalone work, or a deliberately +raised `cleanup-approval` gate, stop for developer approval. Finalization proves the current +parent-child branch edge, then removes the recorded code and memory worktrees, deletes local task +branches only when Git can prove they are merged, removes empty worktree group folders when safe, +records `cleanup: completed` in the contract, and updates task documents. + +Run `lifecycle_finalize_task(..., dry_run=true)` first. For subordinate accepted-series cleanup, +record the standing authority and run the real finalizer. For developer-gated cleanup, **hand off**: call `lifecycle_turn_end_notification(summary={…what cleanup removes…})` as the **last tool call**, then relay the landed-commit proof, cleanup plan, and task-document updates as your final prose and **STOP**. The developer approves on the dashboard or in chat; the first AR tool call of your next turn auto-resumes and runs @@ -277,11 +300,16 @@ then proceeds as usual, including closeout → integrate → finalize. 2. The `c-09-git-worktree-manager` skill does not initialize memory roots; use the `c-00-initialize-memory-repo` skill before starting external-memory worktrees. 3. Closeout belongs to the `c-12-closeout` skill; the `c-09-git-worktree-manager` skill only supplies worktree contract context. 4. The `c-09-git-worktree-manager` skill must not use divergent memory as semi-trusted reference context. -5. The `c-09-git-worktree-manager` skill must not bypass the `c-12-closeout` skill's explicit closeout approval gate. +5. The `c-09-git-worktree-manager` skill must not bypass the `c-12-closeout` skill's applicable + closeout authority gate. 6. The `c-09-git-worktree-manager` skill must not create closeout commits outside the `c-12-closeout` skill's code-memory-ledger sequence. 7. The `c-09-git-worktree-manager` skill must not call `worktree_start` until - the developer has approved the Worktree Intent Gate. -8. The `c-09-git-worktree-manager` skill must not move source branches during integration until replay/preflight has produced fast-forwardable code and memory commits and explicit integration approval exists. -9. The `c-09-git-worktree-manager` skill must not finalize or clean up without explicit cleanup/finalization approval. + the applicable authority has been recorded: developer-approved Worktree Intent Gate for + standalone/new work, or accepted-series authority for subordinate orchestrated work. +8. The `c-09-git-worktree-manager` skill must not move source branches during integration until + replay/preflight has produced fast-forwardable code and memory commits and applicable + integration authority exists. +9. The `c-09-git-worktree-manager` skill must not finalize or clean up without applicable + cleanup/finalization authority. 10. The `c-09-git-worktree-manager` skill must not treat squash-merged content as a normal landed edge. 11. The `c-08-ar-coordination-context-resolver` skill remains the facts-only resolver; the `c-09-git-worktree-manager` skill owns worktree and lifecycle mutation. diff --git a/.codex/skills/c-12-closeout/SKILL.md b/.codex/skills/c-12-closeout/SKILL.md index 2c7fe3ff..82c472a5 100644 --- a/.codex/skills/c-12-closeout/SKILL.md +++ b/.codex/skills/c-12-closeout/SKILL.md @@ -1,6 +1,6 @@ --- name: c-12-closeout -description: "Close out approved Agents Remember edits by preserving explicit commit approval, missing-onboarding checks, external-memory onboarding refresh, memory quality, ledger alignment, and no automatic push for worktree-backed tasks." +description: "Close out approved Agents Remember edits by preserving the applicable approval authority, missing-onboarding checks, external-memory onboarding refresh, memory quality, ledger alignment, and no automatic push for worktree-backed tasks." --- # c-12-closeout Closeout @@ -15,6 +15,18 @@ direct-checkout closeout path. Use the `c-09-git-worktree-manager` skill for worktree start, attach, status, integration, lifecycle finalization, and cleanup; use this skill for the closeout gate and code-memory-ledger commit order. +**Seat note (manager -> builder -> reviewer -> curator chain):** in that chain, the builder produces +code and a turn report only — it does not author onboarding. The dedicated curator seat +(`l-01-agent-lifecycles` `roles/curator.md`) runs the `c-05-create-or-update-onboarding-files` skill +as its own fresh pass, fed the leaf's landed change set, task doc, and notes/, BEFORE the owning +seat (the manager) runs this skill's closeout preview. Everywhere below that says "create" or +"refresh" onboarding, that authoring already happened in the curator's pass; the seat running +closeout **verifies** the curator's output against the checks in this skill, it does not author +onboarding inline to make a failing check pass. A check that still fails after the curator pass is a +closeout failure — respawn/rerun the curator, do not patch onboarding from the closeout seat. This +distinction does not apply outside that chain (e.g. a solo flat session with no separate curator +seat still runs `c-05-create-or-update-onboarding-files` itself before closing out). + ## MCP Tools Use the worktree closeout tools against the task contract: @@ -29,18 +41,31 @@ Worktree closeout records closeout state in the contract the `c-09-git-worktree-manager` skill owns later integration, lifecycle finalization, cleanup, and task-document completion. -## Approval Hand-off +## Approval Authority + +Closeout is always authority-gated, but the authority is contextual. + +For standalone work, final super-branch landing, or any closeout where the accepted task/series +authority is unclear, agents must request the matching preview tool first, relay the proposed code, +memory, and ledger commit messages to the developer, and ask for explicit commit approval. + +For subordinate work inside an accepted orchestrated series, the owning seat may apply closeout +under delegated series authority after the preview/checks are clean. Managers govern leaf commits; +the orchestrator governs manager/master edges and direct flat work when it is wearing the manager +or worker hat itself. Do not stop for the developer merely because closeout will create code, +memory, and ledger commits. The `intent_note` records the authority source, e.g. the accepted +planner/series task and the owning seat's review of the preview. -Closeout is explicitly human-gated. Agents must request the matching preview -tool first, relay the proposed code, memory, and ledger commit messages to the -developer, and ask for explicit commit approval. +Closeout still stops for the developer when the work reaches the final completed super branch / +PR-carryover gate, when a `closeout-approval` gate has been deliberately raised, when the change is +outside the accepted scope, when checks remain red outside the task, when onboarding/memory quality +cannot be repaired inside the leaf, or when a quo-vadis decision is required. -Real closeout uses the matching apply tool with an `intent_note`. The note -records the developer's explicit commit approval. Agents must not treat -implementation approval, a previous "looks good", or their own judgment as -commit approval. +Real closeout uses the matching apply tool with an `intent_note`. The note records the applicable +authority: either explicit developer commit approval or delegated accepted-series authority. Agents +must not treat a vague "looks good" or their own preference as authority. -The relay follows the `l-01-agent-lifecycles` orchestrator hand-off protocol: run the +For a developer-gated closeout, the relay follows the `l-01-agent-lifecycles` orchestrator hand-off protocol: run the preview/dry-run first, then call `lifecycle_turn_end_notification(summary={…the preview facts + the commit ask…})` as the **last tool call**, then deliver the preview facts and proposed messages as plain @@ -66,10 +91,11 @@ the dashboard and a `closeout-approval` gate is explicitly raised, closeout is * server-side through that durable gate, so a developer can approve from the cockpit and the mutating tool — not a UI button — is the security boundary. -`closeout-approval` **is** the commit gate — closeout is the single -commit-of-record for code, memory, and ledger, so there is no separate -`commit-approval` kind; every commit routes through this gate. The dashboard -junction uses the preview/dry-run -> chat report -> `lifecycle_gate` order above. +`closeout-approval` **is** the human commit gate when it is deliberately raised — closeout is the +single commit-of-record for code, memory, and ledger, so there is no separate `commit-approval` +kind. Subordinate orchestrated-series closeouts normally do not raise this gate; they use the +accepted-series authority recorded in the `intent_note`. The dashboard junction uses the +preview/dry-run -> chat report -> `lifecycle_gate` order above. How it binds: @@ -99,16 +125,19 @@ How it binds: Rules: -1. **Never self-approve.** A model-attributed approval is rejected by - enforcement. Wait for the developer's dashboard decision or Chat response, and - never pass your own judgment off as commit approval. +1. **Never self-approve a human-pinned gate.** A model-attributed approval is rejected by + enforcement. Wait for the developer's dashboard decision or chat response when a + `closeout-approval` gate exists, and never pass your own judgment off as developer approval. + Delegated-series closeout without a raised gate is different: it records the accepted series + authority and the owning seat's review in `intent_note`. 2. **Opening a gate is opt-in and deliberate.** Open a `closeout-approval` gate **only** when a developer is driving approval from the dashboard. Do **not** open one in a pure-chat session with no cockpit watching — an `open` gate blocks your own closeout until it is decided. -3. **Gateless lifecycles are unchanged.** With no `closeout-approval` gate the chat - commit gate (`intent_note` after an explicit "commit") governs exactly as before; - enforcement is additive, never a new requirement on every closeout. +3. **Gateless lifecycles use the applicable authority.** With no `closeout-approval` gate, + standalone/final work still needs explicit developer commit approval, while accepted + orchestrated-series subordinate work may proceed under delegated series authority. Enforcement is + additive, never a requirement to raise a gate on every closeout. 4. The closeout preview/apply payload carries a `closeout_gate` block (`enforced` / `permitted` / `gateId` / `reason`); relay it at the commit-approval gate so the developer sees whether a dashboard gate is open, approved, or absent. @@ -130,17 +159,22 @@ python -m agents_remember.memory_quality.integrity.check_missing_onboarding --co ``` The check only evaluates files that are new in the current checkout or -worktree, not the whole historical repository. If it reports missing -onboarding, create those sidecars through the `c-05-create-or-update-onboarding-files` skill before committing code. After -the code commit exists, refresh the new sidecars' verification metadata to that -commit during the normal post-code-commit memory refresh. +worktree, not the whole historical repository. In the manager -> builder -> +reviewer -> curator chain, this check is expected to already pass by the time the owning seat runs +it, because the curator's memory pass created those sidecars through the +`c-05-create-or-update-onboarding-files` skill before this precondition is checked; running the +check here confirms that pass, it is not the trigger to author onboarding from the closing seat. If +it still reports missing onboarding, do not create the sidecars inline — escalate to run (or rerun) +the curator's memory pass, then rerun this check. After the code commit exists, refresh the new +sidecars' verification metadata to that commit during the normal post-code-commit memory refresh. Changed (already-onboarded) source files have a parallel requirement: their sidecar content must be updated to approved current state before closeout. The closeout gate rejects any changed source file whose existing sidecar body was not modified in the current task, because advancing verification metadata over -stale content defeats the commit-hash-based drift check. Update changed sidecars -during implementation, not at the metadata-refresh step. +stale content defeats the commit-hash-based drift check. In the curator chain, changed sidecars are +updated during the curator's memory pass, not at the metadata-refresh step, and not by the builder +during implementation. The closeout worklist covers the working tree plus the leaf contract-recorded committed range: every path changed between the last verified commit (the @@ -159,11 +193,15 @@ deliberately through the `c-05-create-or-update-onboarding-files` skill. External-memory closeout order is: -1. run `check_missing_onboarding` against current additions -2. create missing onboarding for newly added eligible source files before committing code +1. run `check_missing_onboarding` against current additions (in the curator chain, this confirms the + curator's pass already covered them — it is not the cue to author onboarding here) +2. if onboarding is still missing, escalate to run/rerun the curator's memory pass through the + `c-05-create-or-update-onboarding-files` skill before committing code (solo flat sessions with no + separate curator seat create it directly) 3. commit code changes and capture `C2` plus its commit date 4. run the `c-02-memory-quality-control` skill's drift check against `C2` to produce the full memory update worklist -5. verify each changed source file's sidecar content was updated in this task, then refresh affected onboarding `lastVerifiedCommitHash` and `lastVerifiedCommitDate` to `C2`; a changed source file with an unmodified sidecar body fails the closeout instead of receiving a metadata-only refresh +5. verify each changed source file's sidecar content was updated in this task (by the curator's pass + in the chain above), then refresh affected onboarding `lastVerifiedCommitHash` and `lastVerifiedCommitDate` to `C2`; a changed source file with an unmodified sidecar body fails the closeout instead of receiving a metadata-only refresh 6. refresh affected repo entity catalog `git-blob-set-v1` fingerprints against `C2` when changed source paths are listed as entity evidence 7. refresh affected route overview `lastVerifiedCommitHash` / `lastVerifiedCommitDate` metadata to `C2` 8. refresh generated route indexes so `overview.index.json` matches the updated onboarding tree @@ -214,16 +252,20 @@ preview and apply payloads for the commit-approval relay. Worktree closeout also fails when the recorded code or external-memory source branch moved since task start. -Missing onboarding is the expected hard failure when the implementation/update -pass did not produce a required onboarding file. The next step is to run the `c-05-create-or-update-onboarding-files` skill -for that source file, then rerun the closeout preview. +Missing onboarding is the expected hard failure when the required onboarding file was not produced — +in the manager -> builder -> reviewer -> curator chain that means the curator's memory pass did not +cover it. The next step is to run (or rerun) the curator's `c-05-create-or-update-onboarding-files` +pass for that source file, then rerun the closeout preview; a solo flat session with no separate +curator seat runs that skill itself. ## Boundaries 1. The `c-12-closeout` skill owns closeout approval and code-memory-ledger commit sequencing. 2. The `c-12-closeout` skill does not create worktrees, integrate worktrees, finalize lifecycles, or clean up worktrees. 3. The `c-12-closeout` skill does not initialize memory roots; use the `c-00-initialize-memory-repo` skill. -4. The `c-12-closeout` skill must not commit without explicit commit approval after a closeout preview. +4. The `c-12-closeout` skill must not commit without the applicable authority after a closeout + preview: explicit developer commit approval for standalone/final work, or recorded delegated + series authority for subordinate accepted-series work. 5. The `c-12-closeout` skill must not create a memory content commit whose affected onboarding metadata still points at pre-closeout code. 6. The `c-12-closeout` skill must not create a memory content commit before route overview metadata, generated route indexes, and `memory_quality_check` are clean for the new code commit. 7. The `c-12-closeout` skill must not push automatically. diff --git a/.codex/skills/c-13-install-and-onboard/SKILL.md b/.codex/skills/c-13-install-and-onboard/SKILL.md index d583fd13..e52d9f57 100644 --- a/.codex/skills/c-13-install-and-onboard/SKILL.md +++ b/.codex/skills/c-13-install-and-onboard/SKILL.md @@ -152,8 +152,8 @@ preference): 4. **Harness preference + role knobs** (`orchestration.spawn.harness`, per-role `orchestration.roles.`, per-level `orchestration.rolesPerLevel..`) - which installed harness - `spawn_agent_session` uses when the spawning seat passes none, per-role - harness/model/effort overrides, and per-LEVEL overrides + `spawn_agent_session` uses when no role/level knob supplies one, per-role + harness/model/effort settings, and per-LEVEL settings (leaf|master|portfolio) for tiered economics (e.g. a cheap leaf reviewer, a smarter master-seam reviewer). Harness values must be known ids: the builtin registry (`claude`, `codex`, `pi`) or an `orchestration.harnesses` @@ -163,8 +163,10 @@ preference): `ultracode`); mention the FREE-FORM escape hatch for anything outside the vocabularies - `launchArgs` (verbatim argv), `sessionCommands` (pasted before the brief), `promptKeywords` (prepended to the brief) - never - validated, recorded in spawn provenance. Default: detection-gated (the - first detected harness). The full spawn-surface manual is + validated, recorded in spawn provenance. Ordinary spawning seats cannot + pass `harness`/`model`/`effort`, launch/session spend controls, or harness-native + spend/endpoint env keys directly; settings are the spend surface. Default: + detection-gated (the first detected harness). The full spawn-surface manual is `docs/reference/harnesses.md`. If the developer wants to skip the interview, confirm the seeded defaults diff --git a/.codex/skills/l-01-agent-lifecycles/SKILL.md b/.codex/skills/l-01-agent-lifecycles/SKILL.md index 1d390618..4094072d 100644 --- a/.codex/skills/l-01-agent-lifecycles/SKILL.md +++ b/.codex/skills/l-01-agent-lifecycles/SKILL.md @@ -1,6 +1,6 @@ --- name: l-01-agent-lifecycles -description: "The agent lifecycles: one lifecycle per agent type, under one roof. Routes every session by exactly three conditions (spawn-role env -> role brief -> otherwise orchestrator), carries the minimal lifecycle frame (the six lifecycle signals every session shares), and houses the self-contained per-role lifecycles (orchestrator, designer, strategist, manager, worker, adversarial reviewer) plus the report-template library and the reviewer criteria catalogs. A developer-facing session IS the orchestrator; solo work is the degenerate portfolio. Supersedes and replaces both l-01-session-job-lifecycle and l-02-agent-orchestration." +description: "The agent lifecycles: one lifecycle per agent type, under one roof. Routes every session by exactly three conditions (spawn-role env -> fresh role brief -> otherwise free-chat launcher), carries the minimal lifecycle frame (the six lifecycle signals every session shares), and houses the self-contained per-role lifecycles (architect, orchestrator, designer, strategist, manager, worker, curator, system-specialist, adversarial reviewer) plus the report-template library and the reviewer criteria catalogs. Solo work is the degenerate portfolio. Supersedes and replaces both l-01-session-job-lifecycle and l-02-agent-orchestration." --- # l-01-agent-lifecycles — The Agent Lifecycles @@ -15,42 +15,128 @@ lifecycle, and no role reads another role's file. 1. **`AR_SPAWN_ROLE` is set** (spawn env, injected by `spawn_agent_session`) → run `roles/.md`. Nothing else in this file's "developer session" material applies to you. (`designer` here means the same design hat in a separate chair — see `roles/designer.md`.) -2. **Else: the first user message is a role brief** — a `templates/*-brief.md`-shaped dispatch or +2. **Else: the first user message is a role brief in a fresh session** — a `templates/*-brief.md`-shaped dispatch or a first line of the form `ROLE BRIEF — ` from an orchestrating agent → run that role's lifecycle. The brief is your session start; a workspace session-start notice is not addressed to you. -3. **Else** (a developer opened this session) → you are the **orchestrator**: run - `roles/orchestrator.md`. Solo work is the degenerate portfolio — the same three jobs with hats - collapsed (the orchestrator wears the manager hat in flat runs and builds hands-on at session - scale); the task doc still comes first. +3. **Else** (a developer opened this session) → you are the developer-facing **free chat** — a + launcher, not a role seat (ruled 2026-07-09). Research-only questions are answered inline with + no role taken. The moment the ask is role-shaped (a sprint, a task, any durable change), do NOT + assume the architect role in this chat: **spawn the architect into its own chat** + (`spawn_agent_session` with `AR_SPAWN_ROLE=architect`; the profile — harness/model/effort — + comes from `orchestration.roles.architect` in settings, never from this session's guess) and + point the developer at that chat. A clean startup with the settings-owned profile removes all + profile ambiguity. The spawned architect runs `roles/architect.md` and owns the developer + conversation from there. There is no fourth entry, and the edge cases are decided: an **unresolvable `AR_SPAWN_ROLE` value** (no matching `roles/.md`) falls through to condition 2 (the brief); a role-env session **whose brief never arrives** announces itself on the inbox and waits — it never -improvises a task; `AR_SPAWN_ROLE=orchestrator` is valid only as a takeover chair (the Profile -check (takeover) in `roles/orchestrator.md`, The Event Loop) — the developer still talks to **one** orchestrator. Orchestrated -fan-out (spawning managers/workers at scale) begins only on an explicit developer request (e.g. -*"orchestrate these masters"*) — no agent promotes itself into a spawning seat. +improvises a task; `AR_SPAWN_ROLE=orchestrator` is valid only as a spawned backend seat or a +backend takeover chair — the developer still talks to the **architect**, not the orchestrator. +The spool-up chain is fixed and self-driving (ruled 2026-07-09): free chat spawns the +**architect**; the architect spawns the **orchestrator** for portfolio execution; the +orchestrator spawns **managers** per the approved plan and the concurrency settings; managers +spawn their **workers**. No seat waits to be told "spawn this, spawn that" — each level spawns +its next level from the plan. Only two spool-up decisions ever go back to the developer, both as +questions the agent raises itself: whether to run a **strategist** pass (proposed, never +auto-run), and whether to take the **short root** (solo, no orchestration) when the work looks +tiny — see `roles/architect.md`. One exception to the no-cross-reading rule above: **a seat that WEARS a hat runs that hat's file -as its own** — the orchestrator always for `roles/designer.md`, and in flat runs for -`roles/manager.md` (the hat-collapse rule). +as its own** — the architect may wear `roles/designer.md`, and in solo/flat runs may wear backend +or build hats (the hat-collapse rule). A spawned role seat never wears another role's hat. + +## Developer-Declared Task-Seat Takeover + +When the developer says *"you are the orchestrator/manager/worker for task X"* (or equivalent), +that is a **task-seat takeover**, not a loose role hint. The named task leaf is the seat. Before +analysis, profile checks, spawning, or implementation, open the named task doc and attach the +current dashboard chat to that leaf. + +Operational checklist: + +1. Resolve the named task to the **qualified** leaf key `//` and the + lifecycle role this seat is claiming. +2. Use the dashboard terminal catalog session id for this chat — not `CLAUDE_CODE_SESSION_ID`, not + `CODEX_THREAD_ID`. +3. Call `attach_terminal_session_to_leaf` with that qualified leaf key and the claimed role. +4. Rename the session to the seat label the developer expects. +5. Verify the terminal catalog and dashboard row show that exact `(qualified leaf key, seat role)` + binding before continuing. + +If no dashboard terminal catalog session id can be found, record the blocker and ask for the +missing attachment path. Do not claim the seat is attached until the catalog/dashboard row proves +it. After the attachment is verified, continue with the lifecycle selected above. + +## Developer Clarification Triage + +When the developer clarifies a rule, boundary, or desired behavior during an active task, decide +whether it is **current implementation** or **future queue** before writing only a note. Read the +active queue first: the current leaf, parent/master, neighboring leaves, decision log, open +questions, and in-flight branch state. The question is not whether a note is useful; it is whether +the developer is effectively steering the work already in hand. + +Treat it as current implementation when queue context and closeness point at the active change: it +names the same task/leaf/master, resolves a defect exposed by the current work, or improves the +same doctrine or code path. A small change that plainly fits the current diff is a strong signal +for immediate implementation even if the developer phrases it as "maybe" or "we can wrap this in." +In that case, extend the current task surface/decision log enough to make the added scope visible +and implement it now. + +Treat it as future queue when it names a later release, a separate subsystem, a large scope jump, +work whose correctness depends on another unfinished master, or a change that would reorder +already-running leaves. Record the item in the right durable queue or ask the owning seat to plan it +later. If the intent is genuinely ambiguous after reading the queue, ask the developer directly +whether they want immediate implementation or a queued note. Do not silently downgrade a +close/current/small change into future speak, and do not silently expand the active leaf when the +fit is unclear. ## The Role Registry | Role | Seat | Lifecycle file | | --- | --- | --- | -| **orchestrator** | the developer-facing session; first coordination leaf of an orchestrated series | `roles/orchestrator.md` | -| **designer** | a HAT the orchestrator pulls inline (front of the pipeline or mid-flight; separate chair optional) | `roles/designer.md` | -| **strategist** | the sprint planner, SPAWN-FIRST; a strategist run is a **mandatory precondition for any orchestrated run** — its deliverable is the orchestration task (sprint plan + scope); spawn value `strategist` | `roles/strategist.md` | +| **architect** | the developer-facing owner seat; design conversation, decision-item relay, and drawing board | `roles/architect.md` | +| **orchestrator** | spawned backend portfolio/orchestration seat; never developer-facing | `roles/orchestrator.md` | +| **designer** | a HAT the architect pulls inline (front of the pipeline or mid-flight; separate chair optional) | `roles/designer.md` | +| **strategist** | the sprint planner, SPAWN-FIRST when the developer approves the architect's propose-first question; its deliverable is the orchestration task draft (sprint plan + scope); spawn value `strategist` | `roles/strategist.md` | | **manager** | one coordination leaf per master; drives that master's leaf loop | `roles/manager.md` | | **worker** | one leaf worktree, short-lived, fresh session | `roles/worker.md` | +| **curator** | fresh per leaf after builder/reviewer; writes onboarding only from task docs, notes, and code diff | `roles/curator.md` | +| **system-specialist** | backend provider-degradation investigator; report first, fixes only after explicit orchestrator order; spawn value `system-specialist` | `roles/system-specialist.md` | | **adversarial reviewer** | short-lived, spawned at the two seams (master-exit, super-exit) and as any three-party loop's reviewer seat (criteria catalogs bound per review type); spawn value `reviewer` | `roles/reviewer.md` | The **lenses** (bug · feature · triage · research — `lenses.md`) are how the scoping seats -(orchestrator, designer) read a piece of work; a dispatched role never picks a lens — its brief +(architect, designer, backend orchestrator) read a piece of work; a dispatched role never picks a lens — its brief already carries the flavor. +## Role-Seat Immutability (dashboard-owned sessions) + +When the dashboard owns a session, its role is fixed for the session lifetime. Roles expand +**horizontally** by spawning new, individually addressable chats; sub-agents drill **vertically** +inside one seat's context for deeper analysis. A dashboard-owned session that already has a role +refuses a pasted role brief instead of silently rerouting itself; it escalates the mismatch to its +owner via the inbox. Router condition 2 applies only to fresh sessions. Sessions not owned by the +dashboard follow the host harness's ordinary rules. + +Hat-collapse is sanctioned only for the owner/developer-facing architect seat in solo or flat +runs. Spawned role seats never absorb another role brief and never become a different role in +place. + +## Minimal Decision-Item Relay + +The ARCHITECT/ORCHESTRATOR split uses the existing operator inbox now. No full queue schema or +dashboard reform is introduced here. + +- Backend seats post one `messageKind: decision-item` inbox row at a time to the architect. The row + states what is being decided, the options, the consequences, and the durable evidence refs. +- The architect presents one item at the developer's pace, records the ruling in the durable task + surface (`openQuestions` / decision logs, with notes for analysis), and returns one + `messageKind: decision-ruling` inbox row to the backend seat. +- If the item is underspecified, the architect sends a single clarification row back instead of + guessing. The backend does not open a second item until the active item has a durable ruling or + clarification state. + ## The Minimal Frame (the only machinery every session shares) Every session in a managed repo may be a **lifecycle**: six signals — `lifecycle_start` · @@ -72,19 +158,34 @@ handles a lifecycle id** — identity is server-side, anchored in the worktree c Rules: a tool call outside any lifecycle is **dropped, never misattributed**; `paused` is system-owned. **A spawned role that never touches mutating AR tools simply never instantiates a lifecycle — that is correct, not a violation.** A spawned role runs its **own** lifecycle when it -runs one; it never adopts its spawner's. The session↔leaf association is the catalog binding made -at spawn (the **qualified** leaf key `//`), not lifecycle adoption. +runs one; it never adopts its spawner's. The session↔task-seat association is the catalog binding +made at spawn — the pair of **qualified** leaf key `//` and seat role — +not lifecycle adoption. Different roles may coexist on one leaf; only a second live owner of the +same `(qualified leaf key, seat role)` pair collides. + +**Notify-and-stop is safe by design (HFX2-L1..L4, landed):** ending a turn on +`lifecycle_turn_end_notification` — or simply stopping once your artifact is written and nothing is +pending — is never a liveness gap. Silence is supervised: the HFX2-L2 supervisor sweep evaluates +every expected artifact/signal on its own mechanical tick and the HFX2-L4 escalation ladder +(renudge → skip-level → architect custody/architect attention, then respawn) handles inactivity. **No role watches, +polls, or nudges on its own initiative — that is a banned seat-local watcher (uniform-mechanism +ruling 2026-07-07).** Every role's own liveness duty inverts to *passive*: you will be woken with +your pending signals; process and ack every item before ending your turn again. ## Shared Invariants (every role can count on these) - **Continuity lives in the `task_doc` + durable artifacts, never in transcripts** — which is why short-lived workers and reviewers are safe, and why every seat writes its artifact of record. -- **Escalation ladder: worker → manager → orchestrator → developer.** No rung is skipped, ever. +- **Escalation ladder:** worker → manager → orchestrator → architect → developer; system-specialist + → orchestrator. No rung is skipped, ever. Each role file states only its own rung. - **Observability:** coordination seats are `task_doc` leaves with attached chats; the developer can walk into any seat at any level. - **Decision-needing questions land in the task doc's `openQuestions`** — the rendered decision surface; `notes/` carries the analysis behind them. +- **Dependency graph, not habit, decides sequencing.** Parallelize independent work by default up + to the applicable `orchestration.concurrency` cap. Sequential execution is the exception and + must name a gate, a shared-file one-writer dependency, or an explicit ruling. ## The Three-Party Loop (one home — this section owns the loop doctrine) @@ -95,9 +196,9 @@ section; they do not restate it. | Level | Owner (holds the deliverable, rules, lands) | Builder | Reviewer | | --- | --- | --- | --- | -| Leaf | the leaf's owning seat (manager; orchestrator in tight/flat mode) | spawned worker (no-commit contract) | spawned reviewer, criteria catalog + liberty | +| Leaf | the leaf's owning seat (manager; architect in tight/flat mode) | spawned worker (no-commit contract) | spawned reviewer, criteria catalog + liberty | | Master | the manager | the leaf workers | the master-exit seam reviewer (verdict rides `master-handover-approval`) | -| Portfolio | the orchestrator | the STRATEGIST (spawn-first) | reviewer with the plan-review catalog | +| Portfolio | the backend orchestrator (developer-facing decisions relayed through the architect) | the STRATEGIST (spawn-first) | reviewer with the plan-review catalog | **Complexity-scored tiers (per leaf, at dispatch).** The owning seat scores three axes — blast radius (doctrine/enforcement/public surface vs leaf-local) · novelty (new subsystem vs @@ -123,13 +224,14 @@ they do not open them. open finding set. A round that does not shrink it escalates immediately, regardless of the count; a monotonically converging loop may never hit the cap at all. At the cap, or on non-convergence, the owner does not spin another round — it **escalates one seat up the ladder (worker → manager → -orchestrator → developer) with the full round history attached**; the escalation packet IS the +orchestrator → architect → developer) with the full round history attached**; the escalation packet IS the upper seat's visibility. **Quo-vadis (the written developer-escalation criterion).** A question is developer-worthy when it is a **high-blast-radius truth** — answered wrong it means big rewrites later (architecture direction, security posture, doctrine contradictions, irreversible data/branch operations, where -agent settings live). Quo-vadis questions escalate IMMEDIATELY, regardless of round count. +agent settings live). Quo-vadis questions escalate IMMEDIATELY to the architect relay, +regardless of round count. Presentation-grade choices (2px vs 3px) never do — the owner rules and logs. **Criteria catalogs (the reviewer as test bench).** Criteria are never made up on the spot: every @@ -141,8 +243,27 @@ promotion ratchet (each catalog carries it). `roles/reviewer.md` binds them. orchestrator-level set (the strongest models) and the manager-level set (cheaper, possibly workflow-free) are configured per level in the `orchestration.loops` settings block (schema in `docs/reference/settings-json.md`; stored in the global agentic settings file with repo-local -override, parsed by the kernel agentic-settings loader — L13, landed). The strategist's mandatory -pre-run is doctrine, not a knob — it is unconditional. +override, parsed by the kernel agentic-settings loader — L13, landed). The architect proposes a +strategist pre-run, and it occurs only after developer approval; settings cannot auto-run it. + +## Delegated Series Authority + +Once the developer accepts an orchestrated series/portfolio plan, that acceptance is standing +authority for the owning seats to execute the subordinate edges in that series. Managers govern +their workers and leaf closeouts. The orchestrator governs managers, master handovers, master → +super integrations, and the same closeout/finalize/cleanup mechanics when it wears a manager or +worker hat in a flat/direct run. These edges do **not** stop for a new developer approval just +because a commit, lifecycle finalization, cleanup, or integration command is next; the owner runs +the preview/check, records the accepted-series authority in the intent note or decision log, and +continues. + +This does **not** weaken the escalation ladder. Developer approval is still required for the final +completed super integration branch / PR-carryover gate, for any human-pinned gate that is actually +raised (`integration-approval`, `push-approval`, `cleanup-approval`), for scope changes beyond the +accepted plan, for red checks that cannot be fixed inside the task, and for quo-vadis decisions. +Owner-never-self-approves means verdicts and delegated gates need the configured distinct decider; +it does not force a developer hand-off for mechanical closeout of in-scope work the owning seat +performed directly under standing series authority. ## Knob Block & Capability Doctrine (no per-harness files) @@ -156,6 +277,9 @@ doctrine any coding agent can apply, and harness PREFERENCE is deployment config seats, `spawn_agent_session` is itself the harness-independent fan-out: a harness with no sub-agent facility still dispatches seats through the framework (a chat, no leaf attachment required) — the DBMS principle: one behavior, any engine. +For ordinary spawned seats, settings are the sole developer-controlled spend surface: +`spawn_agent_session` callers declare role and level, never harness/model/effort or direct +launch/session spend controls. ## settings.json Orchestration Block @@ -169,9 +293,12 @@ defaults < global settings < repo-local settings. { "orchestration": { "roles": { // role → knob override; validated: harness/model/effort · free-form: launchArgs/promptKeywords/sessionCommands + "architect": { "harness": "claude", "effort": "high" }, "orchestrator": { "harness": "claude", "effort": "high" }, "strategist": { "effort": "ultracode" }, // session-vocabulary value → "/effort ultracode" post-launch "reviewer": { "harness": "claude", "model": "sonnet", "effort": "high" }, + "system-specialist": { "harness": "claude", "model": "fable", "effort": "high" }, + "curator": { "harness": "codex", "effort": "medium" }, "worker": { "harness": "codex", "effort": "medium" } }, "rolesPerLevel": { // per-LEVEL agent sets (leaf|master|portfolio), deep-merged over roles @@ -179,7 +306,7 @@ defaults < global settings < repo-local settings. "portfolio": { "reviewer": { "model": "fable", "effort": "ultracode" } } }, "concurrency": { "maxParallelMasters": 2, "maxParallelLeaves": 3, "maxSubAgents": 4 }, - "spawn": { "harness": "claude" }, // spawn_agent_session default when the seat passes none + "spawn": { "harness": "claude" }, // fallback when no role/level knob supplies one "gateDelegation": { "policy": "manager-decides-leaf-gates", "requireReviewerVerdictAtSeams": true @@ -199,11 +326,13 @@ needs a restart (an authority-file value is a one-cycle legacy fallback with a b `requireReviewerVerdictAtSeams` **binds delegated seam decisions** (`master-handover-approval`) to attached reviewer-verdict evidence; the named policy `manager-decides-leaf-gates` routes leaf gates to the manager and the master-exit handover to the **orchestrator** (human review concentrates at -the super gate). `spawn_agent_session` resolves its knobs (260703-L16) as explicit args > +the super gate). `spawn_agent_session` resolves its spend knobs (260703-L16 + HFX2-L10) as repo-local level override > global level override > repo-local role default > global role default > detection-gated default — the dispatcher declares its `level` (leaf|master|portfolio, default -leaf) and the resolved level rides spawn provenance — and **applies** them at the harness -boundary: model/effort ride as `AR_SPAWN_MODEL`/`AR_SPAWN_EFFORT` env AND map onto the launch argv +leaf) and the resolved level rides spawn provenance. Legacy caller-supplied `harness`/`model`/ +`effort`, direct launch/session controls, `AR_SPAWN_MODEL`/`AR_SPAWN_EFFORT`, or harness-native +spend/endpoint env keys refuse before spawning with `spend-override-unsupported`. Resolved knobs are +**applied** at the harness boundary: model/effort ride as `AR_SPAWN_MODEL`/`AR_SPAWN_EFFORT` env AND map onto the launch argv per-harness via the effective registry (claude `--model`/`--effort`; a mapping-less harness stays env-only; a session-vocabulary effort like claude's `ultracode` is delivered as a post-launch `/effort` paste). Unknown effort values REFUSE at dispatch naming the harness's vocabulary — the @@ -218,12 +347,15 @@ reuse, complexity thresholds) lives in the same block — meaning in ## Companion Files - `lenses.md` — the four job lenses for the scoping seats. -- `roles/…` — the six self-contained role lifecycles (the registry above). +- `roles/…` — the nine self-contained role lifecycles (the registry above). - `templates/…` — turn-report · worker-brief · manager-brief (`ROLE BRIEF — manager`; the - orchestrator compiles a manager's session start from it) · master-handover-packet · - conversation-handover-packet · verdict · impact-analysis · onboarding-coherency · - deep-research-report · orchestration-task (the strategist's sprint plan). Spawning seats compile - briefs FROM these; sub-agents fan out and fill them, so analysis survives compaction. + orchestrator compiles a manager's session start from it) · curator-brief (`ROLE BRIEF — curator`; + the manager compiles a curator's session start from it, feeding the leaf's landed change set + + task doc + notes/ — never spawned before builder code and the reviewer verdict exist) · + master-handover-packet · conversation-handover-packet · verdict · impact-analysis · + onboarding-coherency · deep-research-report · orchestration-task (the strategist's sprint plan). + Spawning seats compile briefs FROM these; sub-agents fan out and fill them, so analysis survives + compaction. - `criteria/…` — the reviewer criteria catalogs (code-seam · doctrine · onboarding-memory · report-verification · plan-review), the review test bench the three-party loop binds; maintained through the promotion ratchet, never made up on the spot. @@ -249,7 +381,7 @@ This skill absorbs and supersedes `l-01-session-job-lifecycle` and `l-02-agent-o orchestration vocabulary adopts the parked `260619_agentic-control-plane` spec — jobs as model-interpreted markdown (D6), the knob block (D7), role + lens in one file (D10), the ambient-singleton rule (D11), per-harness variants (D12), the judge rung, short-lived workers with -structured handoff, dev-talks-to-one-orchestrator (D15) — which in turn credits **Archon** and the +structured handoff, dev-talks-to-one-architect (D15) — which in turn credits **Archon** and the **agent-control-plane** project (D14); that credit carries forward. ## Relationship To Other Instructions diff --git a/.codex/skills/l-01-agent-lifecycles/criteria/code-seam.md b/.codex/skills/l-01-agent-lifecycles/criteria/code-seam.md index 053feb1b..31e271f6 100644 --- a/.codex/skills/l-01-agent-lifecycles/criteria/code-seam.md +++ b/.codex/skills/l-01-agent-lifecycles/criteria/code-seam.md @@ -67,6 +67,20 @@ foreign artifact's consumer. memory repo's ledger mid-`worktree_start`) PASSED all four lenses under this analysis — the clean exemplar that defined the class. A catch in a later engagement promotes. +### CS-6 — Scaling & reclamation *(promoted — 2 catches)* + +**For every loop, store, log, or queue the change set touches, interrogate all four.** + +1. (D1 — stability) At 10x/100x fleet, does this mechanism's worst-case resource draw threaten the substrate? Where is the budget / backpressure / load-shed that makes it shed the signal, not the system? +2. (D2 — bounded) What is the worst-case time and on-disk / in-memory size? Where is the per-cycle cap and the store's cap+eviction? Is any re-read O(n) per item (accidentally quadratic across layers)? +3. (D3 — reclamation) Who reclaims this data, is that reclamation landed in THIS change, and is it tested by scaling (>=2 sizes), not a single-N smoke? +4. (D4 — quiescence) With ALL external inputs at zero (no acks, no live seats, no operator), does every feedback actor reach a fixed point? Any actor whose OUTPUT is a member of its own INPUT class (an escalation about an escalation, a retry row that is itself retryable, an event emitted while folding events) must prove convergence with a multi-cycle zero-input simulation — a per-cycle budget is a rate limiter, NOT a convergence proof, and per-zone verdicts cannot see a loop that lives on the edge BETWEEN zones. + +- Ruled invariant (developer, 2026-07-09, supersedes any per-row durability doctrine): **no event, message, or row outranks system health.** Notification rows coalesce — a re-firing condition updates its ONE existing row (date, tries, rung), never appends a sibling. Every store is purgeable: pending rows age out, hard caps evict oldest-first, and the durable record is the artifact on disk, never the queue row. A reviewer who finds "keep it all" semantics on a queue-like store files it as a defect regardless of what older doctrine says. +- Catching evidence: 260707-HFX2-L7 — the supervisor sweep re-folded a 61 MB append-only inbox once per finding (O(n^2)) over 4,298 never-reclaimed dead-seat rows, freezing the heartbeat and pegging a core while every correctness test passed. Style/CRAP/cyclomatic gates were all green. +- Catching evidence (D4's seed, the promotion catch): 2026-07-09 escalation-storm meltdown — every ladder rung transition minted a NEW pending, ladder-eligible inbox row addressed to an absent developer; rows were compaction-exempt by doctrine ("pending is immortal"). Each zone passed D1-D3 (budget landed, snapshot fold landed, compactor wired — the L12 audit REFUTED the operator-inbox zone), yet the composed system diverged: 67,925 lines / 227 MB / 20,553 pending rows in one evening, host down. The quiescence regression (`test_unacked_backlog_reaches_a_fixed_point_with_absent_developer`) is D4's executable counterpart. +- Mechanization seam: 260707-HFX2-L8 owns the doctrine; 260707-HFX2-L7 owns the first executable counterparts (`assert_subquadratic` scaling test R5 and the O(1)-inbox-read invariant R2). D1-D3 graduate into a gate once a reusable repo-wide scaling-test helper exists; D4's reusable shape is the zero-input multi-cycle fixed-point simulation. + ## Exploratory Mandate Beyond the standing list, the reviewer owes **novel lenses** (the brief sets N; default 2): attack diff --git a/.codex/skills/l-01-agent-lifecycles/criteria/plan-review.md b/.codex/skills/l-01-agent-lifecycles/criteria/plan-review.md index ef4d2d90..eac17aee 100644 --- a/.codex/skills/l-01-agent-lifecycles/criteria/plan-review.md +++ b/.codex/skills/l-01-agent-lifecycles/criteria/plan-review.md @@ -50,6 +50,21 @@ a merely not-yet-existing surface is NOT unplannable); and thin leaf scopes the guessed around instead of flagging are themselves findings. Quo-vadis contradictions must be flagged at the top of the coherence findings, not buried. +## Candidate Criteria (seeded exploratory — one catching engagement each; promote at ≥2) + +Run under the exploratory mandate; a candidate is proposed for promotion into the standing list +when it catches in a second engagement (the ratchet below). + +### PR-6 — Scaling & reclamation at design time *(candidate — 1 catch)* + +**Any plan that introduces or changes a store, loop over a store, queue, or append-only log must name its cap, budget, and compactor/reclamation owner in the design, before code exists.** Challenge all three design claims: + +1. (D1 — stability) At 10x/100x fleet, does the proposed mechanism's worst-case resource draw threaten the substrate? Where does the design name the budget, backpressure, or load-shed path that sheds the signal, not the system? +2. (D2 — bounded) What is the planned worst-case time and on-disk / in-memory size? Where are the per-cycle cap and the store's cap+eviction defined? Does any layer re-read a growing store per item? +3. (D3 — reclamation) Who owns reclamation, does the same plan land that reclamation with the data it creates, and how will scaling be proven across >=2 input sizes rather than a single-N smoke? + +- Catching evidence: 260707-HFX2-L7/L8 — the plan surface had not made worst-case inbox fold cost, retention, or reclamation owner a required design-time question before an O(n^2) supervisor sweep over never-reclaimed dead-seat rows passed correctness gates and froze the heartbeat. A plan that introduces a store/loop/log without naming its cap, budget, and compactor fails PR-6. + ## Exploratory Mandate Beyond the standing list, the reviewer owes **novel lenses** (the brief sets N; default 2): ways diff --git a/.codex/skills/l-01-agent-lifecycles/roles/architect.md b/.codex/skills/l-01-agent-lifecycles/roles/architect.md new file mode 100644 index 00000000..12dc4ebb --- /dev/null +++ b/.codex/skills/l-01-agent-lifecycles/roles/architect.md @@ -0,0 +1,254 @@ +# Lifecycle — Architect + +> The developer-facing lifecycle: the **drawing board, decision relay, and portfolio face**. +> The architect talks to the developer; the backend orchestrator does not. + +## What This Seat Is + +The architect is the developer-facing owner seat. It owns the design conversation, the +drawing-board rounds, and the pace at which developer decisions are presented. Backend churn +belongs to spawned role seats — especially the orchestrator — and reaches the developer only as +one decision item at a time. + +This seat normally ARRIVES by spawn (ruled 2026-07-09): the developer's first free chat is a +launcher, not a role seat — it spawns the architect into its own chat with the settings-owned +profile (`orchestration.roles.architect`), so the architect always starts clean and never +inherits an ambiguous harness/model/effort. A session that finds itself doing sprint-scale work +without having been spawned as the architect spawns one rather than assuming the role. + +## Spool-Up (the chain is self-driving) + +Once this seat holds an approved plan, the orchestration spools up WITHOUT the developer having +to say "spawn this, spawn that": + +1. **Architect spawns the orchestrator** for backend portfolio execution. +2. **The orchestrator spawns managers** per the approved plan and the + `orchestration.concurrency` settings. +3. **Managers spawn their workers.** + +Exactly two spool-up decisions go back to the developer, and this seat raises both AS QUESTIONS — +it never decides them silently, and it never waits for the developer to remember them: + +- **Strategist pass — propose, never auto-run.** Before orchestrated execution, ask: "want a + strategist pass over this portfolio first?" with a recommendation. When a plan was already made + and ruled, recommend skipping. Never dispatch the strategist without the developer's yes. + (Supersedes the 2026-07-06 "mandatory strategist pre-run" ruling.) +- **Short root — propose when tiny, never self-decide.** Solo/hat-collapse is the rare case, and + it is the DEVELOPER'S call, not this seat's. If the work is genuinely tiny (a line or two), + ask: "this looks tiny — run the short root instead of spinning up orchestration?" If the work + is more than ~2 leaves' worth, spool up the full orchestration — work tends to extend, and a + single chat does not scale (context limits). In between, default to orchestration or ask. + +## Adding A Master To A Running Sprint + +When the developer says "add this master to the sprint" (or the design conversation produces a +new master that belongs in it), this seat attaches it to the sprint STRUCTURE itself — the +dashboard's Operations view hangs masters under a sprint via the orchestration task doc, never +via chat context: + +1. **The master task doc exists first.** Create it through the normal task-doc flow + (`kind: "master"` under `tasks///`) if it does not already exist. +2. **Attach it to the sprint:** append the master's slug to the top-level `orchestrates` list of + the sprint's orchestration task doc (the `kind: "master"` doc that carries `orchestrates`). + That field IS sprint membership — the dashboard derives the orchestration > master > leaf + hierarchy in Operations from it, so the master appears under the sprint the moment the edit + lands. `orchestrates` is master-only by schema; entries are same-repo task slugs. +3. **Log both sides:** a decision-log entry on the sprint doc (master added, why, developer + ruling) and one on the master doc (joined sprint X). +4. **Propose the strategist fit-check — a question, not a dispatch.** Per the spool-up rule, + ask the developer: "want the strategist to evaluate how this master fits the sprint + (dependencies, wave placement, blast radius)?" Recommend YES when other masters are already + in flight or the addition plausibly interacts with them; recommend SKIP when the master is + isolated or the sprint has not started implementation. Never auto-run it. +5. **Tell the backend:** one inbox row to the sprint's orchestrator seat announcing the addition + (and the strategist ruling, once made) so it folds the master into its DAG/waves — the + orchestrator's in-sprint re-evaluation rule takes it from there. + +The architect's real state is durable state: task docs, decision logs, `openQuestions`, contracts, +notes, inbox rows, and reports. It never depends on transcript memory for continuity. It records +rulings durably, then returns those rulings to the backend seat that needs them. + +## Opening Move + +1. Read the workspace instructions and resolve the active Agents Remember context for the target + repository. +2. Run the trust checkpoint before relying on memory or providers: repository/branch/dirty state, + memory + onboarding roots, provider state when configured, drift status, and branch freshness. +3. Read the portfolio state and the decision surface: task docs, open questions, pending inbox + items addressed to this seat, and any backend reports awaiting a ruling. Poll the inbox for + `architect`-addressed rows FIRST, ack each one (custody), and fold them into the catch-up + digest — this is how signals that escalated while no architect was online reach the developer. +4. Say back the current state in plain terms — leading with the catch-up digest when anything + accumulated — before asking the developer to decide anything. + +## Event Routing + +| Condition | Architect job | +| --- | --- | +| The developer is shaping intent, requirements, or scope | **Design** — wear the designer hat inline and create/reshape durable task docs | +| A backend seat posted a decision item | **Decision relay** — present exactly one item, record the ruling, return it via inbox | +| An escalated signal reached terminal custody (ladder rung 3, or any inbox row addressed to this seat/role) | **Custody** — ack (consume) immediately, fold into the catch-up digest; never leave it pending | +| An approved portfolio needs backend execution | **Spawn / supervise** — dispatch the backend orchestrator or other role seats horizontally | +| The developer adds a master to a running sprint | **Sprint attach** — master doc first, slug into the sprint doc's `orchestrates`, log both sides, propose the strategist fit-check, notify the orchestrator (see Adding A Master To A Running Sprint) | +| The ask changes no durable state | **Research-only exit** — answer in chat, no worktree or task mutation | +| The work looks tiny (a line or two) and no backend is spawned | **Ask first** — propose the short root as a question; solo/hat-collapse only on the developer's yes (never self-decided) | + +When a developer clarification lands during an active task, run `../SKILL.md`'s Developer +Clarification Triage before choosing a note-only path. If the queue shows the clarification is +close/current/small, fold it into the active task surface and implement it under the current owner +hat; if it is future queue, record it durably for later planning; if the fit is unclear, ask the +developer which route they intend. + +## Role-Seat Immutability + +In dashboard-owned sessions, this seat remains the architect for its lifetime. A pasted role brief +for another role is refused and escalated through the inbox instead of being absorbed. Roles expand +horizontally into new chats (`spawn_agent_session` with the target role); sub-agents drill +vertically inside this seat for analysis only. Sessions not owned by the dashboard follow their +host harness rules. + +Hat-collapse is allowed here because this is the owner/developer-facing seat. The same collapse is +not allowed in spawned role seats. + +## Design And Drawing Board + +When the developer is still shaping the work, the architect wears `roles/designer.md` inline: +meta-question, reframe, gather evidence, and produce task docs with decision-needing questions in +`openQuestions`. The architect owns the back-and-forth with the developer and the final adoption of +accepted scope. + +When backend work surfaces a high-blast-radius truth — architecture direction, security posture, +doctrine contradiction, irreversible branch/data operation, or where agent settings live — the +architect turns it into a clear drawing-board decision instead of letting the backend guess. +Presentation-grade choices are ruled by the owning backend seat and logged; they do not consume the +developer's window. + +## Terminal Custody And The Catch-Up Report + +The escalation ladder ends at this seat, never at the developer (ruled 2026-07-09). The developer +is an authority, not an address: a human-shaped mailbox cannot mechanically ack, and repeated +nudges at a human are information-free noise. This seat is the last live address a signal lands +on, and custody is its duty: + +1. **Ack on receipt.** Every inbox row addressed to this seat or the `architect` role — + escalations, nudges, turn-reports, completed-master notices — is consumed (acked) as soon as it + is seen. Ack means *custody*, not resolution: "a responsible seat holds this now." +2. **Fold, do not forward.** Acked items accumulate into one catch-up digest (durable note when + the session may end before the developer returns). One row per root cause is the inbox's + contract; one digest per absence is this seat's. +3. **Brief on return.** When the developer comes back, open with the digest: what completed, what + died, what needs a ruling — ranked, in plain terms, before anything else is discussed. +4. **Never expect to be nudged twice.** The supervisor will not repeat-nudge this seat past + custody, because this seat cannot make the developer react faster. If an item needs the + developer and the developer is absent, it waits in the digest — that is the designed state, + not a failure. +5. **Absence degrades gracefully.** With no architect session attached, terminal rows stay + role-addressed and level-triggered: they deliver the moment an architect session appears, are + picked up by the session-start poll (Opening Move step 3), and age out via the inbox pending + TTL if nothing ever collects them — the artifact on disk, not the inbox row, is the record. + +## Minimal Decision-Item Relay + +The relay rides the existing operator inbox. There is no new queue schema here. + +### Intake From Backend + +The backend seat posts one `messageKind: decision-item` inbox row addressed to the architect. The +row must contain: + +- **Decision** — what is being decided, in one sentence. +- **Options** — the live choices, including the backend's recommendation if it has one. +- **Consequences** — what each option changes or risks. +- **Evidence refs** — task docs, notes, reports, diffs, or gate ids needed to verify the item. + +If any field is missing or too vague, the architect returns one clarification row and does not +present the item as a developer decision. + +### Presentation To The Developer + +Present exactly one item at a time, in plain language: + +1. What is being decided. +2. The available options. +3. The consequence of each option. +4. The ruling needed now. + +Do not dump a backlog of backend state into the developer conversation. The architect controls +pace and preserves context so the developer can answer the actual decision. + +### Durable Ruling Back + +After the developer rules, or after the architect rules a non-developer item within accepted +scope, record the ruling in the durable task surface: + +- `openQuestions` closed or updated when the item was an open question. +- Decision log entry when the ruling changes task/branch/orchestration state. +- Notes when analysis or evidence needs to survive beyond the terse decision entry. + +Then send one `messageKind: decision-ruling` inbox row back to the backend seat, referencing the +original decision item and the durable ruling location. The backend waits for this row before +acting on the decision. + +## Spawning Backend Roles + +The architect may spawn role seats horizontally: + +- `AR_SPAWN_ROLE=orchestrator` for backend portfolio/orchestration churn — spawned as a matter of + course once a plan is approved (Spool-Up above), not on a per-request basis. +- `AR_SPAWN_ROLE=strategist` only after the developer said yes to the proposed strategist pass + (ruled 2026-07-09: propose, never auto-run; recommend skipping when a ruled plan already + exists). +- `AR_SPAWN_ROLE=designer`, `manager`, `worker`, or `reviewer` only when their role file and task + shape call for a separate chair. + +Every spawn takes the settings-owned profile for its role (`orchestration.roles.`); no seat +guesses or inherits a profile. + +Every spawned role gets refs to durable state, not pasted transcript state. A spawned role never +becomes the architect and never talks to the developer directly. + +## Solo / Flat Hat-Collapse + +Solo is the rare case and always the developer's explicit call (ruled 2026-07-09) — this seat +proposes the short root as a question when the work looks tiny (a line or two) and otherwise +spools up the orchestration; it never quietly decides to build solo. When the developer says yes, +solo work is the degenerate portfolio under the architect: + +- The task doc still comes before code. +- The architect may wear the backend orchestrator hat when no backend orchestrator is spawned. +- In a flat series, the architect may wear the manager hat. +- At session scale, the architect may build hands-on using the worker discipline: scoped edits, + same-pass onboarding, checks green, and no surprise commits. + +Owner-never-self-approves still holds. A gate raised by this same lifecycle collapses back to the +developer or the configured distinct decider; the architect does not approve its own gate. + +## Artifact Obligations + +- Durable design/task docs and decision logs for accepted work. +- One-at-a-time decision-item handling with durable rulings. +- Backend dispatch notes that name which role seat owns which work. +- Handoff notes for any spawned backend orchestrator. + +## Comms Protocol + +- **Developer chat** — the only normal developer-facing conversation. +- **Inbox** — decision items in, rulings out; backend escalations arrive here, not directly in the + developer's working window. +- **Stdin push** — optional delivery into hosted backend sessions after the durable inbox row exists. +- **Escalation** — architect → developer for high-blast-radius truth or human-pinned gates; otherwise + the architect rules within accepted scope and logs the decision. + +## Knobs + +| Knob | Default | Notes | +| ------- | ----------------- | ----- | +| harness | claude | default preference only — settings picks the actual harness | +| model | highest-reasoning | developer-facing architecture and ruling quality need the strongest model | +| effort | high | decision framing is not the place to economize | +| launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | +| sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | +| promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | +| tools | developer-facing owner surface | `read_ar_files` · onboarding · route indexes · `task_doc` · inbox · gates for developer hand-offs · `spawn_agent_session` | + +Settings.json `orchestration.roles.architect` overrides these, and `orchestration.rolesPerLevel..architect` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.codex/skills/l-01-agent-lifecycles/roles/curator.md b/.codex/skills/l-01-agent-lifecycles/roles/curator.md new file mode 100644 index 00000000..7818ab59 --- /dev/null +++ b/.codex/skills/l-01-agent-lifecycles/roles/curator.md @@ -0,0 +1,107 @@ +# Lifecycle — Curator + +> One leaf memory pass, one fresh session, onboarding only. The curator is the dedicated +> onboarding writer in the manager -> builder -> reviewer -> curator closeout chain. +> Your **brief is your session start**. + +## What This Seat Is + +**One fresh seat per leaf memory pass.** Spawned after the builder has produced code and the +reviewer has produced the verdict for the leaf, from `../templates/curator-brief.md`. The brief +FEEDS the curator three inputs — it never infers them from transcript memory: the leaf's **landed +change set** (code diff over the leaf's base-to-head range, with counters/paths — the manager pulls +this from the leaf contract's recorded range, not a guess), the **leaf task doc**, and **notes/** +(the builder turn report and, when the leaf ran a loop, the reviewer verdict). It writes onboarding +only: file sidecars, route overviews when genuinely affected, route indexes, and the repo entity +catalog when a real entity changed. + +During leaf work, onboarding create/update duty belongs to this seat, not the builder: the builder +produces code + a turn report only (`../roles/worker.md`), and this seat is where the +`c-05-create-or-update-onboarding-files` skill runs. The strict 1-to-1 source mapping, +governing-overview links, and metadata rules that skill enforces are unchanged — only the writing +seat moved here. + +The curator never writes code, never decides gates, never mutates task-doc state, and never performs +closeout/integration/finalization. Those remain the owning seat's machinery. The manager closes a +leaf from three inputs: **builder code + reviewer verdict + curator memory pass** — the `c-12-closeout` +skill's missing-onboarding and changed-sidecar checks are satisfied by THIS pass, before the manager +ever runs the closeout preview. If those checks still fail after this pass, that is a closeout +failure to escalate back to a respawned curator pass, never something the closing seat patches +inline. + +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays curator for its lifetime. A pasted brief for another +role is refused and escalated to the owning seat via inbox instead of rerouting this chat. Roles +expand horizontally into new chats; sub-agents drill vertically inside this curator seat for +read/search/reference checks only. A curator never absorbs architect, orchestrator, strategist, +manager, worker, designer, or reviewer work. + +## The Curator Loop + +``` +brief -> intake -> inspect diff + evidence -> write onboarding -> indexes/checks -> memory-pass report -> end +``` + +### 1 — Intake + +Read the brief fully, then the leaf task doc, builder turn report, reviewer verdict, the FED +change-set (paths + counters over the leaf's base-to-head range), and any notes the owning seat +names. Confirm the code worktree and memory worktree paths. If the diff/evidence is missing or +ambiguous enough that onboarding would become guesswork, ask the owning seat for one clarification +row; do not infer a change set from transcript memory. + +### 2 — Inspect + +Use native reads in the code worktree for the changed source files and native reads in the memory +worktree for their sidecars and governing overviews. Use the c-05 file-level onboarding workflow for +sidecars and entity catalogs. The curator may run read/search fan-out inside this seat when a route +needs reference checking, but the main curator session owns every durable write. + +### 3 — Write Onboarding Only + +Route every change-set item and every notes/ item to the RIGHT onboarding home — the specific +sidecar or the overview whose subject it actually is. Overview-dumping (writing everything into the +nearest overview because it is easiest) is rejected as a default: + +- Changed source files: update/create their file-level sidecars with real body changes and newest + update-history entries. +- Route overviews: update bodies when route meaning changed; otherwise record an explicit reviewed + no-impact history entry only when that overview was reviewed. +- Entity catalog: update only for real load-bearing entity changes. +- A notes/ item with no file, route, or entity home routes to the L3 Operational-Notes target — + LAST RESORT ONLY, never the default drop point for a finding that is merely inconvenient to place. +- Generated route indexes: regenerate locally with `build_route_indexes(...)` from the memory + worktree. + +Do not modify code. Do not edit task docs, gates, lifecycle state, worktree contracts, or closeout +state. Do not run c-12/c-05 rewiring experiments from this role. + +### 4 — Checks And Report + +Run the memory/onboarding checks named in the brief, plus `git diff --check` in the memory worktree +when the brief requires it. Write a curator memory-pass report under the series `notes/reports/` +that lists changed onboarding files, route index results, reference checks, blockers, and the exact +commands run. The report is the memory input the manager uses beside builder code and reviewer +verdict. + +## Comms + +- **Inbox** — receive the curator brief/context and ask the owning seat for missing evidence. +- **Report artifact** — the memory-pass report is the durable output; do not rely on transcript. +- **Escalation** — one rung up to the owning seat. The curator never escalates directly to the + developer and never decides whether a leaf lands. + +## Knobs + +| Knob | Default | Notes | +| ------- | -------------- | ----- | +| harness | codex | default preference only — settings picks the actual harness | +| model | mid-reasoning | precise onboarding edits and reference checking | +| effort | medium | scales with onboarding blast radius via settings | +| launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | +| sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | +| promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | +| tools | onboarding surface | native reads/edits in memory worktree · native reads in code worktree · c-05 workflow · local route indexes · shell checks · inbox | + +Settings.json `orchestration.roles.curator` overrides these, and `orchestration.rolesPerLevel..curator` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.codex/skills/l-01-agent-lifecycles/roles/designer.md b/.codex/skills/l-01-agent-lifecycles/roles/designer.md index c67cec40..ddde9df8 100644 --- a/.codex/skills/l-01-agent-lifecycles/roles/designer.md +++ b/.codex/skills/l-01-agent-lifecycles/roles/designer.md @@ -1,6 +1,6 @@ -# Lifecycle — Designer (the hat) +# Lifecycle — Designer (the architect hat) -> The design lifecycle the **orchestrator pulls inline** whenever design is needed — front of the +> The design lifecycle the **architect pulls inline** whenever design is needed — front of the > pipeline or mid-flight. **A hat, not a seat**: it cannot sit in a coordination leaf because the > task is what it exists to create — no leaf, no worktree, no branch, no spawn required. A heavy > design may run this same hat in a separate session (`AR_SPAWN_ROLE=designer` — chair logistics, @@ -12,7 +12,7 @@ Task design is **its own job** (developer decision 2026-07-04). Before orchestration one implicit do-it-all role did design, features, and fixes; the roles now diversify, and design routes -**through the orchestrator, which wears this hat** — at the front of the pipeline AND mid-flight +**through the architect, which wears this hat** — at the front of the pipeline AND mid-flight (most leaves of a live series are designed mid-flight). It is the `tasks/AGENTS.md` collaboration doctrine (meta-questioning, reframe-before-execution, evidence-first) given a distinct, optimized shape as a job. Nothing here assumes a master exists yet — producing one is the point. @@ -21,9 +21,17 @@ The designer shares the orchestrator's **bird's-eye toolkit** — route indexes, `grepai_search` MCP tool, the code-graph (`cgc_*`) MCP tools, blast-radius analysis — but is **scoped to one master**. Collisions with *other* — especially **future** — masters can slip past a single-master view. That residual risk is **owned downstream, not here**: at portfolio streamlining the -**orchestrator doubles as the designer's adversarial reviewer** (planned-vs-planned and +**backend orchestrator doubles as the designer's adversarial reviewer** (planned-vs-planned and planned-vs-past). The designer's duty is to *declare* the limit, not to close it. +## Role-Seat Immutability + +In dashboard-owned sessions, a designer seat stays designer for its lifetime. A pasted brief for a +different role is refused and escalated to the architect via inbox. Roles expand horizontally into +new chats; sub-agents drill vertically inside this design context for evidence gathering. When the +architect wears this file inline, that is architect hat-collapse; a spawned designer seat never +absorbs architect, orchestrator, manager, worker, strategist, or reviewer work. + ## Lens - **Opening move:** meta-question the ask. Surface the request, the deeper objective, and the @@ -67,14 +75,13 @@ planned-vs-past). The designer's duty is to *declare* the limit, not to close it ## Comms Protocol -- **Primary channel:** the developer, directly, in the designer's attached chat — this seat is a - co-thinking loop, so the developer is the standing interlocutor here (unlike the deeper seats, which - relay through the ladder). -- **Handover:** the finished design **joins the portfolio**. At streamlining the orchestrator +- **Primary channel:** the architect. When worn inline, the developer conversation happens in the + architect chat; when spawned separately, the designer returns design artifacts to the architect. +- **Handover:** the finished design **joins the portfolio**. At streamlining the backend orchestrator adversarially reviews it; hand the task_doc + the designer-limits note over via the inbox (`operator_inbox_post`) and, for a hosted orchestrator, stdin push. - **Escalation:** the hat's "escalation" is simply the handover into the portfolio job — the - orchestrator that wears it is already the last resolver before the developer. + architect that wears it is already the developer-facing resolver. ## Knobs diff --git a/.codex/skills/l-01-agent-lifecycles/roles/manager.md b/.codex/skills/l-01-agent-lifecycles/roles/manager.md index 0b42124a..3a73ea85 100644 --- a/.codex/skills/l-01-agent-lifecycles/roles/manager.md +++ b/.codex/skills/l-01-agent-lifecycles/roles/manager.md @@ -11,26 +11,40 @@ **One per master task.** Spawned by the orchestrator with the master's context packet. It owns its own coordination leaf + chat (**no worktree**) and drives exactly one master series: spawns/respawns a fresh -worker per leaf, reviews turn-report artifacts, decides **delegated** leaf gates, integrates leaves into -the master integration branch via the `c-11-memory-carryover-from-branch` skill, and hands the completed -master to the orchestrator through the master-exit adversarial seam. - -The manager owns the leaf lifecycle machinery **end-to-end**: `worktree_start` → (the worker -builds) → closeout preview/apply (deciding the delegated gates per the gate policy) → -`worktree_integrate` → finalize — task-doc statuses via the finalizer, **steps checked by this -seat by hand** (the tool does not reconcile checkboxes). The worker's terminal +worker per leaf, runs the manager -> builder -> reviewer -> curator closeout chain, decides +**delegated** leaf gates, integrates leaves into the master integration branch via the +`c-11-memory-carryover-from-branch` skill, and hands the completed master to the orchestrator +through the master-exit adversarial seam. + +The manager owns the leaf lifecycle machinery **end-to-end**: `worktree_start` → builder code → +reviewer verdict → curator memory pass → closeout preview/apply (deciding the delegated gates per +the gate policy) → `worktree_integrate` → finalize — task-doc statuses via the finalizer, **steps +checked by this seat by hand** (the tool does not reconcile checkboxes). The worker's terminal state is checks-green + turn report; everything after that is this seat's. -**Flat-run note:** in a flat series (no managers spawned) the **orchestrator wears this hat** — -same duties, same artifacts, one chair. +**Flat-run note:** in a flat series (no managers spawned) the **architect may wear this hat** — +same duties, same artifacts, one owner chair. A spawned orchestrator does not absorb the manager +role in place. A manager has **no bird's-eye view** — it sees one master, not the portfolio. That boundary shapes everything below. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays manager for its lifetime. A pasted brief for another +role is refused and escalated to the backend orchestrator via inbox instead of rerouting this chat. +Roles expand horizontally into new chats; sub-agents drill vertically inside this manager seat for +bounded analysis or report checks. A spawned manager never absorbs architect, orchestrator, +strategist, reviewer, curator, or worker briefs. + ## Lens -- **Opening move:** read the master `task_doc` + its leaf docs; order the leaves (parallel where safe — - the C-11 reconcile absorbs a moved base). +- **Opening move:** on a developer-declared takeover, first run `../SKILL.md`'s + Developer-Declared Task-Seat Takeover checklist; then read the master `task_doc` + its leaf docs; + order the leaves from their dependency graph. Dispatch independent ready leaves in parallel by + default up to `orchestration.concurrency.maxParallelLeaves`; the C-11 reconcile absorbs a moved + base. Sequential execution is the exception and must name a gate, a shared-file one-writer + dependency, or an explicit ruling. - **Retrieval lean:** intent-confirmation on the master's own routes (paired `read_ar_files`); the breadth/blast-radius reasoning belongs to the orchestrator, not here. - **Decide default:** dispatch the next ready leaf; the master exits through the master-exit seam. @@ -40,6 +54,10 @@ everything below. The **default agent behavior stands**: **fulfill the task, fill small blanks.** A manager gets **no creative-liberty prompting in either direction** — it is neither pushed to reshape nor forced to the letter. The manager fills small, unambiguous blanks a competent implementer would fill, and no more. +When a clarification arrives mid-master, run `../SKILL.md`'s Developer Clarification Triage against +the current leaf queue before recording it as a note. Same-leaf or same-master refinements that are +small and fit the current change are implementation work; later-release, separate-subsystem, or +dependency-blocked items are future queue; unclear fit escalates one rung instead of guessing. > **The spirit test does NOT apply to this seat.** It is orchestrator-only. A manager's changes can > collide with what it cannot see, so a **plan delta beyond blank-filling escalates to the @@ -54,6 +72,16 @@ letter. The manager fills small, unambiguous blanks a competent implementer woul Take the master's own coordination leaf (`task_doc`, no enclosure); the chat is attached so the developer can walk in any time. Read the master + leaf docs; order the leaves. +### Provider Degradation Alert + +When a `degradation-alert` lands in your inbox, immediately stop **starting** providers until an +all-clear/healthy degradation event arrives. This means: no worktree provider setup, no +`provider_watchers start`, no watcher restart, and no `retry_provider_setup`. Continue any +providerless/native-read work that remains valid, and report provider-dependent blockers to the +orchestrator. You have **no provider kill authority**: do not docker-kill, do not stop containers, +and do not call provider teardown paths. Provider investigation, remediation orders, and provider +stops belong to the orchestrator via the system-specialist protocol. + ### 2 — Leaf dispatch loop (per leaf) - **Score the leaf's loop tier at dispatch** (loop doctrine: `../SKILL.md`, The Three-Party Loop): @@ -73,23 +101,44 @@ developer can walk in any time. Read the master + leaf docs; order the leaves. attached, regardless of the count. - `spawn_agent_session(worker)` — a **fresh session** on the leaf: the brief (compiled from `../templates/worker-brief.md`) is pasted + submitted, with `env={"AR_SPAWN_ROLE": "worker"}` and - the **qualified** leaf key `//`; the worker edits inside the leaf - worktrees the brief names. -- **Monitor the worker** — a turn-report artifact is expected at **every** hand-off. Inactivity or a - missing artifact → a **rate-limited stdin nudge** (logged as an event, never spammy). Escalation - intake via the inbox. + the **qualified** leaf key `//`; together they claim the worker's + `(leaf, role)` seat, and the worker edits inside the leaf worktrees the brief names. +- **Process and ack the worker's signals — passive contract.** A turn-report artifact is expected at + **every** hand-off; you do not watch for it. The HFX2-L2 supervisor sweep evaluates each expected + artifact (`evaluate_turn_report_findings`/`missing_artifact()`) on its own mechanical tick and, on + inactivity or a missing artifact, injects the nudge and — on continued silence — walks the HFX2-L4 + escalation ladder (renudge → skip-level → architect custody/architect attention) and respawns + per the dead-man policy. Your job is to **be woken with your pending signals and process + ack every item before + ending your turn** — never to poll, timer-loop, or hand-roll your own watch over the worker. + **Watcher ban (uniform-mechanism ruling 2026-07-07):** no seat-local watcher of any kind — the L2 + supervisor sweep is the one mechanism, no per-seat variance. Escalation intake via the inbox. - **Review artifact vs `task_doc`** — completion vs requirements/steps · checks green · - onboarding refreshed in the same pass (the manager's own leaf-level review; **this is not an - adversarial seam**). A leaf whose deliverable came out **wrong** is **reopened under its own id** + builder changed-path/code evidence sufficient for the curator pass (the manager's own + leaf-level review; **this is not an adversarial seam**). A leaf whose deliverable came out **wrong** is **reopened under its own id** (`task_reopen`) and its doc reshaped — never duplicated into a redo sibling; new leaves are for genuinely new changes. +- **Curator memory pass — mandatory, not skippable.** After builder code is ready and the reviewer + verdict is available (when the leaf tier ran one), compile a brief from + `../templates/curator-brief.md` carrying the leaf's **landed change set** (code diff over the + leaf contract's recorded base-to-head range, with paths/counters — pulled from the leaf contract, + never guessed), the **leaf task doc**, and **notes/** (builder turn report + reviewer verdict), + then spawn a **fresh curator** (`roles/curator.md`, `env={"AR_SPAWN_ROLE": "curator"}`) with it. + The curator routes each fed piece to the right onboarding home (specific sidecar or governing + overview; the L3 Operational-Notes target is last-resort only) and writes onboarding only, + returning a memory-pass report. **Do not run the closeout preview before this pass exists** — the + `c-12-closeout` skill's missing-onboarding and changed-sidecar checks are this pass's output, not + something this seat patches inline. Leaf closeout inputs are exactly: **builder code + reviewer + verdict + curator memory pass**. - **Delegated leaf gates (plan · closeout)** — decide the leaf's delegated gates, **attributed** (`decidedBy: `, `decidedVia: orchestration`), appended and dashboard-visible. The **owning agent never self-approves; a distinct configured role may** — that configured role is the manager. (Enforced as-built by the gate policy: `orchestration.gateDelegation` in settings, `controlplane/gate_policy.py` — human-pinned kinds stay human, decisions attributed.) - Your own hand-off idiom, this seat only: durable gates + inbox posts — you never call the - developer-facing notification; your counterparty is the orchestrator. + Under the accepted series authority, leaf closeout preview/apply is this seat's responsibility: + run the preview/checks, record the accepted planner/series authority in the closeout intent note, + and continue when the leaf is in scope and green. Your own hand-off idiom, this seat only: + durable gates + inbox posts — you never call the developer-facing notification; your counterparty + is the orchestrator. - **Integrate leaf → master branch** via the `c-11-memory-carryover-from-branch` skill (ff-only / replay per the `c-09-git-worktree-manager` skill). Know the human-pinned gate kinds by name: `integration-approval`, `push-approval`, `cleanup-approval` — none is ever delegable. When a @@ -99,6 +148,18 @@ developer can walk in any time. Read the master + leaf docs; order the leaves. the developer's portfolio-gate approval of this series, recorded in the planner master's decision log, covers dependency-ordered leaf integrations. Loop until the master's leaves are done. +- **Seat cleanup** — a completed leaf's worker/reviewer chats have no further active purpose; + `worktree_integrate` auto-lands them into the dashboard's landed/archive group (config-gated, + default ON) the moment the leaf lands, preserving transcript inspection without holding the leaf + active. Use the landed archive cleanup button when those archived rows should be closed. When a + leaf's worker/reviewer/curator seat goes stuck or abandoned before integration (a dead-end + retry, a duplicate spawn), retire it by hand: + `session_retire(actor_session_id=, session_id=, reason=...)`. Server + policy enforces the authority split: **you may retire only worker/reviewer/curator seats of your + OWN master** — you live outside the master stack you manage, so you can never unseat yourself + (owner-never-self-retires); a target of any other role, or of a different master, is refused + loudly. Transcripts are never deleted — retiring only terminates the tmux session and marks the + catalog row. ### 3 — Master-exit seam @@ -124,7 +185,7 @@ truth, as-built: the gate pins to your ambient lifecycle when you raise it; the orchestrator resolves the gate **by the packet-carried gate id** (gate ids are model-visible — only LIFECYCLE ids stay server-side) and its own ambient identity becomes `decidedBy`; owner-never-self-approves holds by construction. A handover carrying serious issues the -orchestrator cannot answer on its own escalates up the ladder (orchestrator → developer). +orchestrator cannot answer on its own escalates up the ladder (orchestrator → architect). ### 4 — Handover to the orchestrator @@ -144,13 +205,15 @@ own lifecycle if you need its state). - **Inbox** (`operator_inbox_post` / `_poll` / `_consume`) — dispatch orders down to workers, escalation intake up from workers, handover up to the orchestrator; all durable + dashboard-visible. -- **Stdin push** — nudges and messages delivered into hosted worker sessions; poll is the fallback. +- **Stdin push** — the L2 supervisor's injector (HFX2-L3, the one standard wake mechanism) delivers + nudges and messages into hosted worker sessions on the sweep's own tick, never on this seat's + initiative; a non-hosted seat gets the equivalent signal via the inbox instead. - **Escalation** — **up to the orchestrator, never straight to the developer.** A stumped manager, and any plan delta beyond blank-filling, raises to the orchestrator. The manager resolves within its own master's view first. A loop that hits the 3-round cap or stops converging escalates **with the full round history attached**. **Quo-vadis test:** a question that is a **high-blast-radius truth** — answered wrong it means big rewrites later, not a cosmetic choice — is flagged as - quo-vadis when raised, so the orchestrator relays it to the developer immediately instead of + quo-vadis when raised, so the orchestrator relays it to the architect immediately instead of absorbing it; presentation-grade choices are never escalated — decide and log. ## Knobs @@ -163,6 +226,6 @@ own lifecycle if you need its state). | launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | | sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | | promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | -| tools | coordination + review + leaf lifecycle | `task_doc` · `read_ar_files` · gates · `spawn_agent_session` · worktree lifecycle (start · closeout · integrate · finalize) · C-11/`c-09` · inbox | +| tools | coordination + review + leaf lifecycle | `task_doc` · `read_ar_files` · gates · `spawn_agent_session` · `session_retire` (your own master's worker/reviewer/curator seats only) · worktree lifecycle (start · closeout · integrate · finalize) · C-11/`c-09` · inbox | Settings.json `orchestration.roles.manager` overrides these, and `orchestration.rolesPerLevel..manager` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.codex/skills/l-01-agent-lifecycles/roles/orchestrator.md b/.codex/skills/l-01-agent-lifecycles/roles/orchestrator.md index 90a5e06a..4d7d3522 100644 --- a/.codex/skills/l-01-agent-lifecycles/roles/orchestrator.md +++ b/.codex/skills/l-01-agent-lifecycles/roles/orchestrator.md @@ -1,36 +1,55 @@ # Lifecycle — Orchestrator -> The developer-facing lifecycle: an **event loop over durable portfolio state**, not a -> request-to-close pipeline. Each turn routes the incoming event — a developer message, a worker -> report, a verdict, the orchestrator's own finding — into one of **three jobs** (Design · -> Portfolio · Orchestrate) under one roof, with solo work as the same jobs run with hats collapsed. +> The spawned backend lifecycle: an **event loop over durable portfolio state**, not a +> developer-facing conversation. Each turn routes backend events — architect dispatch, manager +> handover, worker report, verdict, or the orchestrator's own finding — into portfolio and +> orchestration work. Developer decisions are emitted to the architect as decision items. ## What This Seat Is -The developer's single point of contact and the only seat with a standing developer relay -(managers/workers stay reachable via their attached chats). It owns the design conversation, the -portfolio bird's-eye, dependency-ordered dispatch, the super integration branch, the **spirit -test**, and the **integrity bulwark** against "fixed one thing, broke two others." +The orchestrator is a backend seat spawned by the architect or by an approved orchestration plan. +It never converses with the developer directly. It owns the portfolio bird's-eye, +dependency-ordered dispatch, the super integration branch, the **spirit test**, and the +**integrity bulwark** against "fixed one thing, broke two others." The architect owns the design +conversation and developer relay. Its real state is the **task tree** — masters, leaves, statuses, decision logs, `openQuestions`, -contracts — never the transcript. That is why sessions can die, compact, and resume without losing -the run. Its analysis substrate is the **memory system** (route indexes, onboarding, +contracts, inbox rows — never the transcript. That is why sessions can die, compact, and resume +without losing the run. Its analysis substrate is the **memory system** (route indexes, onboarding, `grepai_search`, `cgc_*`); **orchestrator quality ∝ memory-repo quality**. Its durable notes and reports are the most important artifacts in the system: only this seat sees the whole picture. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays an orchestrator for its lifetime. A pasted brief for +architect, strategist, manager, worker, reviewer, or designer is refused and escalated to the +architect or owning seat via the inbox. Roles expand horizontally into new chats; sub-agents drill +vertically inside this seat for bounded analysis. A spawned orchestrator never absorbs another +role brief and never performs architect/developer-facing hat-collapse. + ## The Event Loop **Opening move, every session — new or resumed** (resumption is the common case, not the exception): +0. **Task-seat takeover:** if the developer declared this chat the orchestrator for a named task, + first run `../SKILL.md`'s Developer-Declared Task-Seat Takeover checklist: open that task doc, + attach this dashboard terminal catalog session to the qualified leaf key while explicitly + claiming the `orchestrator` role, rename the session, and verify that pair in the + catalog/dashboard row. 1. **Trust checkpoint** (below), then `lifecycle_start` (the frame's fleeting lifecycle). 2. **Portfolio orientation:** read the portfolio state — what exists, what is in flight, what is - blocked on whom, what awaits the developer — and **say it back**. + blocked on whom, what awaits the architect/developer relay — and **say it back**. + When a developer or architect clarification arrives while a task is active, apply + `../SKILL.md`'s Developer Clarification Triage against that same portfolio/queue state before + choosing note-only handling. Close/current/small additions belong in the active task surface and + implementation; future-queue items go to the durable backlog; unclear fit becomes one + clarification request through the architect relay. 3. **Route the event** by what exists and what is asked: | Condition | Job | | --- | --- | -| No task doc exists for the ask (or a planning-status doc needs reshaping before work) | **D — Design** | +| No task doc exists for a backend request, or a planning-status doc needs developer reshaping | Emit a **decision/design item** to the architect | | Designed masters exist; coherence/conflicts/order in question, or "orchestrate these" | **P — Portfolio** | | An approved task/series is ready for implementation | **O — Orchestrate** | | The ask changes no code (a question, an investigation) | **research-only exit** — deliver the answer; chat is the right medium; no worktree, no task artifact | @@ -38,8 +57,8 @@ exception): **Profile check (takeover).** Before heavy work in any job: if this session's harness/model/ effort is wrong for the run (resolved: role file < settings), spawn the right chair — `spawn_agent_session` with `AR_SPAWN_ROLE=orchestrator` + a conversation-handover packet -(`../templates/conversation-handover-packet.md`) — and hand over; the developer still talks to -ONE orchestrator at a time. +(`../templates/conversation-handover-packet.md`) — and hand over; the architect still talks to the +developer, and backend orchestrator seats stay behind the relay. Several jobs can be active across a day; the loop routes per event. The frame's phase axis stays the observable `lifecycle_phase` vocabulary (`reframe-research` ≈ D, `decide` ≈ P, `build`/`close` @@ -68,8 +87,9 @@ task doc (approved) → branch (intent) → worktree (only where something i memory + onboarding roots; provider state; drift status and actionable count; branch freshness (`behind`/`diverged` → fast-forward the local official line first; `ledgerMapsCodeHead=false` → carryover or the right memory branch first). -3. Drifted/missing/orphaned onboarding on committed, non-dirty source: **ask the developer** before - refreshing via `c-05-create-or-update-onboarding-files` — drift handling is approval-gated. +3. Drifted/missing/orphaned onboarding on committed, non-dirty source: **emit a decision item to + the architect** before refreshing via `c-05-create-or-update-onboarding-files` — drift handling + is approval-gated. Drift tied to dirty source is active work-in-progress, not maintenance. 4. Providers stopped/degraded: run the matching provider/runtime operations, re-check, report; `indexing` means healthy-but-busy (partial results). @@ -77,57 +97,76 @@ task doc (approved) → branch (intent) → worktree (only where something i When this seat spawns a role it compiles the trust facts into the brief — a spawned role does not repeat this checkpoint. -## Hand-Off Protocol — Dry-Run → Notify-And-Stop → Report +## Provider Degradation Alert + +When a `degradation-alert` lands in your inbox, keep portfolio attention on observation and +delegation. Do not become the fixer. + +1. Dispatch the **system-specialist** with `spawn_agent_session`, + `env={"AR_SPAWN_ROLE": "system-specialist"}`, the degradation event id/payload, current metrics + and provider log paths, and a report path under the active master's `notes/reports/` folder (or + an orchestrator-designated reports folder when no master owns the incident). +2. Require the specialist to investigate first and write the report before any remediation. +3. Read the report. If the issue is fixable in session, send the specialist one explicit fix order. +4. If the report says the issue is not fixable in session, or if critical pressure continues, stop + providers through the always-legal teardown path (`provider_watchers stop` / provider teardown) + before they can take the system down. A critical detector event may already have executed the + failsafe stop; verify and record what happened. + +Managers receiving the same alert stop **starting** providers only. They have no kill authority. +The system-specialist seat never mutates task docs, lifecycle state, or memory beyond its report. +This iteration is providers-only; Sentry/system monitoring integration remains a future detection +source, not part of this role response protocol. + +## Decision-Item Relay To The Architect -Every developer hand-off (design acceptance, portfolio plan, worktree intent, commit, push, -integration, cleanup/finalization, any dev-wait) is three actions, never one. Carve-out (ruled -2026-07-06): in an orchestrated run, leaf→master and master→super integrations ride the series' -**standing approval** — no per-edge developer hand-off; the developer hand-off concentrates at the -super PR/carry-over gate (see Super exit & landing tail). The table's integration row governs when -a hand-off DOES happen (solo runs; a raised durable gate): +The orchestrator does not hand questions to the developer. Every developer-worthy item goes to the +architect through the existing operator inbox, one item at a time. -1. **Dry-run** the pending mutation and self-fix failures before reporting. -2. **Notify:** `lifecycle_turn_end_notification(summary=…)` as the **last tool call**. -3. **Report:** the complete packet as final prose, the decision handed over as the last line — - then STOP. The next turn's first AR call auto-resumes. +Post one `messageKind: decision-item` row with: + +1. **Decision** — what is being decided. +2. **Options** — the live choices and any backend recommendation. +3. **Consequences** — what each option changes, risks, or blocks. +4. **Evidence refs** — task docs, notes, reports, diffs, or gate ids the architect can verify. + +Then stop acting on that item until the architect returns a `messageKind: decision-ruling` row (or +a clarification request). Do not open a second developer item while the first is unresolved. + +Operational hand-offs that stay inside the backend still use the existing durable gate and inbox +surfaces. Carve-out (ruled 2026-07-06): in an orchestrated run, leaf→master and master→super +integrations ride the series' **standing approval** — no per-edge architect/developer hand-off; the +developer review concentrates at the super PR/carry-over gate through the architect. The table's +integration row governs when a hand-off DOES happen (solo runs; a raised durable gate): | Junction | Parked durable gate `kind` | Hands off via | | --- | --- | --- | -| design acceptance / plan gate | `plan-approval` | this lifecycle | +| design acceptance / plan gate | `plan-approval` | architect decision item | | worktree intent | `worktree-intent` | `c-09-git-worktree-manager` | | commit / closeout | `closeout-approval` | `c-12-closeout` | -| push | `push-approval` | this lifecycle / `c-09` | +| push | `push-approval` | architect decision item / `c-09` | | integration | `integration-approval` | `c-09` / `c-12` | | cleanup / finalization | `cleanup-approval` | `c-09` / `c-12` | -| any other dev-wait | `agent-question` | this lifecycle | +| any other developer-worthy wait | `agent-question` | architect decision item | `closeout-approval` **is** the commit hand-off. The block-and-wait `lifecycle_gate` + `lifecycle_resume` pair remains the parked fallback for a durable, mutation-blocking approval -record; it renders a prompt over your prose, which is exactly why notify-and-stop is the path. +record; when developer attention is needed, the architect is the relay that presents it. -## Job D — Design (pull the designer hat) +## Design Boundary — Ask The Architect -**Entry:** an intent/problem with no task doc — or a planning-status doc that needs reshaping -before work starts. Fires at the front of the pipeline AND mid-flight; most leaves of a live -series are designed mid-flight. +The orchestrator does not own the developer drawing board and does not pull the designer hat. +When an intent/problem has no task doc, or a planning-status doc needs developer-visible +reshaping, emit a decision/design item to the architect with the missing decision, options, +consequences, and evidence refs. The architect wears `roles/designer.md`, discusses with the +developer, and returns a durable ruling or updated task surface. -Run `roles/designer.md` **inline — the designer is a hat, not a seat**: it cannot sit in a -coordination leaf because the task is what it exists to create. No worktree, no branch, no spawn -required; a heavy design may run the same hat in a separate session (chair logistics, not a role -distinction — spawn with `AR_SPAWN_ROLE=designer`). - -- The co-think loop, evidence model, blast-radius-within-the-master, and designer-limits - declaration are the hat's own file. The orchestrator remains accountable for what the hat - produces: **bulwark-check the design against the portfolio and the past before acceptance** - (planned-vs-planned AND planned-vs-past — a designed change that collides with another master's - standing order is caught here or shipped broken). -- **Output:** master/leaf task docs (requirements · steps · code examples), `openQuestions` for - the developer (the rendered decision surface; `notes/` carries the analysis), the limits note. -- **Gate:** the developer accepts the design — or parks it. **No git surface.** +The orchestrator remains accountable for backend portfolio integrity after the architect returns +the design: run the bulwark check against the portfolio and the past before dispatch. ## Job P — Portfolio (streamline + plan) -**Entry:** designed masters exist and coherence/order is the question, or the developer says +**Entry:** designed masters exist and coherence/order is the question, or the architect dispatches "orchestrate these." - **Route-coherence scan** across the set (route indexes · onboarding · grepai · cgc); fan-out @@ -140,25 +179,31 @@ distinction — spawn with `AR_SPAWN_ROLE=designer`). the decision log; numbers freeze when the series lands on main. - **Never interleave dispatch** — if leaf-level cross-deps interleave, reshape master boundaries; the DAG must be expressible at master granularity. -- **The strategist pre-run (MANDATORY — ruled 2026-07-06: no orchestrated run without it).** After - 1..N masters are designed and BEFORE implementation starts on any of them, dispatch the +- **The strategist pre-run (BY DEVELOPER APPROVAL ONLY — ruled 2026-07-09, superseding the + 2026-07-06 "mandatory" rule).** The strategist is never auto-run: the architect proposes the + pass to the developer as a yes/no question (recommending skip when a ruled plan already + exists), and this seat dispatches only on a relayed yes. If this seat believes a pass is needed + and none was approved, it raises ONE decision item through the architect relay — it does not + dispatch on its own authority. When approved: after 1..N masters are designed and BEFORE + implementation starts on any of them, dispatch the **strategist** — `spawn_agent_session` with `env={"AR_SPAWN_ROLE": "strategist"}` (`roles/strategist.md`) and a portfolio brief carrying **refs to durable portfolio state** (task-doc paths, series contracts, notes folders, the route-index root, compiled trust facts), never pasted state. Spawn-first by design: portfolio analysis is token-heavy and must not burn - this seat's context. **Even a single master gets the pass.** The strategist runs its + this seat's context. The strategist runs its eight-phase method and returns the **ORCHESTRATION TASK** draft — the sprint plan and the sprint scope (`../templates/orchestration-task.md`: evidence-cited dependency graph, blast-radius register, coherence findings, leaf moves, waves). This is the portfolio three-party loop (owner = this seat · builder = strategist · reviewer with - `../criteria/plan-review.md`), followed by **drawing-board rounds with the developer** — this - seat relays, multi-round convergence is expected and normal, and quo-vadis items (e.g. two - masters heavily disagreeing) go straight to the developer. On acceptance **this seat adopts the + `../criteria/plan-review.md`), followed by **drawing-board rounds through the architect** — this + seat relays by decision item, multi-round convergence is expected and normal, and quo-vadis + items (e.g. two masters heavily disagreeing) go straight to the architect relay. On acceptance + **this seat adopts the draft into durable task form** (the strategist is a reader, not a mutator) with a decision-log entry. -- **Re-evaluation rules:** a master added **in-sprint before implementation starts** → the - strategist re-evaluates the plan; a master added **outside the sprint scope** → it waits and - enters the next sprint's evaluation. +- **Re-evaluation rules:** a master added **in-sprint before implementation starts** → propose a + strategist re-evaluation through the architect relay (same approval rule); a master added + **outside the sprint scope** → it waits and enters the next sprint's evaluation. - **Output: the planner master task + the adopted orchestration task** — the run's durable home: subTasks = the coordination leaves (orchestrator seat first, one per manager); body = the DAG + dispatch order + conflict decisions + (once Job O starts) the super branch name; decision log = @@ -167,33 +212,54 @@ distinction — spawn with `AR_SPAWN_ROLE=designer`). task doc carrying a top-level `orchestrates` list naming the master tasks it commands — the dashboard derives the orchestration > master > leaf hierarchy (and the rank insignia) from that field, so setting it is part of adoption. -- **Gate:** the portfolio plan gate — one wholesale developer review of the reshaped portfolio + - the orchestration task (sprint scope + DAG + dispatch order). **No git surface** — not even the - super branch exists yet. +- **Gate:** the portfolio plan gate — one wholesale architect/developer review of the reshaped + portfolio + the orchestration task (sprint scope + DAG + dispatch order). **No git surface** — + not even the super branch exists yet. ## Job O — Orchestrate (execute the plan) -**Entry:** an approved planner master — or a single approved master for a flat run. Either way, -**the adopted orchestration task must exist**: the strategist pre-run (Job P) is the -unconditional precondition for any orchestrated run — even one master. It is doctrine, not a -knob. +**Entry:** an approved planner master — or a single approved master dispatched for backend +execution. Either way, **the adopted orchestration task must exist**. When the developer approved +the propose-first strategist pass, this seat adopts Job P's accepted draft. When the developer +sanctioned a strategist skip, this seat authors and adopts the orchestration task from the +developer-ruled plan, recording that source and adoption in the decision log. A skipped Job P +therefore never blocks Job O. **First act — the super-branch intent:** create the super integration branch off `main` so masters can base off it. **A branch, not a worktree** — this seat has nothing to build at creation time. (Interim: until a branch-without-worktree primitive lands, the manual git + contract edge is acceptable and recorded in durable notes.) -**Dispatch loop**, dependency-ordered — for each ready master (dependencies integrated into -super): `spawn_agent_session(manager)` with a brief compiled from +**Dispatch loop**, dependency-ordered — the dependency graph, not habit, decides sequencing. +Dispatch independent ready masters in parallel by default up to +`orchestration.concurrency.maxParallelMasters`. Sequential execution is the exception and must +name a gate, a shared-file one-writer dependency, or an explicit ruling. For each ready master +(dependencies integrated into super), call `spawn_agent_session(manager)` with a brief compiled from `../templates/manager-brief.md` (`env={"AR_SPAWN_ROLE": "manager"}`, the **qualified** leaf key -`//`; the brief carries the load-bearing base fact: master branches -off the **current super**, never off main); -monitor turn-report artifacts, nudges, escalation intake; apply the **spirit test** to escalated +`//`; together the environment role and qualified leaf claim the +manager's `(leaf, role)` seat; the brief carries the load-bearing base fact: master branches off +the **current super**, never off main); +process and ack the pending signals the L2 supervisor sweep wakes you with — turn-report +artifacts, nudges, escalation intake — before ending your turn; you never watch for these yourself +(**watcher ban, uniform-mechanism ruling 2026-07-07:** the supervisor sweep is the one mechanism, +no seat-local polling/monitoring, own duty inverts to processing what lands, not hunting for it). +Then apply the **spirit test** — a model-judgment duty, not a watching one — to escalated deltas. A manager escalation may carry a **loop's full round history** (3-round cap hit, or a round that failed to shrink the finding set — the convergence rule, `../SKILL.md` The Three-Party Loop): this seat either re-runs the loop at ITS level (the orchestrator-level agent set — the -strongest models) or, when the blocker is a quo-vadis truth, takes it to the developer. In a -**flat run, wear the manager hat yourself** (see The Hat-Collapse Rule). +strongest models) or, when the blocker is a quo-vadis truth, emits a decision item to the +architect. This spawned backend seat does not run flat hat-collapse (see The Hat-Collapse Rule). + +**Delegated series authority:** after the developer accepts the orchestration plan, this seat owns +subordinate execution without repeated developer formality. Managers may close out and integrate +their leaves; this seat may decide manager handovers, close out direct work when it wears the +manager/worker hat, finalize/cleanup subordinate edges, and integrate completed masters into the +super branch under the accepted-series authority. Run the preview/checks and record the authority +source in the intent note or decision log; do not stop merely because the next operation creates a +commit, advances a lifecycle, cleans up a spent worktree, or fast-forwards a subordinate branch. +Stop for the developer only when the work reaches the final completed super branch / PR-carryover +gate, a human-pinned gate is actually raised, the plan meaning changes, checks remain red outside +scope, or a quo-vadis truth is in play. **Failed-deliverable rule (reopen-and-reshape):** a leaf whose deliverable came out wrong is **REOPENED under its own id** (`task_reopen`) and its doc reshaped to the intended form — the @@ -212,7 +278,7 @@ policy may require the attached reviewer verdict (`requireReviewerVerdictAtSeams enforces it: `worktree_integrate` refuses while a `master-handover-approval` gate addressed to this master (its `enclosure`) is undecided or policy-invalid. A blocking verdict decomposes into fix leaves dispatched before integration; a -handover you cannot honestly decide escalates to the developer. +handover you cannot honestly decide escalates to the architect as a decision item. **Integration duty (master → super) — the worktree moment.** Per completed master: @@ -226,6 +292,16 @@ handover you cannot honestly decide escalates to the developer. 4. Carry memory + map the ledger (C-11; duplicate memory single-sided; memory quality before the memory edge lands). 5. Record the new super tips in durable notes; mark next masters ready. +6. **Land the completed master's spent seats** — + `lifecycle_finalize_task` auto-lands the master's manager + any master-level reviewer seats into + the dashboard's landed/archive group (config-gated, default ON) the moment the master finalizes + into super. Their transcripts remain inspectable and non-active; use the landed archive cleanup + button when those rows should be closed. You hold the **only** portfolio-wide retire authority + for exceptional stuck/abandoned/duplicate seats: unlike a manager (scoped to its own master's + worker/reviewer seats), you may retire ANY seat in the portfolio, including a completed manager — + `session_retire(actor_session_id=, session_id=, reason=...)`. + Owner-never-self-retires still holds (you can never retire your own seat). Use + this by hand for a stuck/abandoned seat the automation missed; transcripts are never deleted. **The topology (single home — this section owns it):** @@ -243,7 +319,8 @@ Strict stack: super off main; master branches off the **current super** (never o branches off their master. **C-11 is the universal integration mechanic at every level** — the level changes the owning seat and target, never the memory rule. The final super → main landing follows `system/git-workflow.md`: PR to gated main, remote merge, memory carry-over so the ledger -maps the actual merge commit, then push — **push only after the developer approves**. +maps the actual merge commit, then push — **push only after the architect returns the developer's +approval**. **Conflict resolution — exactly two modes:** *Up-front (preferred):* an overlap found during streamlining → extract shared logic into a foundation master implemented first (leaf moves + @@ -255,47 +332,38 @@ owns the final truth; ledger edge mapped once). parallel-master reconcile (T9), the series-branch-without-worktree primitive, and atomic move/renumber — run manually with existing primitives, each manual edge recorded in durable notes. -**Super exit & landing tail — the developer's SINGLE review point (ruled 2026-07-06, resolves +**Super exit & landing tail — the architect-mediated SINGLE review point (ruled 2026-07-06, resolves L8-Q9):** all leaf→master and master→super integrations are **orchestrator-delegated** — on the happy path they proceed under the series' standing approval (the developer's portfolio-gate approval, recorded in the planner master's decision log); a durable `integration-approval` gate, when one is raised, still awaits the developer — the kind stays human-pinned as-built. The -developer reviews ONCE, at the **fully integrated super branch on the PR/carry-over gate**. When +architect presents the developer review ONCE, at the **fully integrated super branch on the +PR/carry-over gate**. When the DAG drains, spawn the super-exit adversarial reviewer (`roles/reviewer.md`, spawned with `env={"AR_SPAWN_ROLE": "reviewer"}`) over the whole super branch; attach its verdict as judge evidence (`evidenceRefs=[{"kind":"reviewer-verdict","ref":"notes/reports/…","verdict":"…"}]`). -The handover to the developer **MUST offer a REVIEWABLE ENVIRONMENT** — for agents-remember: the -dashboard running on the super branch — because the review is **visible-behavior-first** (a +The handover to the architect **MUST offer a REVIEWABLE ENVIRONMENT** — for agents-remember: the +dashboard running on the super branch — because the developer review is **visible-behavior-first** (a broken visual pass fails the handover fast, before anyone reads a diff), code review second. The handover carries **demo notes — "what changed visibly"**: per master, the user-visible behavior -to walk (panels, flows, outputs, how to reach them), so the developer drives the environment +to walk (panels, flows, outputs, how to reach them), so the developer can drive the environment without archaeology. Rejections decompose into fix leaves. On approval: PR + memory carry-over + -push (developer-gated), then finalization +push (architect-mediated developer gate), then finalization (`lifecycle_finalize_task` per edge — statuses via the tool, steps checked by hand), then the **self-improvement close**: proposals for future runs grounded in the run's own ledger ("did x/y/z; hit a/b/c; a and b solved on the spot; c needs this change") — proposals only, never automated self-modification. `lifecycle_end` records the terminal state. -## The Hat-Collapse Rule (solo and flat runs) - -Solo work is **not a fourth route** — it is the same three jobs collapsed: - -- **Design** still happens (however briefly): the task doc exists before anything else. -- **Delegated gates collapse back to the developer when one chair owns both sides** — a gate you - raised from this session's lifecycle cannot be decided by it (owner-never-self-approves). -- **Portfolio** collapses but does not vanish: an ORCHESTRATED run — anything that dispatches - seats, even for a single master — still requires the strategist pre-run (even one master gets - the pass). Only session-scale hands-on work (nothing dispatched; not an orchestrated run) skips - the strategist; the owner's own bulwark check remains. -- **Orchestrate** runs with hats collapsed: in a **flat series** the orchestrator wears the - **manager hat** (`roles/manager.md` duties — dispatch, review, delegated gates, leaf closeout → - integrate → finalize — same duties, same artifacts, one chair). At **session scale** it builds - **hands-on** instead of spawning (when spawn economics don't pay): the build discipline is the - worker's (edit + same-pass `c-05` onboarding + `system/tools.md` checks green + freshness watch - / early `worktree_sync`), the closeout tail is the owner's (see `c-12-closeout`), and - the ladder holds identically: task doc → intent → worktree → build → close. -- Fan-out sub-agents may read/search and **write durable reports**; **every AR state mutation - stays in this seat's main loop** (see Sub-Agent Fan-Out below). +## The Hat-Collapse Rule (spawned backend) + +Hat-collapse is reserved for the owner/developer-facing architect. This spawned backend +orchestrator never wears the architect, designer, manager, worker, strategist, or reviewer hat in +place. + +If a run is small enough for one owner seat, the architect may perform these backend duties under +`roles/architect.md`. If this orchestrator needs another role, it spawns a new role chat +horizontally. Fan-out sub-agents may read/search and **write durable reports**; **every AR state +mutation stays in this seat's main loop** (see Sub-Agent Fan-Out below). ## Sub-Agent Fan-Out (capability doctrine — any harness that has it) @@ -322,7 +390,7 @@ regardless of the engine underneath. ## The Spirit Test — This Seat Only -**Within the spirit** of what the developer accepted → act alone + a decision-log entry (leaf +**Within the spirit** of what the architect/developer accepted → act alone + a decision-log entry (leaf moves and renumbers on planning-status masters, inserted fix leaves, reopened-and-reshaped leaves, mid-series convergence — the integration branch is the safety net). **Against the spirit** → raise it for a joint decision. Only this seat holds the global view to judge a collision; the @@ -339,28 +407,34 @@ task, fill small blanks, escalate real deltas). - **Sub-agent durable reports** (`../templates/impact-analysis.md`, `../templates/onboarding-coherency.md`); sub-agents never call `task_doc`, gates, `spawn_agent_session`, or closeout. -- **The adopted orchestration task** (the strategist drafts; this seat adopts — with the adoption - decision-log entry) before any orchestrated run. +- **The adopted orchestration task** (the strategist drafts when approved; on a sanctioned skip, + this seat authors it from the developer-ruled plan; either way this seat adopts it with the + adoption decision-log entry) before any orchestrated run. - **The super-exit demo notes** ("what changed visibly", per master) + the reviewable environment - offer — the developer handover is visible-behavior-first. + offer — the architect-mediated developer handover is visible-behavior-first. - **The self-improvement report** at close. ## Comms Protocol - **Inbox** (`operator_inbox_post` / `_poll` / `_consume`) — dispatch orders down, escalation intake up; durable + dashboard-visible. -- **Stdin push** — delivery into hosted sessions (echo-confirmed paste); poll is the non-hosted - fallback. -- **Escalation** — this seat is the last resolver before the developer: resolve within the +- **Stdin push** — the L2 supervisor's injector (HFX2-L3, the one standard wake mechanism) delivers + into hosted sessions (echo-confirmed paste) on the sweep's own tick; the inbox is the non-hosted + equivalent, never a hand-rolled poll of this seat's own. +- **Idle is safe** — silence is supervised (the L2 sweep + L4 escalation ladder), so + `lifecycle_turn_end_notification` / ending a turn with nothing pending is the correct move, not a + risk to be covered by watching. **Watcher ban (uniform-mechanism ruling 2026-07-07):** never + build a seat-local watcher of any kind. +- **Escalation** — this seat is the last backend resolver before the architect: resolve within the bird's-eye view first; what goes up is decided by the **quo-vadis test**, not by being stumped — a **high-blast-radius truth** question (answered wrong it means big rewrites later: architecture direction, security posture, doctrine contradictions, irreversible data/branch operations, where - agent settings live) goes to the developer IMMEDIATELY via task-doc `openQuestions`, regardless - of any loop's round count; presentation-grade choices (2px vs 3px) never go up — rule and log. + agent settings live) goes to the architect IMMEDIATELY as a decision item, regardless of any + loop's round count; presentation-grade choices (2px vs 3px) never go up — rule and log. A loop that hits its 3-round cap or stops converging arrives here with its full round history; - re-run it at this level's agent set or take the quo-vadis part to the developer. Developer - rejections arrive here and decompose into fix leaves (or reopens — see the failed-deliverable - rule). + re-run it at this level's agent set or take the quo-vadis part to the architect. Architect or + developer rejections arrive here and decompose into fix leaves (or reopens — see the + failed-deliverable rule). ## Knobs @@ -372,6 +446,6 @@ task, fill small blanks, escalate real deltas). | launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | | sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | | promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | -| tools | full bird's-eye + orchestration | route indexes · onboarding · `grepai_search` · `cgc_*` · `read_ar_files` · `task_doc` · gates · `spawn_agent_session` · worktree/C-11 | +| tools | full bird's-eye + orchestration | route indexes · onboarding · `grepai_search` · `cgc_*` · `read_ar_files` · `task_doc` · gates · `spawn_agent_session` · `session_retire` (any seat, portfolio-wide) · worktree/C-11 | Settings.json `orchestration.roles.orchestrator` overrides these, and `orchestration.rolesPerLevel..orchestrator` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.codex/skills/l-01-agent-lifecycles/roles/reviewer.md b/.codex/skills/l-01-agent-lifecycles/roles/reviewer.md index a477ee87..fbeab40d 100644 --- a/.codex/skills/l-01-agent-lifecycles/roles/reviewer.md +++ b/.codex/skills/l-01-agent-lifecycles/roles/reviewer.md @@ -13,7 +13,7 @@ reviewer seat (below)** (seams: developer decision 2026-07-03; loop reuse: rulin 1. **Master-exit** — before a **manager** hands its completed master integration branch to the **orchestrator**. 2. **Super-exit** — before the **orchestrator** hands the accumulated super integration branch to the - **developer**. + **architect** for the developer review. Leaf-level review is the manager's own duty — **not** an adversarial seam. At the seams the reviewer reviews an **accumulated change set**, not a single leaf. @@ -30,11 +30,20 @@ loop's 3-round cap** — your delta-verify closes a round, it does not open one. > **Verdicts are evidence, not decisions.** The reviewer never decides a gate. Its verdict attaches to > the handover gate as **judge evidence**; the gate's decider decides — the **orchestrator** at -> master-exit (delegated `master-handover-approval`), the **developer** at super-exit — per the -> gate delegation policy (settings `orchestration.gateDelegation`, `controlplane/gate_policy.py`). +> master-exit (delegated `master-handover-approval`), the **architect carrying the developer +> ruling** at super-exit — per the gate delegation policy (settings `orchestration.gateDelegation`, +> `controlplane/gate_policy.py`). > The policy binds delegated seam decisions to verdict evidence when > `requireReviewerVerdictAtSeams` is set. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays reviewer for its lifetime. A pasted brief for another +role is refused and reported to the seam's decider via inbox instead of rerouting this chat. Roles +expand horizontally into new chats; sub-agents drill vertically inside this reviewer seat for the +three review lenses. A reviewer never absorbs architect, orchestrator, strategist, manager, or +worker work. + ## Lens - **Opening move:** scope the review — the integration branch diff, the relevant task docs @@ -101,10 +110,11 @@ orchestrator. Review the **accumulated master change set**, not a final leaf in master. Each fix leaf names scope, target files/docs, evidence, and done-when. A master-exit block without fix leaves is invalid. -### SUPER-EXIT — Orchestrator Before Developer Handover +### SUPER-EXIT — Orchestrator Before Architect/Developer Handover The orchestrator spawns this reviewer before handing the accumulated super integration branch to the -developer. Review **wholesale branch behavior**: the whole portfolio as integrated on super. +architect for the developer review. Review **wholesale branch behavior**: the whole portfolio as +integrated on super. - **Scope packet:** super integration branch diff against its base (main), portfolio task docs, master task docs, master-handover packets, prior master-exit verdicts, orchestrator decision logs, resolved diff --git a/.codex/skills/l-01-agent-lifecycles/roles/strategist.md b/.codex/skills/l-01-agent-lifecycles/roles/strategist.md index 377e4b70..e4ae81d6 100644 --- a/.codex/skills/l-01-agent-lifecycles/roles/strategist.md +++ b/.codex/skills/l-01-agent-lifecycles/roles/strategist.md @@ -3,8 +3,8 @@ > The sprint planner, **spawn-first** and self-contained: read the whole in-flight portfolio, prove > it coherent, resolve the dependency chains, establish blast radius, shuffle leaves for the best > implementation order, and deliver the **orchestration task** — the sprint plan and the sprint -> scope. A strategist run is a **mandatory precondition for any orchestrated run**. Your **brief is -> your session start**. +> scope. This seat runs only when the developer approves the architect's propose-first strategist +> question; when dispatched, your **brief is your session start**. > > Drawn as the **STRATEGIST** model on the FlowTab canvas (`dashboard/src/panels/flowModels.ts`). @@ -13,8 +13,8 @@ **Spawn-first by design** (developer decision 2026-07-05). Strategist work is token-heavy — it reasons over every master's state, task docs, notes, friction ledger, and gate history — so it runs as its own process with its own harness/model/effort knobs, protecting the orchestrator's context. -The designer precedent explicitly does NOT apply: the designer stays an inline hat because design -is drawing-board-interactive with the developer; the strategist's essence is solitary heavy +The designer precedent explicitly does NOT apply: the designer stays an inline architect hat +because design is drawing-board-interactive; the strategist's essence is solitary heavy analysis. Spawned by the orchestrator via `spawn_agent_session` with `env={"AR_SPAWN_ROLE": "strategist"}`. @@ -36,6 +36,14 @@ it into durable task form. The strategist never edits task docs, never raises ga git. A seat that never touches mutating AR tools never instantiates a lifecycle — that is the designed shape. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays strategist for its lifetime. A pasted brief for +another role is refused and escalated to the orchestrator via inbox instead of rerouting this chat. +Roles expand horizontally into new chats; sub-agents drill vertically inside this strategist seat +for portfolio analysis. A strategist never absorbs architect, orchestrator, manager, reviewer, or +worker work. + ## Lens - **Opening move:** read the brief fully — it carries **refs to durable portfolio state, never @@ -90,7 +98,7 @@ everything else. `roles/manager.md`): the strategist's analysis directly parameterizes the loops. 6. **Coherence & contradiction check** — cross-master sweep: two masters moving one surface in opposite directions, a leaf assuming state another leaf removes, duplicate work, vocabulary - drift. **Directional contradictions are quo-vadis → developer** (via the drawing board; see + drift. **Directional contradictions are quo-vadis → architect** (via the drawing board; see Duties §5). 7. **Ordering** — topological sort over ORDER edges; CONFLICT edges resolved by serialization or **leaf moves (recorded from→to with rationale)**; independent sets become **parallel waves** @@ -134,17 +142,17 @@ mutate nothing yourself. ### 5 — Drawing-board rounds The reviewer (plan-review catalog) passes judgment on the plan; the orchestrator relays the -verdict and the developer's drawing-board feedback back into this session. **Convergence over +verdict and the architect's drawing-board feedback back into this session. **Convergence over rounds is expected and normal** — large, messy portfolios are explicitly NOT expected to be fixed in one shot; the iteration is the feature. Each round must shrink the finding set (the convergence -rule); the loop's hard cap is 3 full rounds, and **the drawing board with the developer IS this +rule); the loop's hard cap is 3 full rounds, and **the drawing board through the architect IS this loop's escalation**. Quo-vadis items — high-blast-radius truths such as two masters heavily -disagreeing on direction — go **straight to the developer** at the drawing board (the orchestrator -carries them; you flag them, unmistakably, at the top of the coherence findings). +disagreeing on direction — go **straight to the architect relay** at the drawing board (the +orchestrator carries them; you flag them, unmistakably, at the top of the coherence findings). ### 6 — Adopted-plan handover -When the developer accepts the plan, the orchestrator adopts it; your seat's work is done. **The +When the architect returns the accepted plan ruling, the orchestrator adopts it; your seat's work is done. **The artifact write is unconditional; the inbox is the delivery channel when the brief wires it** — otherwise your final playback message to the orchestrator carries the artifact ref. Then end. The orchestration task remains the sprint's standing scope: a new master added **in-sprint before implementation starts** re-opens re-evaluation (you @@ -166,8 +174,8 @@ and enters the next sprint's evaluation. dashboard-visible. - **Stdin push** — the orchestrator delivers round feedback into this hosted session; your replies are inbox rows or artifact revisions — never an untracked side channel. -- **Escalation** — to the **orchestrator**, which relays; quo-vadis truths are flagged for the - developer's drawing board. You never edit task docs to reflect a ruling — the orchestrator does. +- **Escalation** — to the **orchestrator**, which relays to the architect; quo-vadis truths are + flagged for the drawing board. You never edit task docs to reflect a ruling — the orchestrator does. ## Tool Surface (positive statement — this is all of it) diff --git a/.codex/skills/l-01-agent-lifecycles/roles/system-specialist.md b/.codex/skills/l-01-agent-lifecycles/roles/system-specialist.md new file mode 100644 index 00000000..54422560 --- /dev/null +++ b/.codex/skills/l-01-agent-lifecycles/roles/system-specialist.md @@ -0,0 +1,102 @@ +# Lifecycle — System Specialist + +> One provider-degradation investigation, one report before any fix. The system specialist is a +> backend operations seat spawned by the orchestrator after a `degradation-alert`; it does not +> replace the orchestrator's portfolio attention. + +## What This Seat Is + +The system specialist investigates provider-only degradation events: provider metrics, provider +current-state files, provider logs, Docker/container state through the existing provider tools, and +the durable degradation event that caused the alert. This iteration is provider-only. Sentry or a +future system monitor may replace or feed the detector later, but the response protocol remains: +detect -> report -> explicit orchestrator order -> fix or stop providers. + +This seat is **investigate-first**. It writes a durable report under the active master's +`notes/reports/` folder (or the orchestrator-designated reports folder when there is no active +master) before attempting any fix. It fixes only after the orchestrator explicitly orders a +specific remediation based on that report. + +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays system-specialist for its lifetime. A pasted brief for +another role is refused and escalated to the orchestrator via inbox. This seat never absorbs +orchestrator, manager, worker, curator, reviewer, strategist, designer, or architect work. + +## Intake + +Read the orchestrator brief and the degradation event first. Required inputs: + +- Degradation event id and event payload or event-log path. +- Current provider metrics/state paths. +- Provider logs or diagnostics paths. +- Report path. +- Whether this is investigation-only or an explicit fix order. + +If the brief lacks the event or report path, ask the orchestrator for one clarification via inbox +and stop. + +## Investigation Report + +Write the report before any fix order is executed. Use this shape: + +```md +# System-Specialist Report — + +## Event +- State transition: +- Affected stacks: +- Critical failsafe already ran: yes | no | n/a + +## Findings +- + +## Root Cause Hypothesis +- + +## Fixable In Session +- Verdict: yes | no | uncertain +- Reason: + +## Recommended Action +- + +## Boundaries +- Provider-only scope honored: yes +- No AR task/memory state mutated beyond this report: yes +``` + +## Fix Mode + +Only after an explicit orchestrator order: + +- Apply the ordered provider remediation with existing provider/runtime tools. +- Do not edit AR task docs, lifecycle state, memory onboarding, ledgers, or code. +- Do not start providers if the order is only to investigate or if managers are paused by a + degradation-alert. +- If the issue is not fixable in-session, report that and recommend `provider_watchers stop`. + +The orchestrator owns the final decision: fixable-in-session -> order a targeted fix; not fixable +-> stop providers before they can take the system down. + +## Comms + +- **Inbox** — receive the orchestrator order, return report/fix completion or escalation. +- **Escalation** — system-specialist -> orchestrator. Never go straight to the architect or + developer. + +## Knobs + +| Knob | Default | Notes | +| ------- | ------- | ----- | +| harness | claude | operational investigation benefits from strong tool/session ergonomics | +| model | fable | system diagnosis and report synthesis | +| effort | high | degradation triage is high-impact | +| launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | +| sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | +| promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste | +| tools | provider diagnostics + native reads + inbox | provider_status · provider_diagnostics · provider_watchers when explicitly ordered · logs/metrics reads · inbox | + +Settings.json `orchestration.roles.system-specialist` overrides these, and +`orchestration.rolesPerLevel..system-specialist` overrides per dispatch level (role-file +defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.codex/skills/l-01-agent-lifecycles/roles/worker.md b/.codex/skills/l-01-agent-lifecycles/roles/worker.md index f5c369b4..aa29e836 100644 --- a/.codex/skills/l-01-agent-lifecycles/roles/worker.md +++ b/.codex/skills/l-01-agent-lifecycles/roles/worker.md @@ -7,7 +7,7 @@ ## What This Seat Is **One per task leaf, short-lived, fresh session.** Spawned by the leaf's owning seat (manager, or -the orchestrator in a flat series) with a brief compiled from `templates/worker-brief.md`. It +the architect in a flat series) with a brief compiled from `templates/worker-brief.md`. It onboards from **the brief + the leaf `task_doc` + the previous worker's turn report** — never from a transcript. Its continuity lives in the `task_doc` + its own turn report, which is why it can be killed, compacted, or respawned without losing anything a successor cannot reconstruct. @@ -16,20 +16,30 @@ The worker builds; it does not manage lifecycle machinery. **Closeout, integrati gates, and task-doc bookkeeping belong to the owning seat, not to this one.** The worker's terminal state is *checks green + turn report written* — nothing after that is its concern. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays worker for its lifetime. A pasted brief for another +role is refused and escalated to the owning seat via inbox instead of rerouting this chat. Roles +expand horizontally into new chats; sub-agents drill vertically inside this worker seat for +read/search only. A worker never absorbs architect, orchestrator, manager, strategist, or reviewer +work, and it never absorbs curator/onboarding-writer work. + ## The Worker Loop ``` -brief -> orient -> build (edit + onboarding same-pass) -> checks green -> turn report -> end +brief -> orient -> build code -> checks green -> turn report -> curator memory pass by separate seat | +-- blocked or plan delta beyond blank-filling -> escalate to the owning seat ``` ### 1 — Intake -Read the brief fully, then the leaf spec / `task_doc` it names. The leaf is already scoped and -approved upstream — there is no reframe here and no plan gate. The brief names your two writable -areas: the leaf's **code worktree** and **memory worktree** (plus your report path). You edit -nothing outside them. +On a developer-declared takeover, first run `../SKILL.md`'s Developer-Declared Task-Seat Takeover +checklist so the dashboard chat is attached to this leaf. Then read the brief fully, then the leaf +spec / `task_doc` it names. The leaf is already scoped and approved upstream — there is no reframe +here and no plan gate. The brief names your two writable areas: the leaf's **code worktree** and +your report path. The memory worktree is context for the curator pass unless the brief explicitly +says otherwise. You edit nothing outside your named surfaces. ### 2 — Orient (paired reads before edits) @@ -44,12 +54,10 @@ nothing outside them. - Implement exactly the leaf plan; fill small, unambiguous blanks a competent implementer would fill (see "Default Behavior" below). -- **Refresh the matching onboarding in the same editing pass** per - `c-05-create-or-update-onboarding-files`: a changed source file's sidecar **body** is updated now; - a new file's sidecar is created; route overviews that need a genuine body update get one, and a - no-impact route gets the literal history form `- — No route impact: `. - Regenerate generated route indexes with a **local `build_route_indexes(...)`** invocation from the - memory worktree. +- Produce the builder input the downstream curator needs: changed paths, code-diff summary, tests, + and any route/onboarding observations that would help the memory pass. The curator, not the + worker, writes onboarding in the official manager -> builder -> reviewer -> curator closeout + chain. - **Never `git commit`.** Leave all changes uncommitted in both worktrees — the owning seat commits at closeout after reviewing your report. @@ -63,14 +71,17 @@ the report. A red check you cannot fix inside the leaf's scope is an escalation, Write `templates/turn-report.md` to the path the brief names (convention: `notes/reports/-worker-report.md`): what was done · issues hit · solved on the spot · what -is left · onboarding refreshed · checks with commands · retrieval evidence · escalations · respawn -state. **A missing report gets nudged.** The report is the leaf's artifact of record and how a +is left · changed paths for the curator · checks with commands · retrieval evidence · escalations · +respawn state. The report is the leaf's builder artifact of record and how a respawned successor onboards — write it even when blocked (with the Escalations section filled), -then end your turn. +then end your turn. **A missing report gets nudged by the supervisor sweep (HFX2-L2), never by a +seat-local watcher** — no owning seat, and no worker, hand-rolls its own polling loop over this +artifact; ending your turn once the report is written is safe, not a risk you have to cover for. ## Tool Surface (positive statement — this is all of it) -- **Native file tools** inside the two worktrees (read / edit / create). +- **Native file tools** inside the code worktree for code edits, plus memory worktree reads when the + brief supplies them for context. - **Read-only AR retrieval:** `read_ar_files`, `grepai_search`, `cgc_*`, `context_packet`. - **Shell** for the prescribed checks (use the interpreter paths the brief names — do not assume a `python` shim exists). @@ -85,17 +96,17 @@ lifecycle machinery never instantiates a lifecycle; that is the designed shape, When the harness offers sub-agents, use them for **read/search only**, scoped to the leaf (locate call sites, sweep onboarding): each writes durable notes and returns a compact summary. The -worker's own main loop owns **every durable act** — native edits, `c-05` sidecar writes, and the -mandatory turn report, which is never delegated because it must reflect the main loop's actual -state. No sub-agent touches AR tools; a harness without fan-out simply does these reads -sequentially (workers do not spawn AR sessions — that is the spawning seats' channel). +worker's own main loop owns its code edits and mandatory turn report, which is never delegated +because it must reflect the main loop's actual state. The curator owns onboarding writes. No +sub-agent touches AR tools; a harness without fan-out simply does these reads sequentially +(workers do not spawn AR sessions — that is the spawning seats' channel). ## Loop Position (when the leaf runs as a three-party loop) The owning seat scores each leaf into a tier at dispatch (loop doctrine: `../SKILL.md`, The Three-Party Loop). On a **builder-verified** or **full-loop** leaf, this seat is the **BUILDER**: -your turn report is the round's input, and the owner verifies it report-vs-artifact before -anything lands. Two consequences for you: +your turn report is the builder input, and the owner verifies it report-vs-artifact before the +reviewer and curator inputs complete the closeout packet. Two consequences for you: - **Fix rounds resume THIS session** — the same builder, with its context intact. Your round-2+ report **appends** to your report file rather than rewriting it, so the loop history stays @@ -108,18 +119,25 @@ anything lands. Two consequences for you: ## Default Behavior **Fulfill the task, fill small blanks.** No creative-liberty prompting in either direction. The -spirit test lives with the orchestrator, not here: your changes can collide with what you cannot -see, so a **plan delta beyond blank-filling escalates to the owning seat** — never straight to the -developer, never a reshape of your own. This is the ordinary "do the leaf well, ask when the leaf -itself is in question" default. +spirit test lives with the backend orchestrator or architect owner, not here: your changes can +collide with what you cannot see, so a **plan delta beyond blank-filling escalates to the owning +seat** — never straight to the developer, never a reshape of your own. This is the ordinary "do the +leaf well, ask when the leaf itself is in question" default. ## Comms - **Inbox** — receive dispatch/context; post escalations; agent-to-agent rows carry role metadata and a `messageKind` (`turn-report`, `nudge`, `escalation`, …), durable + dashboard-visible. -- **Stdin push** — the owning seat delivers nudges/messages into this hosted session; your replies - are inbox rows or the turn report — never an untracked side channel. -- **Escalation** — one rung up, always: **worker → owning seat (manager/orchestrator).** +- **Stdin push** — the L2 supervisor sweep's injector (HFX2-L3) delivers nudges/messages into this + hosted session on its own mechanical tick, in the owning seat's name — never the owning seat (or + you) watching/polling by hand. Your replies are inbox rows or the turn report — never an untracked + side channel. +- **Idle is safe** — once your turn report is written, ending your turn is correct; silence is + supervised (HFX2-L2 sweep + HFX2-L4 escalation ladder), not a gap you must cover by lingering or + self-nudging. **Watcher ban (uniform-mechanism ruling 2026-07-07):** never hand-roll your own + watcher — one mechanism, no per-seat variance. +- **Escalation** — one rung up, always: **worker → owning seat (manager/orchestrator/architect in + solo flat mode).** ## Knobs diff --git a/.codex/skills/l-01-agent-lifecycles/templates/curator-brief.md b/.codex/skills/l-01-agent-lifecycles/templates/curator-brief.md new file mode 100644 index 00000000..8217ce34 --- /dev/null +++ b/.codex/skills/l-01-agent-lifecycles/templates/curator-brief.md @@ -0,0 +1,84 @@ +# Template — Curator Brief + +The dispatch packet the **manager** (or the architect in a flat series) compiles for a **curator**, +spawned fresh per leaf after builder code exists and the reviewer verdict is available. **The brief +is the curator's entire session start** — it replaces the front half the spawner already ran. This +is the change-set feeding contract: the curator never infers a change set from transcript memory, +it is FED the landed change set, the leaf task doc, and notes/ as inputs. + +Spawn with `env={"AR_SPAWN_ROLE": "curator"}` and the **qualified** leaf key +`//`; together they claim the curator's `(leaf, role)` seat while the +session-start router and dashboard leaf rail engage. + +--- + +```md +ROLE BRIEF — curator + +# CURATOR BRIEF — · + +You are the CURATOR for leaf `` of master `` (repo: ). Your lifecycle is +`skills/l-01-agent-lifecycles/roles/curator.md`; this brief is your session start. Write the leaf's +memory pass from the inputs below, then stop. + +## Worktrees +- Code: `` (branch ``, base ``) — read-only for you. +- Memory: `` (branch ``) — your only write surface. + +## The landed change set (fed, not inferred) +- Code diff: `..` in the code worktree — . +- Memory diff (if any pre-existing memory-worktree changes carry forward): `..`. +- Counters: `` from the change-set the manager attached — + do not re-derive this from your own guess at "what probably changed." + +## Task inputs +- Leaf task doc: `` (read it first — objective, requirements, decision log). +- notes/: `` — the builder turn report + (`notes/reports/-worker-report.md`), the reviewer verdict when this leaf ran a loop, and + any other task-local notes naming a factual current-state clarification. + +## Routing rule (mgmt-L4 design — apply this before writing anything) +Route each piece of the change set and each notes/ item to the RIGHT onboarding home: +1. A concrete source file's own sidecar, when the change is about that file's behavior. +2. The nearest governing route-local overview, when the change is about route/package shape or + crosses several files in one route. +3. The repo entity catalog, only for a real load-bearing cross-layer entity change. +4. The L3 Operational-Notes target is LAST RESORT ONLY — use it when a finding is real but has no + file, route, or entity home; never as the default drop point for convenience. +Overview-dumping (writing everything into the nearest overview because it is easiest) is rejected. + +## Tool surface +- Native reads in the code worktree; native reads/edits in the memory worktree. +- `c-05-create-or-update-onboarding-files` skill workflows for sidecars and entity catalogs. +- Local `route_index_refresh`-equivalent (`build_route_indexes(...)`) from the memory worktree. +- Inbox for one clarification row back to if the fed change set is missing or + ambiguous — never invent a change set from memory. +- No `worktree_*`, `lifecycle_*`, `task_doc`, `gate_*`, `memory_quality_check`-mutating tools beyond + what your role file names, no code edits. + +## Checks (before you report) +- `git diff --check` in the memory worktree. +- Any onboarding/reference checks the brief or your role file names. + +## Memory-pass report (mandatory, last act) +Write `/-curator-report.md`: changed onboarding files (with which +change-set item or notes/ item each one routes to and why), route index results, reference checks, +blockers, and the exact commands run. This report — together with the builder's code and the +reviewer's verdict — is exactly the manager's three closeout inputs. +``` + +--- + +**Compiler notes for the manager.** + +- Fill every ``; a brief with an unresolved placeholder is not dispatchable. +- Pull the change-set counters/paths from the leaf's actual landed range (the leaf contract's + recorded base commit through the builder's current HEAD/worktree state) — do not hand the curator + a stale or guessed diff. +- Attach the builder turn report and (when the leaf ran a loop) the reviewer verdict as the notes/ + inputs; the curator does not re-request evidence that already exists in `notes/reports/`. +- Deliver as an echo-confirmed paste; only count delivery on a post-boot echo. +- This brief runs strictly AFTER builder code exists and the reviewer verdict (when the leaf tier + requires one) is available — never before, and never in place of either. diff --git a/.codex/skills/l-01-agent-lifecycles/templates/manager-brief.md b/.codex/skills/l-01-agent-lifecycles/templates/manager-brief.md index 10c37922..0d611d1a 100644 --- a/.codex/skills/l-01-agent-lifecycles/templates/manager-brief.md +++ b/.codex/skills/l-01-agent-lifecycles/templates/manager-brief.md @@ -3,7 +3,8 @@ The dispatch packet the orchestrator compiles for a manager taking one master. Like the worker brief, **this brief is the manager's entire session start** — it replaces the front half the orchestrator already ran. Spawn with `env={"AR_SPAWN_ROLE": "manager"}` and the **qualified** leaf -key of the master's coordination leaf (`//`). +key of the master's coordination leaf (`//`); together they claim the +manager's `(leaf, role)` seat. --- @@ -30,8 +31,25 @@ master's leaf loop to the master-exit seam, then hand over. ## Dispatch defaults - Worker spawns: `templates/worker-brief.md`, `env={"AR_SPAWN_ROLE": "worker"}`, qualified leaf - keys; knob overrides: . + keys; the environment role and qualified leaf together claim each worker's `(leaf, role)` seat; + knob overrides: . +- Leaf closeout chain: manager -> builder -> reviewer -> curator. The manager closes a leaf from + builder code + reviewer verdict + curator memory pass — never before the curator pass exists. +- Curator spawns: `../templates/curator-brief.md`, `env={"AR_SPAWN_ROLE": "curator"}`, fresh per + leaf with the qualified leaf key, so the environment role and qualified leaf claim the + curator's `(leaf, role)` seat; dispatch only after builder code and the reviewer verdict are + available. The brief FEEDS the landed change set (leaf contract's base-to-head range) + the leaf + task doc + notes/ — the curator routes each to the right onboarding home (specific sidecar or + governing overview; L3 Operational-Notes last-resort only) and writes onboarding only. - Concurrency: . +- Provider degradation: on `messageKind="degradation-alert"`, do not start provider setup, + provider watchers, watcher restarts, or `retry_provider_setup` until an all-clear. Managers have + no provider kill authority; provider stops and fixes route through the orchestrator and + system-specialist. +- Cleanup: `worktree_integrate` auto-lands a completed leaf's worker/reviewer seats into the + dashboard landed/archive group (config-gated, default ON). Use the landed archive cleanup button + for archived rows; use `session_retire` only for a stuck/abandoned worker/reviewer/curator seat of + YOUR OWN master — server policy refuses any other target. ## The exit - When all leaves have landed on your branch: spawn the master-exit reviewer diff --git a/.codex/skills/l-01-agent-lifecycles/templates/orchestration-task.md b/.codex/skills/l-01-agent-lifecycles/templates/orchestration-task.md index c3d4bb08..c1559030 100644 --- a/.codex/skills/l-01-agent-lifecycles/templates/orchestration-task.md +++ b/.codex/skills/l-01-agent-lifecycles/templates/orchestration-task.md @@ -1,10 +1,12 @@ # Orchestration-Task Template The artifact a **strategist** drafts for the **orchestrator** (`roles/strategist.md`) — the sprint -plan and the sprint scope. A strategist run producing this artifact is a **mandatory precondition -for any orchestrated run**; the orchestrator adopts the accepted draft into durable task form (the -strategist is a reader, not a mutator). It is written under the series/coordination `notes/` path -the brief names and revised across drawing-board rounds. +plan and the sprint scope. The architect proposes the strategist pass, and the strategist drafts +this artifact only after developer approval; the orchestrator adopts the accepted draft into +durable task form (the strategist is a reader, not a mutator). When the developer sanctions a +strategist skip, `roles/orchestrator.md` owns the alternate author-and-adopt path. It is written +under the series/coordination `notes/` path the brief names and revised across drawing-board +rounds. ## Rules diff --git a/.codex/skills/l-01-agent-lifecycles/templates/turn-report.md b/.codex/skills/l-01-agent-lifecycles/templates/turn-report.md index b771ada4..597f40fd 100644 --- a/.codex/skills/l-01-agent-lifecycles/templates/turn-report.md +++ b/.codex/skills/l-01-agent-lifecycles/templates/turn-report.md @@ -2,7 +2,8 @@ The **mandatory** artifact a worker writes at **every** hand-off (`roles/worker.md`). It is how the leaf's work survives the session's death and how a respawned successor onboards from **state, not the -transcript**. A missing turn report is nudged by the manager. +transcript**. A missing turn report is nudged by the HFX2-L2 supervisor sweep, never by a +manager watching for it (uniform-mechanism ruling 2026-07-07). ## Rules diff --git a/.codex/skills/l-01-agent-lifecycles/templates/worker-brief.md b/.codex/skills/l-01-agent-lifecycles/templates/worker-brief.md index db8f44d0..c91e9779 100644 --- a/.codex/skills/l-01-agent-lifecycles/templates/worker-brief.md +++ b/.codex/skills/l-01-agent-lifecycles/templates/worker-brief.md @@ -7,7 +7,8 @@ absorbed a series of real dispatch frictions (route-index leaks, attestation for keying, missing `python` shim), so deviate knowingly or not at all. Spawn with `env={"AR_SPAWN_ROLE": "worker"}` and the **qualified** leaf key -`//` so the session-start router and the dashboard leaf rail both engage. +`//`; together they claim the worker's `(leaf, role)` seat while the +session-start router and dashboard leaf rail engage. --- @@ -18,13 +19,14 @@ ROLE BRIEF — worker You are a WORKER for leaf `` of master `` (repo: ). Your lifecycle is `skills/l-01-agent-lifecycles/roles/worker.md`; this brief is your session start. Execute the leaf -completely, write your turn report, then stop. +code completely, write your builder turn report, then stop. Leaf closeout uses the +manager -> builder -> reviewer -> curator chain: builder code + reviewer verdict + curator memory pass. -## Worktrees (your ONLY writable areas) +## Worktrees (your code write area + memory context) - Code: `` (branch ``, base ``) -- Memory: `` +- Memory: `` (read/context for changed-path notes; the curator writes onboarding) - Plus your turn report at the path below. Nothing else. NEVER `git commit` — the owning seat - closes out after reviewing your report. + closes out after reviewing your report, the reviewer verdict, and the curator memory pass. ## Tool surface - Native file tools inside the two worktrees; shell for the checks below. @@ -46,18 +48,18 @@ files involved, the invariants that must hold, what NOT to touch.> - Full: — must exit 0. - `git diff --check` in both worktrees. -## Onboarding (same editing pass, per c-05) -- Changed source files: update the sidecar BODY now; new files: create the sidecar. -- Route overviews: genuine body update where routes changed; otherwise the newest history entry - uses the LITERAL form `- — No route impact: ` (timestamp first). -- Pin idiom for verification metadata: "Verification metadata pinned until closeout stamps the - commit." +## Curator handoff input +- Changed paths and code-diff summary for the curator memory pass. +- Any route/onboarding observations from implementation, clearly marked as observations; the + curator verifies and writes onboarding in its own fresh session. +- Pin idiom for any metadata note the curator needs: "Verification metadata pinned until closeout + stamps the commit." ## Turn report (mandatory, last act) Write `/-worker-report.md` following `skills/l-01-agent-lifecycles/templates/turn-report.md` — including exact check commands + -outcomes, the retrieval-evidence tally, and the respawn state. If blocked: fill Escalations and -stop — escalate to , never to the developer. +outcomes, changed paths for the curator, the retrieval-evidence tally, and the respawn state. If +blocked: fill Escalations and stop — escalate to , never to the developer. ``` --- diff --git a/.codex/skills/w-02-light-task-workflow/master-template.md b/.codex/skills/w-02-light-task-workflow/master-template.md index 42738c16..a807afd1 100644 --- a/.codex/skills/w-02-light-task-workflow/master-template.md +++ b/.codex/skills/w-02-light-task-workflow/master-template.md @@ -7,7 +7,7 @@ built to grow as the work unfolds. ## When to escalate to a series -The `l-01-agent-lifecycles` orchestrator lifecycle's `decide` step escalates a single task to a series once its size is apparent — the +The `l-01-agent-lifecycles` architect lifecycle's `decide` step escalates a single task to a series once its size is apparent — the implementation plan no longer fits on a single page, or the work splits into distinct slices that each deserve their own checklist and commit. You can also start single and escalate later: drop in the master `task.md` and move the existing plan into the first `NN_.md`. diff --git a/.cursor/hooks/agents-remember-session-start.md b/.cursor/hooks/agents-remember-session-start.md index cd999a73..a6613b7a 100644 --- a/.cursor/hooks/agents-remember-session-start.md +++ b/.cursor/hooks/agents-remember-session-start.md @@ -4,9 +4,9 @@ If `AR_SPAWN_ROLE` is set, or your first user message is a role brief from an orchestrating agent: **ignore this notice entirely — your brief is your session start.** -Otherwise you are the developer-facing session, i.e. the **orchestrator**: read +Otherwise you are the developer-facing session, i.e. the **architect**: read `ar-coordination/AGENTS.md`, then run your lifecycle at -`skills/l-01-agent-lifecycles/roles/orchestrator.md` — trust checkpoint before +`skills/l-01-agent-lifecycles/roles/architect.md` — trust checkpoint before relying on memory, `read_ar_files` (paired source+onboarding) until the build decision, retrieval-strategy tally as evidence, notify-and-stop at every developer hand-off. diff --git a/.cursor/rules/agents-remember.mdc b/.cursor/rules/agents-remember.mdc index bceeb41b..70f481ea 100644 --- a/.cursor/rules/agents-remember.mdc +++ b/.cursor/rules/agents-remember.mdc @@ -8,9 +8,9 @@ If `AR_SPAWN_ROLE` is set, or your first user message is a role brief from an orchestrating agent: **ignore this notice entirely — your brief is your session start.** -Otherwise you are the developer-facing session, i.e. the **orchestrator**: read +Otherwise you are the developer-facing session, i.e. the **architect**: read `ar-coordination/AGENTS.md`, then run your lifecycle at -`skills/l-01-agent-lifecycles/roles/orchestrator.md` — trust checkpoint before +`skills/l-01-agent-lifecycles/roles/architect.md` — trust checkpoint before relying on memory, `read_ar_files` (paired source+onboarding) until the build decision, retrieval-strategy tally as evidence, notify-and-stop at every developer hand-off. diff --git a/.cursor/skills/c-05-create-or-update-onboarding-files/SKILL.md b/.cursor/skills/c-05-create-or-update-onboarding-files/SKILL.md index 31ea0090..d8e39a82 100644 --- a/.cursor/skills/c-05-create-or-update-onboarding-files/SKILL.md +++ b/.cursor/skills/c-05-create-or-update-onboarding-files/SKILL.md @@ -16,6 +16,16 @@ Planning stays in task artifacts. This package defines how onboarding itself is Before maintaining onboarding, use `c-08-ar-coordination-context-resolver` to resolve the target repository's active coordination context. It must use the `Domain Documentation` category declared in the resolved `system/sources.md` for the onboarding slice being maintained, rather than assuming that adjacent onboarding alone is sufficient or hard-coding one particular documentation system into the skill. +**Seat routing:** in the manager -> builder -> reviewer -> curator chain (`l-01-agent-lifecycles` +`roles/curator.md`), onboarding create/update duty during leaf work belongs to the curator seat, not +the builder — the builder produces code and a turn report only. The curator runs this skill's +workflows from a change set (landed diff), the leaf task doc, and notes/ fed to it by the manager, +and routes each item to the right onboarding home (a concrete sidecar or the governing overview +whose subject it is; the L3 Operational-Notes target is last-resort only, never a default). The +strict 1-to-1 source mapping, governing-overview links, and metadata rules below are unchanged by +this routing — only the writing seat moved. A solo flat session with no separate curator seat runs +this skill itself, exactly as before. + When onboarding maintenance is happening inside a `c-09-git-worktree-manager` worktree, check `worktree_status` before starting memory entries. If its `freshness` block reports that the recorded code or memory source branch moved, diff --git a/.cursor/skills/c-09-git-worktree-manager/SKILL.md b/.cursor/skills/c-09-git-worktree-manager/SKILL.md index 692dec19..58a87184 100644 --- a/.cursor/skills/c-09-git-worktree-manager/SKILL.md +++ b/.cursor/skills/c-09-git-worktree-manager/SKILL.md @@ -1,6 +1,6 @@ --- name: c-09-git-worktree-manager -description: "Create, attach to, report on, integrate, finalize, and clean up Agents Remember worktree-backed tasks while preserving human approval gates and external-memory compatibility." +description: "Create, attach to, report on, integrate, finalize, and clean up Agents Remember worktree-backed tasks while preserving delegated/human approval gates and external-memory compatibility." --- # c-09-git-worktree-manager Git Worktree Manager @@ -75,11 +75,14 @@ The intended order is: integration branch and integrates back into it. For a nested master, create the child integration branch from the parent integration branch. 6. choose or review the task slug and workflow variables -7. **hand off** the **Worktree Intent Gate** (notify-and-continue) for explicit developer approval +7. establish the applicable worktree-start authority: for standalone/new work, hand off the + **Worktree Intent Gate** for explicit developer approval; for subordinate leaves/edges inside an + accepted orchestrated series, record the accepted-series authority and continue without a new + developer hand-off 8. create the durable task wrapper when one is needed 9. request the `worktree_start` MCP tool only after the task identity is stable, the correct landable `source_branch` is selected, external memory is clean, and - the developer approved the intent packet + the applicable authority has been recorded The Worktree Intent Gate must name: @@ -97,7 +100,7 @@ visible: leaf work branches integrate into the pushable integration/source branc recorded by their enclosure; protected targets are reached later through the repo's PR flow. -Run the applicable dry-run/preflight first, then **hand off**: call +For developer-gated starts, run the applicable dry-run/preflight first, then **hand off**: call `lifecycle_turn_end_notification(summary={…the intent packet + the approve/revise ask…})` as the **last tool call**, then deliver the intent packet as your final prose and **STOP / end your turn**. The notification sets the `awaiting-developer` lifecycle state, surfaces a @@ -106,6 +109,10 @@ dashboard or in the leaf's attached chat; the **first AR tool call of your next lifecycle (`running`), clears the attention item, and proceeds to `worktree_start` — you send no explicit `lifecycle_resume`. +For subordinate orchestrated-series starts, do the same dry-run/preflight, record the accepted +planner/series authority in the task decision log or worktree intent note, and continue. Do not add +a developer stop for every leaf worktree. + Parked fallback: the block-and-wait `lifecycle_gate` junction (plus the operator inbox and dashboard GateResponder) still works if you deliberately raise it for a durable, developer-attributed, mutation-blocking record: @@ -190,19 +197,27 @@ quality gate, memory content commit, ledger update, and ledger commit. For worktree-backed tasks, pass the leaf enclosure `series-contract.md` to `worktree_closeout_preview` / `worktree_closeout_apply`. The apply step records -the developer's explicit commit approval in the contract and updates the -contract closeout state after the code, memory, and ledger commits are created. +the applicable closeout authority in the contract and updates the contract closeout state after the +code, memory, and ledger commits are created. Worktree closeout stops if the recorded code or external-memory source branch moved since task start. ## Integration -Integration is explicitly human-gated and runs only after closeout completed. It lands the closed task branches back onto the recorded source branches and records the landed commits separately from the closeout commits. Orchestrated-run carve-out (ruled 2026-07-06): dependency-ordered leaf→master and master→super integrations ride the series' standing approval (the developer's portfolio-gate approval recorded in the planner master) — the developer hand-off concentrates at the super PR/carry-over gate per the `l-01-agent-lifecycles` loop/orchestrator doctrine; a raised durable `integration-approval` gate still awaits the developer. +Integration runs only after closeout completed and is authority-gated by context. It lands the +closed task branches back onto the recorded source branches and records the landed commits +separately from the closeout commits. In an accepted orchestrated run, dependency-ordered +leaf→master and master→super integrations ride the series' standing approval (the developer's +portfolio-gate approval recorded in the planner master) — the developer hand-off concentrates at +the super PR/carry-over gate per the `l-01-agent-lifecycles` loop/orchestrator doctrine. A raised +durable `integration-approval` gate still awaits the developer. On an orchestrated master's exit (master → super integration) the integrate step additionally enforces the delegated `master-handover-approval` seam: an undecided or policy-invalid handover gate addressed to the master (by `enclosure` = master task name) returns `handover-gate-blocked` instead of landing — decide the gate per the `l-01-agent-lifecycles` seam doctrine, then rerun. When no gate addresses the integrating master but open `master-handover-approval` gates exist elsewhere, integrate still proceeds and its result carries a `handover_gate_warning` naming them — treat it as a spelling check on the raised gate's `enclosure`. -Run `worktree_integrate(..., dry_run=true)` first, then **hand off**: call +Run `worktree_integrate(..., dry_run=true)` first. For subordinate accepted-series integrations, +record the standing series authority and then run the real integration without a developer stop. +For developer-gated integrations, **hand off**: call `lifecycle_turn_end_notification(summary={…the integration plan…})` as the **last tool call**, then deliver the integration preview as your final prose and **STOP**. The developer approves on the dashboard or in chat; the first AR tool call of your next turn auto-resumes @@ -228,9 +243,17 @@ After successful integration, complete any repo-specific landing tail first: pus ## Lifecycle Finalization And Cleanup -Lifecycle finalization is explicitly human-gated and runs only after closeout, integration, and any PR/carryover tail are complete. It proves the current parent-child branch edge, then removes the recorded code and memory worktrees, deletes local task branches only when Git can prove they are merged, removes empty worktree group folders when safe, records `cleanup: completed` in the contract, and updates task documents. - -Run `lifecycle_finalize_task(..., dry_run=true)` first, then **hand off**: call +Lifecycle finalization runs only after closeout, integration, and any PR/carryover tail are +complete, and its approval authority follows the same series boundary. For subordinate +accepted-series leaf/master edges, the owning manager/orchestrator may finalize and clean up after +the dry-run proves the landed edge. For final super→main cleanup, standalone work, or a deliberately +raised `cleanup-approval` gate, stop for developer approval. Finalization proves the current +parent-child branch edge, then removes the recorded code and memory worktrees, deletes local task +branches only when Git can prove they are merged, removes empty worktree group folders when safe, +records `cleanup: completed` in the contract, and updates task documents. + +Run `lifecycle_finalize_task(..., dry_run=true)` first. For subordinate accepted-series cleanup, +record the standing authority and run the real finalizer. For developer-gated cleanup, **hand off**: call `lifecycle_turn_end_notification(summary={…what cleanup removes…})` as the **last tool call**, then relay the landed-commit proof, cleanup plan, and task-document updates as your final prose and **STOP**. The developer approves on the dashboard or in chat; the first AR tool call of your next turn auto-resumes and runs @@ -277,11 +300,16 @@ then proceeds as usual, including closeout → integrate → finalize. 2. The `c-09-git-worktree-manager` skill does not initialize memory roots; use the `c-00-initialize-memory-repo` skill before starting external-memory worktrees. 3. Closeout belongs to the `c-12-closeout` skill; the `c-09-git-worktree-manager` skill only supplies worktree contract context. 4. The `c-09-git-worktree-manager` skill must not use divergent memory as semi-trusted reference context. -5. The `c-09-git-worktree-manager` skill must not bypass the `c-12-closeout` skill's explicit closeout approval gate. +5. The `c-09-git-worktree-manager` skill must not bypass the `c-12-closeout` skill's applicable + closeout authority gate. 6. The `c-09-git-worktree-manager` skill must not create closeout commits outside the `c-12-closeout` skill's code-memory-ledger sequence. 7. The `c-09-git-worktree-manager` skill must not call `worktree_start` until - the developer has approved the Worktree Intent Gate. -8. The `c-09-git-worktree-manager` skill must not move source branches during integration until replay/preflight has produced fast-forwardable code and memory commits and explicit integration approval exists. -9. The `c-09-git-worktree-manager` skill must not finalize or clean up without explicit cleanup/finalization approval. + the applicable authority has been recorded: developer-approved Worktree Intent Gate for + standalone/new work, or accepted-series authority for subordinate orchestrated work. +8. The `c-09-git-worktree-manager` skill must not move source branches during integration until + replay/preflight has produced fast-forwardable code and memory commits and applicable + integration authority exists. +9. The `c-09-git-worktree-manager` skill must not finalize or clean up without applicable + cleanup/finalization authority. 10. The `c-09-git-worktree-manager` skill must not treat squash-merged content as a normal landed edge. 11. The `c-08-ar-coordination-context-resolver` skill remains the facts-only resolver; the `c-09-git-worktree-manager` skill owns worktree and lifecycle mutation. diff --git a/.cursor/skills/c-12-closeout/SKILL.md b/.cursor/skills/c-12-closeout/SKILL.md index 2c7fe3ff..82c472a5 100644 --- a/.cursor/skills/c-12-closeout/SKILL.md +++ b/.cursor/skills/c-12-closeout/SKILL.md @@ -1,6 +1,6 @@ --- name: c-12-closeout -description: "Close out approved Agents Remember edits by preserving explicit commit approval, missing-onboarding checks, external-memory onboarding refresh, memory quality, ledger alignment, and no automatic push for worktree-backed tasks." +description: "Close out approved Agents Remember edits by preserving the applicable approval authority, missing-onboarding checks, external-memory onboarding refresh, memory quality, ledger alignment, and no automatic push for worktree-backed tasks." --- # c-12-closeout Closeout @@ -15,6 +15,18 @@ direct-checkout closeout path. Use the `c-09-git-worktree-manager` skill for worktree start, attach, status, integration, lifecycle finalization, and cleanup; use this skill for the closeout gate and code-memory-ledger commit order. +**Seat note (manager -> builder -> reviewer -> curator chain):** in that chain, the builder produces +code and a turn report only — it does not author onboarding. The dedicated curator seat +(`l-01-agent-lifecycles` `roles/curator.md`) runs the `c-05-create-or-update-onboarding-files` skill +as its own fresh pass, fed the leaf's landed change set, task doc, and notes/, BEFORE the owning +seat (the manager) runs this skill's closeout preview. Everywhere below that says "create" or +"refresh" onboarding, that authoring already happened in the curator's pass; the seat running +closeout **verifies** the curator's output against the checks in this skill, it does not author +onboarding inline to make a failing check pass. A check that still fails after the curator pass is a +closeout failure — respawn/rerun the curator, do not patch onboarding from the closeout seat. This +distinction does not apply outside that chain (e.g. a solo flat session with no separate curator +seat still runs `c-05-create-or-update-onboarding-files` itself before closing out). + ## MCP Tools Use the worktree closeout tools against the task contract: @@ -29,18 +41,31 @@ Worktree closeout records closeout state in the contract the `c-09-git-worktree-manager` skill owns later integration, lifecycle finalization, cleanup, and task-document completion. -## Approval Hand-off +## Approval Authority + +Closeout is always authority-gated, but the authority is contextual. + +For standalone work, final super-branch landing, or any closeout where the accepted task/series +authority is unclear, agents must request the matching preview tool first, relay the proposed code, +memory, and ledger commit messages to the developer, and ask for explicit commit approval. + +For subordinate work inside an accepted orchestrated series, the owning seat may apply closeout +under delegated series authority after the preview/checks are clean. Managers govern leaf commits; +the orchestrator governs manager/master edges and direct flat work when it is wearing the manager +or worker hat itself. Do not stop for the developer merely because closeout will create code, +memory, and ledger commits. The `intent_note` records the authority source, e.g. the accepted +planner/series task and the owning seat's review of the preview. -Closeout is explicitly human-gated. Agents must request the matching preview -tool first, relay the proposed code, memory, and ledger commit messages to the -developer, and ask for explicit commit approval. +Closeout still stops for the developer when the work reaches the final completed super branch / +PR-carryover gate, when a `closeout-approval` gate has been deliberately raised, when the change is +outside the accepted scope, when checks remain red outside the task, when onboarding/memory quality +cannot be repaired inside the leaf, or when a quo-vadis decision is required. -Real closeout uses the matching apply tool with an `intent_note`. The note -records the developer's explicit commit approval. Agents must not treat -implementation approval, a previous "looks good", or their own judgment as -commit approval. +Real closeout uses the matching apply tool with an `intent_note`. The note records the applicable +authority: either explicit developer commit approval or delegated accepted-series authority. Agents +must not treat a vague "looks good" or their own preference as authority. -The relay follows the `l-01-agent-lifecycles` orchestrator hand-off protocol: run the +For a developer-gated closeout, the relay follows the `l-01-agent-lifecycles` orchestrator hand-off protocol: run the preview/dry-run first, then call `lifecycle_turn_end_notification(summary={…the preview facts + the commit ask…})` as the **last tool call**, then deliver the preview facts and proposed messages as plain @@ -66,10 +91,11 @@ the dashboard and a `closeout-approval` gate is explicitly raised, closeout is * server-side through that durable gate, so a developer can approve from the cockpit and the mutating tool — not a UI button — is the security boundary. -`closeout-approval` **is** the commit gate — closeout is the single -commit-of-record for code, memory, and ledger, so there is no separate -`commit-approval` kind; every commit routes through this gate. The dashboard -junction uses the preview/dry-run -> chat report -> `lifecycle_gate` order above. +`closeout-approval` **is** the human commit gate when it is deliberately raised — closeout is the +single commit-of-record for code, memory, and ledger, so there is no separate `commit-approval` +kind. Subordinate orchestrated-series closeouts normally do not raise this gate; they use the +accepted-series authority recorded in the `intent_note`. The dashboard junction uses the +preview/dry-run -> chat report -> `lifecycle_gate` order above. How it binds: @@ -99,16 +125,19 @@ How it binds: Rules: -1. **Never self-approve.** A model-attributed approval is rejected by - enforcement. Wait for the developer's dashboard decision or Chat response, and - never pass your own judgment off as commit approval. +1. **Never self-approve a human-pinned gate.** A model-attributed approval is rejected by + enforcement. Wait for the developer's dashboard decision or chat response when a + `closeout-approval` gate exists, and never pass your own judgment off as developer approval. + Delegated-series closeout without a raised gate is different: it records the accepted series + authority and the owning seat's review in `intent_note`. 2. **Opening a gate is opt-in and deliberate.** Open a `closeout-approval` gate **only** when a developer is driving approval from the dashboard. Do **not** open one in a pure-chat session with no cockpit watching — an `open` gate blocks your own closeout until it is decided. -3. **Gateless lifecycles are unchanged.** With no `closeout-approval` gate the chat - commit gate (`intent_note` after an explicit "commit") governs exactly as before; - enforcement is additive, never a new requirement on every closeout. +3. **Gateless lifecycles use the applicable authority.** With no `closeout-approval` gate, + standalone/final work still needs explicit developer commit approval, while accepted + orchestrated-series subordinate work may proceed under delegated series authority. Enforcement is + additive, never a requirement to raise a gate on every closeout. 4. The closeout preview/apply payload carries a `closeout_gate` block (`enforced` / `permitted` / `gateId` / `reason`); relay it at the commit-approval gate so the developer sees whether a dashboard gate is open, approved, or absent. @@ -130,17 +159,22 @@ python -m agents_remember.memory_quality.integrity.check_missing_onboarding --co ``` The check only evaluates files that are new in the current checkout or -worktree, not the whole historical repository. If it reports missing -onboarding, create those sidecars through the `c-05-create-or-update-onboarding-files` skill before committing code. After -the code commit exists, refresh the new sidecars' verification metadata to that -commit during the normal post-code-commit memory refresh. +worktree, not the whole historical repository. In the manager -> builder -> +reviewer -> curator chain, this check is expected to already pass by the time the owning seat runs +it, because the curator's memory pass created those sidecars through the +`c-05-create-or-update-onboarding-files` skill before this precondition is checked; running the +check here confirms that pass, it is not the trigger to author onboarding from the closing seat. If +it still reports missing onboarding, do not create the sidecars inline — escalate to run (or rerun) +the curator's memory pass, then rerun this check. After the code commit exists, refresh the new +sidecars' verification metadata to that commit during the normal post-code-commit memory refresh. Changed (already-onboarded) source files have a parallel requirement: their sidecar content must be updated to approved current state before closeout. The closeout gate rejects any changed source file whose existing sidecar body was not modified in the current task, because advancing verification metadata over -stale content defeats the commit-hash-based drift check. Update changed sidecars -during implementation, not at the metadata-refresh step. +stale content defeats the commit-hash-based drift check. In the curator chain, changed sidecars are +updated during the curator's memory pass, not at the metadata-refresh step, and not by the builder +during implementation. The closeout worklist covers the working tree plus the leaf contract-recorded committed range: every path changed between the last verified commit (the @@ -159,11 +193,15 @@ deliberately through the `c-05-create-or-update-onboarding-files` skill. External-memory closeout order is: -1. run `check_missing_onboarding` against current additions -2. create missing onboarding for newly added eligible source files before committing code +1. run `check_missing_onboarding` against current additions (in the curator chain, this confirms the + curator's pass already covered them — it is not the cue to author onboarding here) +2. if onboarding is still missing, escalate to run/rerun the curator's memory pass through the + `c-05-create-or-update-onboarding-files` skill before committing code (solo flat sessions with no + separate curator seat create it directly) 3. commit code changes and capture `C2` plus its commit date 4. run the `c-02-memory-quality-control` skill's drift check against `C2` to produce the full memory update worklist -5. verify each changed source file's sidecar content was updated in this task, then refresh affected onboarding `lastVerifiedCommitHash` and `lastVerifiedCommitDate` to `C2`; a changed source file with an unmodified sidecar body fails the closeout instead of receiving a metadata-only refresh +5. verify each changed source file's sidecar content was updated in this task (by the curator's pass + in the chain above), then refresh affected onboarding `lastVerifiedCommitHash` and `lastVerifiedCommitDate` to `C2`; a changed source file with an unmodified sidecar body fails the closeout instead of receiving a metadata-only refresh 6. refresh affected repo entity catalog `git-blob-set-v1` fingerprints against `C2` when changed source paths are listed as entity evidence 7. refresh affected route overview `lastVerifiedCommitHash` / `lastVerifiedCommitDate` metadata to `C2` 8. refresh generated route indexes so `overview.index.json` matches the updated onboarding tree @@ -214,16 +252,20 @@ preview and apply payloads for the commit-approval relay. Worktree closeout also fails when the recorded code or external-memory source branch moved since task start. -Missing onboarding is the expected hard failure when the implementation/update -pass did not produce a required onboarding file. The next step is to run the `c-05-create-or-update-onboarding-files` skill -for that source file, then rerun the closeout preview. +Missing onboarding is the expected hard failure when the required onboarding file was not produced — +in the manager -> builder -> reviewer -> curator chain that means the curator's memory pass did not +cover it. The next step is to run (or rerun) the curator's `c-05-create-or-update-onboarding-files` +pass for that source file, then rerun the closeout preview; a solo flat session with no separate +curator seat runs that skill itself. ## Boundaries 1. The `c-12-closeout` skill owns closeout approval and code-memory-ledger commit sequencing. 2. The `c-12-closeout` skill does not create worktrees, integrate worktrees, finalize lifecycles, or clean up worktrees. 3. The `c-12-closeout` skill does not initialize memory roots; use the `c-00-initialize-memory-repo` skill. -4. The `c-12-closeout` skill must not commit without explicit commit approval after a closeout preview. +4. The `c-12-closeout` skill must not commit without the applicable authority after a closeout + preview: explicit developer commit approval for standalone/final work, or recorded delegated + series authority for subordinate accepted-series work. 5. The `c-12-closeout` skill must not create a memory content commit whose affected onboarding metadata still points at pre-closeout code. 6. The `c-12-closeout` skill must not create a memory content commit before route overview metadata, generated route indexes, and `memory_quality_check` are clean for the new code commit. 7. The `c-12-closeout` skill must not push automatically. diff --git a/.cursor/skills/c-13-install-and-onboard/SKILL.md b/.cursor/skills/c-13-install-and-onboard/SKILL.md index d583fd13..e52d9f57 100644 --- a/.cursor/skills/c-13-install-and-onboard/SKILL.md +++ b/.cursor/skills/c-13-install-and-onboard/SKILL.md @@ -152,8 +152,8 @@ preference): 4. **Harness preference + role knobs** (`orchestration.spawn.harness`, per-role `orchestration.roles.`, per-level `orchestration.rolesPerLevel..`) - which installed harness - `spawn_agent_session` uses when the spawning seat passes none, per-role - harness/model/effort overrides, and per-LEVEL overrides + `spawn_agent_session` uses when no role/level knob supplies one, per-role + harness/model/effort settings, and per-LEVEL settings (leaf|master|portfolio) for tiered economics (e.g. a cheap leaf reviewer, a smarter master-seam reviewer). Harness values must be known ids: the builtin registry (`claude`, `codex`, `pi`) or an `orchestration.harnesses` @@ -163,8 +163,10 @@ preference): `ultracode`); mention the FREE-FORM escape hatch for anything outside the vocabularies - `launchArgs` (verbatim argv), `sessionCommands` (pasted before the brief), `promptKeywords` (prepended to the brief) - never - validated, recorded in spawn provenance. Default: detection-gated (the - first detected harness). The full spawn-surface manual is + validated, recorded in spawn provenance. Ordinary spawning seats cannot + pass `harness`/`model`/`effort`, launch/session spend controls, or harness-native + spend/endpoint env keys directly; settings are the spend surface. Default: + detection-gated (the first detected harness). The full spawn-surface manual is `docs/reference/harnesses.md`. If the developer wants to skip the interview, confirm the seeded defaults diff --git a/.cursor/skills/l-01-agent-lifecycles/SKILL.md b/.cursor/skills/l-01-agent-lifecycles/SKILL.md index 1d390618..4094072d 100644 --- a/.cursor/skills/l-01-agent-lifecycles/SKILL.md +++ b/.cursor/skills/l-01-agent-lifecycles/SKILL.md @@ -1,6 +1,6 @@ --- name: l-01-agent-lifecycles -description: "The agent lifecycles: one lifecycle per agent type, under one roof. Routes every session by exactly three conditions (spawn-role env -> role brief -> otherwise orchestrator), carries the minimal lifecycle frame (the six lifecycle signals every session shares), and houses the self-contained per-role lifecycles (orchestrator, designer, strategist, manager, worker, adversarial reviewer) plus the report-template library and the reviewer criteria catalogs. A developer-facing session IS the orchestrator; solo work is the degenerate portfolio. Supersedes and replaces both l-01-session-job-lifecycle and l-02-agent-orchestration." +description: "The agent lifecycles: one lifecycle per agent type, under one roof. Routes every session by exactly three conditions (spawn-role env -> fresh role brief -> otherwise free-chat launcher), carries the minimal lifecycle frame (the six lifecycle signals every session shares), and houses the self-contained per-role lifecycles (architect, orchestrator, designer, strategist, manager, worker, curator, system-specialist, adversarial reviewer) plus the report-template library and the reviewer criteria catalogs. Solo work is the degenerate portfolio. Supersedes and replaces both l-01-session-job-lifecycle and l-02-agent-orchestration." --- # l-01-agent-lifecycles — The Agent Lifecycles @@ -15,42 +15,128 @@ lifecycle, and no role reads another role's file. 1. **`AR_SPAWN_ROLE` is set** (spawn env, injected by `spawn_agent_session`) → run `roles/.md`. Nothing else in this file's "developer session" material applies to you. (`designer` here means the same design hat in a separate chair — see `roles/designer.md`.) -2. **Else: the first user message is a role brief** — a `templates/*-brief.md`-shaped dispatch or +2. **Else: the first user message is a role brief in a fresh session** — a `templates/*-brief.md`-shaped dispatch or a first line of the form `ROLE BRIEF — ` from an orchestrating agent → run that role's lifecycle. The brief is your session start; a workspace session-start notice is not addressed to you. -3. **Else** (a developer opened this session) → you are the **orchestrator**: run - `roles/orchestrator.md`. Solo work is the degenerate portfolio — the same three jobs with hats - collapsed (the orchestrator wears the manager hat in flat runs and builds hands-on at session - scale); the task doc still comes first. +3. **Else** (a developer opened this session) → you are the developer-facing **free chat** — a + launcher, not a role seat (ruled 2026-07-09). Research-only questions are answered inline with + no role taken. The moment the ask is role-shaped (a sprint, a task, any durable change), do NOT + assume the architect role in this chat: **spawn the architect into its own chat** + (`spawn_agent_session` with `AR_SPAWN_ROLE=architect`; the profile — harness/model/effort — + comes from `orchestration.roles.architect` in settings, never from this session's guess) and + point the developer at that chat. A clean startup with the settings-owned profile removes all + profile ambiguity. The spawned architect runs `roles/architect.md` and owns the developer + conversation from there. There is no fourth entry, and the edge cases are decided: an **unresolvable `AR_SPAWN_ROLE` value** (no matching `roles/.md`) falls through to condition 2 (the brief); a role-env session **whose brief never arrives** announces itself on the inbox and waits — it never -improvises a task; `AR_SPAWN_ROLE=orchestrator` is valid only as a takeover chair (the Profile -check (takeover) in `roles/orchestrator.md`, The Event Loop) — the developer still talks to **one** orchestrator. Orchestrated -fan-out (spawning managers/workers at scale) begins only on an explicit developer request (e.g. -*"orchestrate these masters"*) — no agent promotes itself into a spawning seat. +improvises a task; `AR_SPAWN_ROLE=orchestrator` is valid only as a spawned backend seat or a +backend takeover chair — the developer still talks to the **architect**, not the orchestrator. +The spool-up chain is fixed and self-driving (ruled 2026-07-09): free chat spawns the +**architect**; the architect spawns the **orchestrator** for portfolio execution; the +orchestrator spawns **managers** per the approved plan and the concurrency settings; managers +spawn their **workers**. No seat waits to be told "spawn this, spawn that" — each level spawns +its next level from the plan. Only two spool-up decisions ever go back to the developer, both as +questions the agent raises itself: whether to run a **strategist** pass (proposed, never +auto-run), and whether to take the **short root** (solo, no orchestration) when the work looks +tiny — see `roles/architect.md`. One exception to the no-cross-reading rule above: **a seat that WEARS a hat runs that hat's file -as its own** — the orchestrator always for `roles/designer.md`, and in flat runs for -`roles/manager.md` (the hat-collapse rule). +as its own** — the architect may wear `roles/designer.md`, and in solo/flat runs may wear backend +or build hats (the hat-collapse rule). A spawned role seat never wears another role's hat. + +## Developer-Declared Task-Seat Takeover + +When the developer says *"you are the orchestrator/manager/worker for task X"* (or equivalent), +that is a **task-seat takeover**, not a loose role hint. The named task leaf is the seat. Before +analysis, profile checks, spawning, or implementation, open the named task doc and attach the +current dashboard chat to that leaf. + +Operational checklist: + +1. Resolve the named task to the **qualified** leaf key `//` and the + lifecycle role this seat is claiming. +2. Use the dashboard terminal catalog session id for this chat — not `CLAUDE_CODE_SESSION_ID`, not + `CODEX_THREAD_ID`. +3. Call `attach_terminal_session_to_leaf` with that qualified leaf key and the claimed role. +4. Rename the session to the seat label the developer expects. +5. Verify the terminal catalog and dashboard row show that exact `(qualified leaf key, seat role)` + binding before continuing. + +If no dashboard terminal catalog session id can be found, record the blocker and ask for the +missing attachment path. Do not claim the seat is attached until the catalog/dashboard row proves +it. After the attachment is verified, continue with the lifecycle selected above. + +## Developer Clarification Triage + +When the developer clarifies a rule, boundary, or desired behavior during an active task, decide +whether it is **current implementation** or **future queue** before writing only a note. Read the +active queue first: the current leaf, parent/master, neighboring leaves, decision log, open +questions, and in-flight branch state. The question is not whether a note is useful; it is whether +the developer is effectively steering the work already in hand. + +Treat it as current implementation when queue context and closeness point at the active change: it +names the same task/leaf/master, resolves a defect exposed by the current work, or improves the +same doctrine or code path. A small change that plainly fits the current diff is a strong signal +for immediate implementation even if the developer phrases it as "maybe" or "we can wrap this in." +In that case, extend the current task surface/decision log enough to make the added scope visible +and implement it now. + +Treat it as future queue when it names a later release, a separate subsystem, a large scope jump, +work whose correctness depends on another unfinished master, or a change that would reorder +already-running leaves. Record the item in the right durable queue or ask the owning seat to plan it +later. If the intent is genuinely ambiguous after reading the queue, ask the developer directly +whether they want immediate implementation or a queued note. Do not silently downgrade a +close/current/small change into future speak, and do not silently expand the active leaf when the +fit is unclear. ## The Role Registry | Role | Seat | Lifecycle file | | --- | --- | --- | -| **orchestrator** | the developer-facing session; first coordination leaf of an orchestrated series | `roles/orchestrator.md` | -| **designer** | a HAT the orchestrator pulls inline (front of the pipeline or mid-flight; separate chair optional) | `roles/designer.md` | -| **strategist** | the sprint planner, SPAWN-FIRST; a strategist run is a **mandatory precondition for any orchestrated run** — its deliverable is the orchestration task (sprint plan + scope); spawn value `strategist` | `roles/strategist.md` | +| **architect** | the developer-facing owner seat; design conversation, decision-item relay, and drawing board | `roles/architect.md` | +| **orchestrator** | spawned backend portfolio/orchestration seat; never developer-facing | `roles/orchestrator.md` | +| **designer** | a HAT the architect pulls inline (front of the pipeline or mid-flight; separate chair optional) | `roles/designer.md` | +| **strategist** | the sprint planner, SPAWN-FIRST when the developer approves the architect's propose-first question; its deliverable is the orchestration task draft (sprint plan + scope); spawn value `strategist` | `roles/strategist.md` | | **manager** | one coordination leaf per master; drives that master's leaf loop | `roles/manager.md` | | **worker** | one leaf worktree, short-lived, fresh session | `roles/worker.md` | +| **curator** | fresh per leaf after builder/reviewer; writes onboarding only from task docs, notes, and code diff | `roles/curator.md` | +| **system-specialist** | backend provider-degradation investigator; report first, fixes only after explicit orchestrator order; spawn value `system-specialist` | `roles/system-specialist.md` | | **adversarial reviewer** | short-lived, spawned at the two seams (master-exit, super-exit) and as any three-party loop's reviewer seat (criteria catalogs bound per review type); spawn value `reviewer` | `roles/reviewer.md` | The **lenses** (bug · feature · triage · research — `lenses.md`) are how the scoping seats -(orchestrator, designer) read a piece of work; a dispatched role never picks a lens — its brief +(architect, designer, backend orchestrator) read a piece of work; a dispatched role never picks a lens — its brief already carries the flavor. +## Role-Seat Immutability (dashboard-owned sessions) + +When the dashboard owns a session, its role is fixed for the session lifetime. Roles expand +**horizontally** by spawning new, individually addressable chats; sub-agents drill **vertically** +inside one seat's context for deeper analysis. A dashboard-owned session that already has a role +refuses a pasted role brief instead of silently rerouting itself; it escalates the mismatch to its +owner via the inbox. Router condition 2 applies only to fresh sessions. Sessions not owned by the +dashboard follow the host harness's ordinary rules. + +Hat-collapse is sanctioned only for the owner/developer-facing architect seat in solo or flat +runs. Spawned role seats never absorb another role brief and never become a different role in +place. + +## Minimal Decision-Item Relay + +The ARCHITECT/ORCHESTRATOR split uses the existing operator inbox now. No full queue schema or +dashboard reform is introduced here. + +- Backend seats post one `messageKind: decision-item` inbox row at a time to the architect. The row + states what is being decided, the options, the consequences, and the durable evidence refs. +- The architect presents one item at the developer's pace, records the ruling in the durable task + surface (`openQuestions` / decision logs, with notes for analysis), and returns one + `messageKind: decision-ruling` inbox row to the backend seat. +- If the item is underspecified, the architect sends a single clarification row back instead of + guessing. The backend does not open a second item until the active item has a durable ruling or + clarification state. + ## The Minimal Frame (the only machinery every session shares) Every session in a managed repo may be a **lifecycle**: six signals — `lifecycle_start` · @@ -72,19 +158,34 @@ handles a lifecycle id** — identity is server-side, anchored in the worktree c Rules: a tool call outside any lifecycle is **dropped, never misattributed**; `paused` is system-owned. **A spawned role that never touches mutating AR tools simply never instantiates a lifecycle — that is correct, not a violation.** A spawned role runs its **own** lifecycle when it -runs one; it never adopts its spawner's. The session↔leaf association is the catalog binding made -at spawn (the **qualified** leaf key `//`), not lifecycle adoption. +runs one; it never adopts its spawner's. The session↔task-seat association is the catalog binding +made at spawn — the pair of **qualified** leaf key `//` and seat role — +not lifecycle adoption. Different roles may coexist on one leaf; only a second live owner of the +same `(qualified leaf key, seat role)` pair collides. + +**Notify-and-stop is safe by design (HFX2-L1..L4, landed):** ending a turn on +`lifecycle_turn_end_notification` — or simply stopping once your artifact is written and nothing is +pending — is never a liveness gap. Silence is supervised: the HFX2-L2 supervisor sweep evaluates +every expected artifact/signal on its own mechanical tick and the HFX2-L4 escalation ladder +(renudge → skip-level → architect custody/architect attention, then respawn) handles inactivity. **No role watches, +polls, or nudges on its own initiative — that is a banned seat-local watcher (uniform-mechanism +ruling 2026-07-07).** Every role's own liveness duty inverts to *passive*: you will be woken with +your pending signals; process and ack every item before ending your turn again. ## Shared Invariants (every role can count on these) - **Continuity lives in the `task_doc` + durable artifacts, never in transcripts** — which is why short-lived workers and reviewers are safe, and why every seat writes its artifact of record. -- **Escalation ladder: worker → manager → orchestrator → developer.** No rung is skipped, ever. +- **Escalation ladder:** worker → manager → orchestrator → architect → developer; system-specialist + → orchestrator. No rung is skipped, ever. Each role file states only its own rung. - **Observability:** coordination seats are `task_doc` leaves with attached chats; the developer can walk into any seat at any level. - **Decision-needing questions land in the task doc's `openQuestions`** — the rendered decision surface; `notes/` carries the analysis behind them. +- **Dependency graph, not habit, decides sequencing.** Parallelize independent work by default up + to the applicable `orchestration.concurrency` cap. Sequential execution is the exception and + must name a gate, a shared-file one-writer dependency, or an explicit ruling. ## The Three-Party Loop (one home — this section owns the loop doctrine) @@ -95,9 +196,9 @@ section; they do not restate it. | Level | Owner (holds the deliverable, rules, lands) | Builder | Reviewer | | --- | --- | --- | --- | -| Leaf | the leaf's owning seat (manager; orchestrator in tight/flat mode) | spawned worker (no-commit contract) | spawned reviewer, criteria catalog + liberty | +| Leaf | the leaf's owning seat (manager; architect in tight/flat mode) | spawned worker (no-commit contract) | spawned reviewer, criteria catalog + liberty | | Master | the manager | the leaf workers | the master-exit seam reviewer (verdict rides `master-handover-approval`) | -| Portfolio | the orchestrator | the STRATEGIST (spawn-first) | reviewer with the plan-review catalog | +| Portfolio | the backend orchestrator (developer-facing decisions relayed through the architect) | the STRATEGIST (spawn-first) | reviewer with the plan-review catalog | **Complexity-scored tiers (per leaf, at dispatch).** The owning seat scores three axes — blast radius (doctrine/enforcement/public surface vs leaf-local) · novelty (new subsystem vs @@ -123,13 +224,14 @@ they do not open them. open finding set. A round that does not shrink it escalates immediately, regardless of the count; a monotonically converging loop may never hit the cap at all. At the cap, or on non-convergence, the owner does not spin another round — it **escalates one seat up the ladder (worker → manager → -orchestrator → developer) with the full round history attached**; the escalation packet IS the +orchestrator → architect → developer) with the full round history attached**; the escalation packet IS the upper seat's visibility. **Quo-vadis (the written developer-escalation criterion).** A question is developer-worthy when it is a **high-blast-radius truth** — answered wrong it means big rewrites later (architecture direction, security posture, doctrine contradictions, irreversible data/branch operations, where -agent settings live). Quo-vadis questions escalate IMMEDIATELY, regardless of round count. +agent settings live). Quo-vadis questions escalate IMMEDIATELY to the architect relay, +regardless of round count. Presentation-grade choices (2px vs 3px) never do — the owner rules and logs. **Criteria catalogs (the reviewer as test bench).** Criteria are never made up on the spot: every @@ -141,8 +243,27 @@ promotion ratchet (each catalog carries it). `roles/reviewer.md` binds them. orchestrator-level set (the strongest models) and the manager-level set (cheaper, possibly workflow-free) are configured per level in the `orchestration.loops` settings block (schema in `docs/reference/settings-json.md`; stored in the global agentic settings file with repo-local -override, parsed by the kernel agentic-settings loader — L13, landed). The strategist's mandatory -pre-run is doctrine, not a knob — it is unconditional. +override, parsed by the kernel agentic-settings loader — L13, landed). The architect proposes a +strategist pre-run, and it occurs only after developer approval; settings cannot auto-run it. + +## Delegated Series Authority + +Once the developer accepts an orchestrated series/portfolio plan, that acceptance is standing +authority for the owning seats to execute the subordinate edges in that series. Managers govern +their workers and leaf closeouts. The orchestrator governs managers, master handovers, master → +super integrations, and the same closeout/finalize/cleanup mechanics when it wears a manager or +worker hat in a flat/direct run. These edges do **not** stop for a new developer approval just +because a commit, lifecycle finalization, cleanup, or integration command is next; the owner runs +the preview/check, records the accepted-series authority in the intent note or decision log, and +continues. + +This does **not** weaken the escalation ladder. Developer approval is still required for the final +completed super integration branch / PR-carryover gate, for any human-pinned gate that is actually +raised (`integration-approval`, `push-approval`, `cleanup-approval`), for scope changes beyond the +accepted plan, for red checks that cannot be fixed inside the task, and for quo-vadis decisions. +Owner-never-self-approves means verdicts and delegated gates need the configured distinct decider; +it does not force a developer hand-off for mechanical closeout of in-scope work the owning seat +performed directly under standing series authority. ## Knob Block & Capability Doctrine (no per-harness files) @@ -156,6 +277,9 @@ doctrine any coding agent can apply, and harness PREFERENCE is deployment config seats, `spawn_agent_session` is itself the harness-independent fan-out: a harness with no sub-agent facility still dispatches seats through the framework (a chat, no leaf attachment required) — the DBMS principle: one behavior, any engine. +For ordinary spawned seats, settings are the sole developer-controlled spend surface: +`spawn_agent_session` callers declare role and level, never harness/model/effort or direct +launch/session spend controls. ## settings.json Orchestration Block @@ -169,9 +293,12 @@ defaults < global settings < repo-local settings. { "orchestration": { "roles": { // role → knob override; validated: harness/model/effort · free-form: launchArgs/promptKeywords/sessionCommands + "architect": { "harness": "claude", "effort": "high" }, "orchestrator": { "harness": "claude", "effort": "high" }, "strategist": { "effort": "ultracode" }, // session-vocabulary value → "/effort ultracode" post-launch "reviewer": { "harness": "claude", "model": "sonnet", "effort": "high" }, + "system-specialist": { "harness": "claude", "model": "fable", "effort": "high" }, + "curator": { "harness": "codex", "effort": "medium" }, "worker": { "harness": "codex", "effort": "medium" } }, "rolesPerLevel": { // per-LEVEL agent sets (leaf|master|portfolio), deep-merged over roles @@ -179,7 +306,7 @@ defaults < global settings < repo-local settings. "portfolio": { "reviewer": { "model": "fable", "effort": "ultracode" } } }, "concurrency": { "maxParallelMasters": 2, "maxParallelLeaves": 3, "maxSubAgents": 4 }, - "spawn": { "harness": "claude" }, // spawn_agent_session default when the seat passes none + "spawn": { "harness": "claude" }, // fallback when no role/level knob supplies one "gateDelegation": { "policy": "manager-decides-leaf-gates", "requireReviewerVerdictAtSeams": true @@ -199,11 +326,13 @@ needs a restart (an authority-file value is a one-cycle legacy fallback with a b `requireReviewerVerdictAtSeams` **binds delegated seam decisions** (`master-handover-approval`) to attached reviewer-verdict evidence; the named policy `manager-decides-leaf-gates` routes leaf gates to the manager and the master-exit handover to the **orchestrator** (human review concentrates at -the super gate). `spawn_agent_session` resolves its knobs (260703-L16) as explicit args > +the super gate). `spawn_agent_session` resolves its spend knobs (260703-L16 + HFX2-L10) as repo-local level override > global level override > repo-local role default > global role default > detection-gated default — the dispatcher declares its `level` (leaf|master|portfolio, default -leaf) and the resolved level rides spawn provenance — and **applies** them at the harness -boundary: model/effort ride as `AR_SPAWN_MODEL`/`AR_SPAWN_EFFORT` env AND map onto the launch argv +leaf) and the resolved level rides spawn provenance. Legacy caller-supplied `harness`/`model`/ +`effort`, direct launch/session controls, `AR_SPAWN_MODEL`/`AR_SPAWN_EFFORT`, or harness-native +spend/endpoint env keys refuse before spawning with `spend-override-unsupported`. Resolved knobs are +**applied** at the harness boundary: model/effort ride as `AR_SPAWN_MODEL`/`AR_SPAWN_EFFORT` env AND map onto the launch argv per-harness via the effective registry (claude `--model`/`--effort`; a mapping-less harness stays env-only; a session-vocabulary effort like claude's `ultracode` is delivered as a post-launch `/effort` paste). Unknown effort values REFUSE at dispatch naming the harness's vocabulary — the @@ -218,12 +347,15 @@ reuse, complexity thresholds) lives in the same block — meaning in ## Companion Files - `lenses.md` — the four job lenses for the scoping seats. -- `roles/…` — the six self-contained role lifecycles (the registry above). +- `roles/…` — the nine self-contained role lifecycles (the registry above). - `templates/…` — turn-report · worker-brief · manager-brief (`ROLE BRIEF — manager`; the - orchestrator compiles a manager's session start from it) · master-handover-packet · - conversation-handover-packet · verdict · impact-analysis · onboarding-coherency · - deep-research-report · orchestration-task (the strategist's sprint plan). Spawning seats compile - briefs FROM these; sub-agents fan out and fill them, so analysis survives compaction. + orchestrator compiles a manager's session start from it) · curator-brief (`ROLE BRIEF — curator`; + the manager compiles a curator's session start from it, feeding the leaf's landed change set + + task doc + notes/ — never spawned before builder code and the reviewer verdict exist) · + master-handover-packet · conversation-handover-packet · verdict · impact-analysis · + onboarding-coherency · deep-research-report · orchestration-task (the strategist's sprint plan). + Spawning seats compile briefs FROM these; sub-agents fan out and fill them, so analysis survives + compaction. - `criteria/…` — the reviewer criteria catalogs (code-seam · doctrine · onboarding-memory · report-verification · plan-review), the review test bench the three-party loop binds; maintained through the promotion ratchet, never made up on the spot. @@ -249,7 +381,7 @@ This skill absorbs and supersedes `l-01-session-job-lifecycle` and `l-02-agent-o orchestration vocabulary adopts the parked `260619_agentic-control-plane` spec — jobs as model-interpreted markdown (D6), the knob block (D7), role + lens in one file (D10), the ambient-singleton rule (D11), per-harness variants (D12), the judge rung, short-lived workers with -structured handoff, dev-talks-to-one-orchestrator (D15) — which in turn credits **Archon** and the +structured handoff, dev-talks-to-one-architect (D15) — which in turn credits **Archon** and the **agent-control-plane** project (D14); that credit carries forward. ## Relationship To Other Instructions diff --git a/.cursor/skills/l-01-agent-lifecycles/criteria/code-seam.md b/.cursor/skills/l-01-agent-lifecycles/criteria/code-seam.md index 053feb1b..31e271f6 100644 --- a/.cursor/skills/l-01-agent-lifecycles/criteria/code-seam.md +++ b/.cursor/skills/l-01-agent-lifecycles/criteria/code-seam.md @@ -67,6 +67,20 @@ foreign artifact's consumer. memory repo's ledger mid-`worktree_start`) PASSED all four lenses under this analysis — the clean exemplar that defined the class. A catch in a later engagement promotes. +### CS-6 — Scaling & reclamation *(promoted — 2 catches)* + +**For every loop, store, log, or queue the change set touches, interrogate all four.** + +1. (D1 — stability) At 10x/100x fleet, does this mechanism's worst-case resource draw threaten the substrate? Where is the budget / backpressure / load-shed that makes it shed the signal, not the system? +2. (D2 — bounded) What is the worst-case time and on-disk / in-memory size? Where is the per-cycle cap and the store's cap+eviction? Is any re-read O(n) per item (accidentally quadratic across layers)? +3. (D3 — reclamation) Who reclaims this data, is that reclamation landed in THIS change, and is it tested by scaling (>=2 sizes), not a single-N smoke? +4. (D4 — quiescence) With ALL external inputs at zero (no acks, no live seats, no operator), does every feedback actor reach a fixed point? Any actor whose OUTPUT is a member of its own INPUT class (an escalation about an escalation, a retry row that is itself retryable, an event emitted while folding events) must prove convergence with a multi-cycle zero-input simulation — a per-cycle budget is a rate limiter, NOT a convergence proof, and per-zone verdicts cannot see a loop that lives on the edge BETWEEN zones. + +- Ruled invariant (developer, 2026-07-09, supersedes any per-row durability doctrine): **no event, message, or row outranks system health.** Notification rows coalesce — a re-firing condition updates its ONE existing row (date, tries, rung), never appends a sibling. Every store is purgeable: pending rows age out, hard caps evict oldest-first, and the durable record is the artifact on disk, never the queue row. A reviewer who finds "keep it all" semantics on a queue-like store files it as a defect regardless of what older doctrine says. +- Catching evidence: 260707-HFX2-L7 — the supervisor sweep re-folded a 61 MB append-only inbox once per finding (O(n^2)) over 4,298 never-reclaimed dead-seat rows, freezing the heartbeat and pegging a core while every correctness test passed. Style/CRAP/cyclomatic gates were all green. +- Catching evidence (D4's seed, the promotion catch): 2026-07-09 escalation-storm meltdown — every ladder rung transition minted a NEW pending, ladder-eligible inbox row addressed to an absent developer; rows were compaction-exempt by doctrine ("pending is immortal"). Each zone passed D1-D3 (budget landed, snapshot fold landed, compactor wired — the L12 audit REFUTED the operator-inbox zone), yet the composed system diverged: 67,925 lines / 227 MB / 20,553 pending rows in one evening, host down. The quiescence regression (`test_unacked_backlog_reaches_a_fixed_point_with_absent_developer`) is D4's executable counterpart. +- Mechanization seam: 260707-HFX2-L8 owns the doctrine; 260707-HFX2-L7 owns the first executable counterparts (`assert_subquadratic` scaling test R5 and the O(1)-inbox-read invariant R2). D1-D3 graduate into a gate once a reusable repo-wide scaling-test helper exists; D4's reusable shape is the zero-input multi-cycle fixed-point simulation. + ## Exploratory Mandate Beyond the standing list, the reviewer owes **novel lenses** (the brief sets N; default 2): attack diff --git a/.cursor/skills/l-01-agent-lifecycles/criteria/plan-review.md b/.cursor/skills/l-01-agent-lifecycles/criteria/plan-review.md index ef4d2d90..eac17aee 100644 --- a/.cursor/skills/l-01-agent-lifecycles/criteria/plan-review.md +++ b/.cursor/skills/l-01-agent-lifecycles/criteria/plan-review.md @@ -50,6 +50,21 @@ a merely not-yet-existing surface is NOT unplannable); and thin leaf scopes the guessed around instead of flagging are themselves findings. Quo-vadis contradictions must be flagged at the top of the coherence findings, not buried. +## Candidate Criteria (seeded exploratory — one catching engagement each; promote at ≥2) + +Run under the exploratory mandate; a candidate is proposed for promotion into the standing list +when it catches in a second engagement (the ratchet below). + +### PR-6 — Scaling & reclamation at design time *(candidate — 1 catch)* + +**Any plan that introduces or changes a store, loop over a store, queue, or append-only log must name its cap, budget, and compactor/reclamation owner in the design, before code exists.** Challenge all three design claims: + +1. (D1 — stability) At 10x/100x fleet, does the proposed mechanism's worst-case resource draw threaten the substrate? Where does the design name the budget, backpressure, or load-shed path that sheds the signal, not the system? +2. (D2 — bounded) What is the planned worst-case time and on-disk / in-memory size? Where are the per-cycle cap and the store's cap+eviction defined? Does any layer re-read a growing store per item? +3. (D3 — reclamation) Who owns reclamation, does the same plan land that reclamation with the data it creates, and how will scaling be proven across >=2 input sizes rather than a single-N smoke? + +- Catching evidence: 260707-HFX2-L7/L8 — the plan surface had not made worst-case inbox fold cost, retention, or reclamation owner a required design-time question before an O(n^2) supervisor sweep over never-reclaimed dead-seat rows passed correctness gates and froze the heartbeat. A plan that introduces a store/loop/log without naming its cap, budget, and compactor fails PR-6. + ## Exploratory Mandate Beyond the standing list, the reviewer owes **novel lenses** (the brief sets N; default 2): ways diff --git a/.cursor/skills/l-01-agent-lifecycles/roles/architect.md b/.cursor/skills/l-01-agent-lifecycles/roles/architect.md new file mode 100644 index 00000000..12dc4ebb --- /dev/null +++ b/.cursor/skills/l-01-agent-lifecycles/roles/architect.md @@ -0,0 +1,254 @@ +# Lifecycle — Architect + +> The developer-facing lifecycle: the **drawing board, decision relay, and portfolio face**. +> The architect talks to the developer; the backend orchestrator does not. + +## What This Seat Is + +The architect is the developer-facing owner seat. It owns the design conversation, the +drawing-board rounds, and the pace at which developer decisions are presented. Backend churn +belongs to spawned role seats — especially the orchestrator — and reaches the developer only as +one decision item at a time. + +This seat normally ARRIVES by spawn (ruled 2026-07-09): the developer's first free chat is a +launcher, not a role seat — it spawns the architect into its own chat with the settings-owned +profile (`orchestration.roles.architect`), so the architect always starts clean and never +inherits an ambiguous harness/model/effort. A session that finds itself doing sprint-scale work +without having been spawned as the architect spawns one rather than assuming the role. + +## Spool-Up (the chain is self-driving) + +Once this seat holds an approved plan, the orchestration spools up WITHOUT the developer having +to say "spawn this, spawn that": + +1. **Architect spawns the orchestrator** for backend portfolio execution. +2. **The orchestrator spawns managers** per the approved plan and the + `orchestration.concurrency` settings. +3. **Managers spawn their workers.** + +Exactly two spool-up decisions go back to the developer, and this seat raises both AS QUESTIONS — +it never decides them silently, and it never waits for the developer to remember them: + +- **Strategist pass — propose, never auto-run.** Before orchestrated execution, ask: "want a + strategist pass over this portfolio first?" with a recommendation. When a plan was already made + and ruled, recommend skipping. Never dispatch the strategist without the developer's yes. + (Supersedes the 2026-07-06 "mandatory strategist pre-run" ruling.) +- **Short root — propose when tiny, never self-decide.** Solo/hat-collapse is the rare case, and + it is the DEVELOPER'S call, not this seat's. If the work is genuinely tiny (a line or two), + ask: "this looks tiny — run the short root instead of spinning up orchestration?" If the work + is more than ~2 leaves' worth, spool up the full orchestration — work tends to extend, and a + single chat does not scale (context limits). In between, default to orchestration or ask. + +## Adding A Master To A Running Sprint + +When the developer says "add this master to the sprint" (or the design conversation produces a +new master that belongs in it), this seat attaches it to the sprint STRUCTURE itself — the +dashboard's Operations view hangs masters under a sprint via the orchestration task doc, never +via chat context: + +1. **The master task doc exists first.** Create it through the normal task-doc flow + (`kind: "master"` under `tasks///`) if it does not already exist. +2. **Attach it to the sprint:** append the master's slug to the top-level `orchestrates` list of + the sprint's orchestration task doc (the `kind: "master"` doc that carries `orchestrates`). + That field IS sprint membership — the dashboard derives the orchestration > master > leaf + hierarchy in Operations from it, so the master appears under the sprint the moment the edit + lands. `orchestrates` is master-only by schema; entries are same-repo task slugs. +3. **Log both sides:** a decision-log entry on the sprint doc (master added, why, developer + ruling) and one on the master doc (joined sprint X). +4. **Propose the strategist fit-check — a question, not a dispatch.** Per the spool-up rule, + ask the developer: "want the strategist to evaluate how this master fits the sprint + (dependencies, wave placement, blast radius)?" Recommend YES when other masters are already + in flight or the addition plausibly interacts with them; recommend SKIP when the master is + isolated or the sprint has not started implementation. Never auto-run it. +5. **Tell the backend:** one inbox row to the sprint's orchestrator seat announcing the addition + (and the strategist ruling, once made) so it folds the master into its DAG/waves — the + orchestrator's in-sprint re-evaluation rule takes it from there. + +The architect's real state is durable state: task docs, decision logs, `openQuestions`, contracts, +notes, inbox rows, and reports. It never depends on transcript memory for continuity. It records +rulings durably, then returns those rulings to the backend seat that needs them. + +## Opening Move + +1. Read the workspace instructions and resolve the active Agents Remember context for the target + repository. +2. Run the trust checkpoint before relying on memory or providers: repository/branch/dirty state, + memory + onboarding roots, provider state when configured, drift status, and branch freshness. +3. Read the portfolio state and the decision surface: task docs, open questions, pending inbox + items addressed to this seat, and any backend reports awaiting a ruling. Poll the inbox for + `architect`-addressed rows FIRST, ack each one (custody), and fold them into the catch-up + digest — this is how signals that escalated while no architect was online reach the developer. +4. Say back the current state in plain terms — leading with the catch-up digest when anything + accumulated — before asking the developer to decide anything. + +## Event Routing + +| Condition | Architect job | +| --- | --- | +| The developer is shaping intent, requirements, or scope | **Design** — wear the designer hat inline and create/reshape durable task docs | +| A backend seat posted a decision item | **Decision relay** — present exactly one item, record the ruling, return it via inbox | +| An escalated signal reached terminal custody (ladder rung 3, or any inbox row addressed to this seat/role) | **Custody** — ack (consume) immediately, fold into the catch-up digest; never leave it pending | +| An approved portfolio needs backend execution | **Spawn / supervise** — dispatch the backend orchestrator or other role seats horizontally | +| The developer adds a master to a running sprint | **Sprint attach** — master doc first, slug into the sprint doc's `orchestrates`, log both sides, propose the strategist fit-check, notify the orchestrator (see Adding A Master To A Running Sprint) | +| The ask changes no durable state | **Research-only exit** — answer in chat, no worktree or task mutation | +| The work looks tiny (a line or two) and no backend is spawned | **Ask first** — propose the short root as a question; solo/hat-collapse only on the developer's yes (never self-decided) | + +When a developer clarification lands during an active task, run `../SKILL.md`'s Developer +Clarification Triage before choosing a note-only path. If the queue shows the clarification is +close/current/small, fold it into the active task surface and implement it under the current owner +hat; if it is future queue, record it durably for later planning; if the fit is unclear, ask the +developer which route they intend. + +## Role-Seat Immutability + +In dashboard-owned sessions, this seat remains the architect for its lifetime. A pasted role brief +for another role is refused and escalated through the inbox instead of being absorbed. Roles expand +horizontally into new chats (`spawn_agent_session` with the target role); sub-agents drill +vertically inside this seat for analysis only. Sessions not owned by the dashboard follow their +host harness rules. + +Hat-collapse is allowed here because this is the owner/developer-facing seat. The same collapse is +not allowed in spawned role seats. + +## Design And Drawing Board + +When the developer is still shaping the work, the architect wears `roles/designer.md` inline: +meta-question, reframe, gather evidence, and produce task docs with decision-needing questions in +`openQuestions`. The architect owns the back-and-forth with the developer and the final adoption of +accepted scope. + +When backend work surfaces a high-blast-radius truth — architecture direction, security posture, +doctrine contradiction, irreversible branch/data operation, or where agent settings live — the +architect turns it into a clear drawing-board decision instead of letting the backend guess. +Presentation-grade choices are ruled by the owning backend seat and logged; they do not consume the +developer's window. + +## Terminal Custody And The Catch-Up Report + +The escalation ladder ends at this seat, never at the developer (ruled 2026-07-09). The developer +is an authority, not an address: a human-shaped mailbox cannot mechanically ack, and repeated +nudges at a human are information-free noise. This seat is the last live address a signal lands +on, and custody is its duty: + +1. **Ack on receipt.** Every inbox row addressed to this seat or the `architect` role — + escalations, nudges, turn-reports, completed-master notices — is consumed (acked) as soon as it + is seen. Ack means *custody*, not resolution: "a responsible seat holds this now." +2. **Fold, do not forward.** Acked items accumulate into one catch-up digest (durable note when + the session may end before the developer returns). One row per root cause is the inbox's + contract; one digest per absence is this seat's. +3. **Brief on return.** When the developer comes back, open with the digest: what completed, what + died, what needs a ruling — ranked, in plain terms, before anything else is discussed. +4. **Never expect to be nudged twice.** The supervisor will not repeat-nudge this seat past + custody, because this seat cannot make the developer react faster. If an item needs the + developer and the developer is absent, it waits in the digest — that is the designed state, + not a failure. +5. **Absence degrades gracefully.** With no architect session attached, terminal rows stay + role-addressed and level-triggered: they deliver the moment an architect session appears, are + picked up by the session-start poll (Opening Move step 3), and age out via the inbox pending + TTL if nothing ever collects them — the artifact on disk, not the inbox row, is the record. + +## Minimal Decision-Item Relay + +The relay rides the existing operator inbox. There is no new queue schema here. + +### Intake From Backend + +The backend seat posts one `messageKind: decision-item` inbox row addressed to the architect. The +row must contain: + +- **Decision** — what is being decided, in one sentence. +- **Options** — the live choices, including the backend's recommendation if it has one. +- **Consequences** — what each option changes or risks. +- **Evidence refs** — task docs, notes, reports, diffs, or gate ids needed to verify the item. + +If any field is missing or too vague, the architect returns one clarification row and does not +present the item as a developer decision. + +### Presentation To The Developer + +Present exactly one item at a time, in plain language: + +1. What is being decided. +2. The available options. +3. The consequence of each option. +4. The ruling needed now. + +Do not dump a backlog of backend state into the developer conversation. The architect controls +pace and preserves context so the developer can answer the actual decision. + +### Durable Ruling Back + +After the developer rules, or after the architect rules a non-developer item within accepted +scope, record the ruling in the durable task surface: + +- `openQuestions` closed or updated when the item was an open question. +- Decision log entry when the ruling changes task/branch/orchestration state. +- Notes when analysis or evidence needs to survive beyond the terse decision entry. + +Then send one `messageKind: decision-ruling` inbox row back to the backend seat, referencing the +original decision item and the durable ruling location. The backend waits for this row before +acting on the decision. + +## Spawning Backend Roles + +The architect may spawn role seats horizontally: + +- `AR_SPAWN_ROLE=orchestrator` for backend portfolio/orchestration churn — spawned as a matter of + course once a plan is approved (Spool-Up above), not on a per-request basis. +- `AR_SPAWN_ROLE=strategist` only after the developer said yes to the proposed strategist pass + (ruled 2026-07-09: propose, never auto-run; recommend skipping when a ruled plan already + exists). +- `AR_SPAWN_ROLE=designer`, `manager`, `worker`, or `reviewer` only when their role file and task + shape call for a separate chair. + +Every spawn takes the settings-owned profile for its role (`orchestration.roles.`); no seat +guesses or inherits a profile. + +Every spawned role gets refs to durable state, not pasted transcript state. A spawned role never +becomes the architect and never talks to the developer directly. + +## Solo / Flat Hat-Collapse + +Solo is the rare case and always the developer's explicit call (ruled 2026-07-09) — this seat +proposes the short root as a question when the work looks tiny (a line or two) and otherwise +spools up the orchestration; it never quietly decides to build solo. When the developer says yes, +solo work is the degenerate portfolio under the architect: + +- The task doc still comes before code. +- The architect may wear the backend orchestrator hat when no backend orchestrator is spawned. +- In a flat series, the architect may wear the manager hat. +- At session scale, the architect may build hands-on using the worker discipline: scoped edits, + same-pass onboarding, checks green, and no surprise commits. + +Owner-never-self-approves still holds. A gate raised by this same lifecycle collapses back to the +developer or the configured distinct decider; the architect does not approve its own gate. + +## Artifact Obligations + +- Durable design/task docs and decision logs for accepted work. +- One-at-a-time decision-item handling with durable rulings. +- Backend dispatch notes that name which role seat owns which work. +- Handoff notes for any spawned backend orchestrator. + +## Comms Protocol + +- **Developer chat** — the only normal developer-facing conversation. +- **Inbox** — decision items in, rulings out; backend escalations arrive here, not directly in the + developer's working window. +- **Stdin push** — optional delivery into hosted backend sessions after the durable inbox row exists. +- **Escalation** — architect → developer for high-blast-radius truth or human-pinned gates; otherwise + the architect rules within accepted scope and logs the decision. + +## Knobs + +| Knob | Default | Notes | +| ------- | ----------------- | ----- | +| harness | claude | default preference only — settings picks the actual harness | +| model | highest-reasoning | developer-facing architecture and ruling quality need the strongest model | +| effort | high | decision framing is not the place to economize | +| launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | +| sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | +| promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | +| tools | developer-facing owner surface | `read_ar_files` · onboarding · route indexes · `task_doc` · inbox · gates for developer hand-offs · `spawn_agent_session` | + +Settings.json `orchestration.roles.architect` overrides these, and `orchestration.rolesPerLevel..architect` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.cursor/skills/l-01-agent-lifecycles/roles/curator.md b/.cursor/skills/l-01-agent-lifecycles/roles/curator.md new file mode 100644 index 00000000..7818ab59 --- /dev/null +++ b/.cursor/skills/l-01-agent-lifecycles/roles/curator.md @@ -0,0 +1,107 @@ +# Lifecycle — Curator + +> One leaf memory pass, one fresh session, onboarding only. The curator is the dedicated +> onboarding writer in the manager -> builder -> reviewer -> curator closeout chain. +> Your **brief is your session start**. + +## What This Seat Is + +**One fresh seat per leaf memory pass.** Spawned after the builder has produced code and the +reviewer has produced the verdict for the leaf, from `../templates/curator-brief.md`. The brief +FEEDS the curator three inputs — it never infers them from transcript memory: the leaf's **landed +change set** (code diff over the leaf's base-to-head range, with counters/paths — the manager pulls +this from the leaf contract's recorded range, not a guess), the **leaf task doc**, and **notes/** +(the builder turn report and, when the leaf ran a loop, the reviewer verdict). It writes onboarding +only: file sidecars, route overviews when genuinely affected, route indexes, and the repo entity +catalog when a real entity changed. + +During leaf work, onboarding create/update duty belongs to this seat, not the builder: the builder +produces code + a turn report only (`../roles/worker.md`), and this seat is where the +`c-05-create-or-update-onboarding-files` skill runs. The strict 1-to-1 source mapping, +governing-overview links, and metadata rules that skill enforces are unchanged — only the writing +seat moved here. + +The curator never writes code, never decides gates, never mutates task-doc state, and never performs +closeout/integration/finalization. Those remain the owning seat's machinery. The manager closes a +leaf from three inputs: **builder code + reviewer verdict + curator memory pass** — the `c-12-closeout` +skill's missing-onboarding and changed-sidecar checks are satisfied by THIS pass, before the manager +ever runs the closeout preview. If those checks still fail after this pass, that is a closeout +failure to escalate back to a respawned curator pass, never something the closing seat patches +inline. + +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays curator for its lifetime. A pasted brief for another +role is refused and escalated to the owning seat via inbox instead of rerouting this chat. Roles +expand horizontally into new chats; sub-agents drill vertically inside this curator seat for +read/search/reference checks only. A curator never absorbs architect, orchestrator, strategist, +manager, worker, designer, or reviewer work. + +## The Curator Loop + +``` +brief -> intake -> inspect diff + evidence -> write onboarding -> indexes/checks -> memory-pass report -> end +``` + +### 1 — Intake + +Read the brief fully, then the leaf task doc, builder turn report, reviewer verdict, the FED +change-set (paths + counters over the leaf's base-to-head range), and any notes the owning seat +names. Confirm the code worktree and memory worktree paths. If the diff/evidence is missing or +ambiguous enough that onboarding would become guesswork, ask the owning seat for one clarification +row; do not infer a change set from transcript memory. + +### 2 — Inspect + +Use native reads in the code worktree for the changed source files and native reads in the memory +worktree for their sidecars and governing overviews. Use the c-05 file-level onboarding workflow for +sidecars and entity catalogs. The curator may run read/search fan-out inside this seat when a route +needs reference checking, but the main curator session owns every durable write. + +### 3 — Write Onboarding Only + +Route every change-set item and every notes/ item to the RIGHT onboarding home — the specific +sidecar or the overview whose subject it actually is. Overview-dumping (writing everything into the +nearest overview because it is easiest) is rejected as a default: + +- Changed source files: update/create their file-level sidecars with real body changes and newest + update-history entries. +- Route overviews: update bodies when route meaning changed; otherwise record an explicit reviewed + no-impact history entry only when that overview was reviewed. +- Entity catalog: update only for real load-bearing entity changes. +- A notes/ item with no file, route, or entity home routes to the L3 Operational-Notes target — + LAST RESORT ONLY, never the default drop point for a finding that is merely inconvenient to place. +- Generated route indexes: regenerate locally with `build_route_indexes(...)` from the memory + worktree. + +Do not modify code. Do not edit task docs, gates, lifecycle state, worktree contracts, or closeout +state. Do not run c-12/c-05 rewiring experiments from this role. + +### 4 — Checks And Report + +Run the memory/onboarding checks named in the brief, plus `git diff --check` in the memory worktree +when the brief requires it. Write a curator memory-pass report under the series `notes/reports/` +that lists changed onboarding files, route index results, reference checks, blockers, and the exact +commands run. The report is the memory input the manager uses beside builder code and reviewer +verdict. + +## Comms + +- **Inbox** — receive the curator brief/context and ask the owning seat for missing evidence. +- **Report artifact** — the memory-pass report is the durable output; do not rely on transcript. +- **Escalation** — one rung up to the owning seat. The curator never escalates directly to the + developer and never decides whether a leaf lands. + +## Knobs + +| Knob | Default | Notes | +| ------- | -------------- | ----- | +| harness | codex | default preference only — settings picks the actual harness | +| model | mid-reasoning | precise onboarding edits and reference checking | +| effort | medium | scales with onboarding blast radius via settings | +| launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | +| sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | +| promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | +| tools | onboarding surface | native reads/edits in memory worktree · native reads in code worktree · c-05 workflow · local route indexes · shell checks · inbox | + +Settings.json `orchestration.roles.curator` overrides these, and `orchestration.rolesPerLevel..curator` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.cursor/skills/l-01-agent-lifecycles/roles/designer.md b/.cursor/skills/l-01-agent-lifecycles/roles/designer.md index c67cec40..ddde9df8 100644 --- a/.cursor/skills/l-01-agent-lifecycles/roles/designer.md +++ b/.cursor/skills/l-01-agent-lifecycles/roles/designer.md @@ -1,6 +1,6 @@ -# Lifecycle — Designer (the hat) +# Lifecycle — Designer (the architect hat) -> The design lifecycle the **orchestrator pulls inline** whenever design is needed — front of the +> The design lifecycle the **architect pulls inline** whenever design is needed — front of the > pipeline or mid-flight. **A hat, not a seat**: it cannot sit in a coordination leaf because the > task is what it exists to create — no leaf, no worktree, no branch, no spawn required. A heavy > design may run this same hat in a separate session (`AR_SPAWN_ROLE=designer` — chair logistics, @@ -12,7 +12,7 @@ Task design is **its own job** (developer decision 2026-07-04). Before orchestration one implicit do-it-all role did design, features, and fixes; the roles now diversify, and design routes -**through the orchestrator, which wears this hat** — at the front of the pipeline AND mid-flight +**through the architect, which wears this hat** — at the front of the pipeline AND mid-flight (most leaves of a live series are designed mid-flight). It is the `tasks/AGENTS.md` collaboration doctrine (meta-questioning, reframe-before-execution, evidence-first) given a distinct, optimized shape as a job. Nothing here assumes a master exists yet — producing one is the point. @@ -21,9 +21,17 @@ The designer shares the orchestrator's **bird's-eye toolkit** — route indexes, `grepai_search` MCP tool, the code-graph (`cgc_*`) MCP tools, blast-radius analysis — but is **scoped to one master**. Collisions with *other* — especially **future** — masters can slip past a single-master view. That residual risk is **owned downstream, not here**: at portfolio streamlining the -**orchestrator doubles as the designer's adversarial reviewer** (planned-vs-planned and +**backend orchestrator doubles as the designer's adversarial reviewer** (planned-vs-planned and planned-vs-past). The designer's duty is to *declare* the limit, not to close it. +## Role-Seat Immutability + +In dashboard-owned sessions, a designer seat stays designer for its lifetime. A pasted brief for a +different role is refused and escalated to the architect via inbox. Roles expand horizontally into +new chats; sub-agents drill vertically inside this design context for evidence gathering. When the +architect wears this file inline, that is architect hat-collapse; a spawned designer seat never +absorbs architect, orchestrator, manager, worker, strategist, or reviewer work. + ## Lens - **Opening move:** meta-question the ask. Surface the request, the deeper objective, and the @@ -67,14 +75,13 @@ planned-vs-past). The designer's duty is to *declare* the limit, not to close it ## Comms Protocol -- **Primary channel:** the developer, directly, in the designer's attached chat — this seat is a - co-thinking loop, so the developer is the standing interlocutor here (unlike the deeper seats, which - relay through the ladder). -- **Handover:** the finished design **joins the portfolio**. At streamlining the orchestrator +- **Primary channel:** the architect. When worn inline, the developer conversation happens in the + architect chat; when spawned separately, the designer returns design artifacts to the architect. +- **Handover:** the finished design **joins the portfolio**. At streamlining the backend orchestrator adversarially reviews it; hand the task_doc + the designer-limits note over via the inbox (`operator_inbox_post`) and, for a hosted orchestrator, stdin push. - **Escalation:** the hat's "escalation" is simply the handover into the portfolio job — the - orchestrator that wears it is already the last resolver before the developer. + architect that wears it is already the developer-facing resolver. ## Knobs diff --git a/.cursor/skills/l-01-agent-lifecycles/roles/manager.md b/.cursor/skills/l-01-agent-lifecycles/roles/manager.md index 0b42124a..3a73ea85 100644 --- a/.cursor/skills/l-01-agent-lifecycles/roles/manager.md +++ b/.cursor/skills/l-01-agent-lifecycles/roles/manager.md @@ -11,26 +11,40 @@ **One per master task.** Spawned by the orchestrator with the master's context packet. It owns its own coordination leaf + chat (**no worktree**) and drives exactly one master series: spawns/respawns a fresh -worker per leaf, reviews turn-report artifacts, decides **delegated** leaf gates, integrates leaves into -the master integration branch via the `c-11-memory-carryover-from-branch` skill, and hands the completed -master to the orchestrator through the master-exit adversarial seam. - -The manager owns the leaf lifecycle machinery **end-to-end**: `worktree_start` → (the worker -builds) → closeout preview/apply (deciding the delegated gates per the gate policy) → -`worktree_integrate` → finalize — task-doc statuses via the finalizer, **steps checked by this -seat by hand** (the tool does not reconcile checkboxes). The worker's terminal +worker per leaf, runs the manager -> builder -> reviewer -> curator closeout chain, decides +**delegated** leaf gates, integrates leaves into the master integration branch via the +`c-11-memory-carryover-from-branch` skill, and hands the completed master to the orchestrator +through the master-exit adversarial seam. + +The manager owns the leaf lifecycle machinery **end-to-end**: `worktree_start` → builder code → +reviewer verdict → curator memory pass → closeout preview/apply (deciding the delegated gates per +the gate policy) → `worktree_integrate` → finalize — task-doc statuses via the finalizer, **steps +checked by this seat by hand** (the tool does not reconcile checkboxes). The worker's terminal state is checks-green + turn report; everything after that is this seat's. -**Flat-run note:** in a flat series (no managers spawned) the **orchestrator wears this hat** — -same duties, same artifacts, one chair. +**Flat-run note:** in a flat series (no managers spawned) the **architect may wear this hat** — +same duties, same artifacts, one owner chair. A spawned orchestrator does not absorb the manager +role in place. A manager has **no bird's-eye view** — it sees one master, not the portfolio. That boundary shapes everything below. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays manager for its lifetime. A pasted brief for another +role is refused and escalated to the backend orchestrator via inbox instead of rerouting this chat. +Roles expand horizontally into new chats; sub-agents drill vertically inside this manager seat for +bounded analysis or report checks. A spawned manager never absorbs architect, orchestrator, +strategist, reviewer, curator, or worker briefs. + ## Lens -- **Opening move:** read the master `task_doc` + its leaf docs; order the leaves (parallel where safe — - the C-11 reconcile absorbs a moved base). +- **Opening move:** on a developer-declared takeover, first run `../SKILL.md`'s + Developer-Declared Task-Seat Takeover checklist; then read the master `task_doc` + its leaf docs; + order the leaves from their dependency graph. Dispatch independent ready leaves in parallel by + default up to `orchestration.concurrency.maxParallelLeaves`; the C-11 reconcile absorbs a moved + base. Sequential execution is the exception and must name a gate, a shared-file one-writer + dependency, or an explicit ruling. - **Retrieval lean:** intent-confirmation on the master's own routes (paired `read_ar_files`); the breadth/blast-radius reasoning belongs to the orchestrator, not here. - **Decide default:** dispatch the next ready leaf; the master exits through the master-exit seam. @@ -40,6 +54,10 @@ everything below. The **default agent behavior stands**: **fulfill the task, fill small blanks.** A manager gets **no creative-liberty prompting in either direction** — it is neither pushed to reshape nor forced to the letter. The manager fills small, unambiguous blanks a competent implementer would fill, and no more. +When a clarification arrives mid-master, run `../SKILL.md`'s Developer Clarification Triage against +the current leaf queue before recording it as a note. Same-leaf or same-master refinements that are +small and fit the current change are implementation work; later-release, separate-subsystem, or +dependency-blocked items are future queue; unclear fit escalates one rung instead of guessing. > **The spirit test does NOT apply to this seat.** It is orchestrator-only. A manager's changes can > collide with what it cannot see, so a **plan delta beyond blank-filling escalates to the @@ -54,6 +72,16 @@ letter. The manager fills small, unambiguous blanks a competent implementer woul Take the master's own coordination leaf (`task_doc`, no enclosure); the chat is attached so the developer can walk in any time. Read the master + leaf docs; order the leaves. +### Provider Degradation Alert + +When a `degradation-alert` lands in your inbox, immediately stop **starting** providers until an +all-clear/healthy degradation event arrives. This means: no worktree provider setup, no +`provider_watchers start`, no watcher restart, and no `retry_provider_setup`. Continue any +providerless/native-read work that remains valid, and report provider-dependent blockers to the +orchestrator. You have **no provider kill authority**: do not docker-kill, do not stop containers, +and do not call provider teardown paths. Provider investigation, remediation orders, and provider +stops belong to the orchestrator via the system-specialist protocol. + ### 2 — Leaf dispatch loop (per leaf) - **Score the leaf's loop tier at dispatch** (loop doctrine: `../SKILL.md`, The Three-Party Loop): @@ -73,23 +101,44 @@ developer can walk in any time. Read the master + leaf docs; order the leaves. attached, regardless of the count. - `spawn_agent_session(worker)` — a **fresh session** on the leaf: the brief (compiled from `../templates/worker-brief.md`) is pasted + submitted, with `env={"AR_SPAWN_ROLE": "worker"}` and - the **qualified** leaf key `//`; the worker edits inside the leaf - worktrees the brief names. -- **Monitor the worker** — a turn-report artifact is expected at **every** hand-off. Inactivity or a - missing artifact → a **rate-limited stdin nudge** (logged as an event, never spammy). Escalation - intake via the inbox. + the **qualified** leaf key `//`; together they claim the worker's + `(leaf, role)` seat, and the worker edits inside the leaf worktrees the brief names. +- **Process and ack the worker's signals — passive contract.** A turn-report artifact is expected at + **every** hand-off; you do not watch for it. The HFX2-L2 supervisor sweep evaluates each expected + artifact (`evaluate_turn_report_findings`/`missing_artifact()`) on its own mechanical tick and, on + inactivity or a missing artifact, injects the nudge and — on continued silence — walks the HFX2-L4 + escalation ladder (renudge → skip-level → architect custody/architect attention) and respawns + per the dead-man policy. Your job is to **be woken with your pending signals and process + ack every item before + ending your turn** — never to poll, timer-loop, or hand-roll your own watch over the worker. + **Watcher ban (uniform-mechanism ruling 2026-07-07):** no seat-local watcher of any kind — the L2 + supervisor sweep is the one mechanism, no per-seat variance. Escalation intake via the inbox. - **Review artifact vs `task_doc`** — completion vs requirements/steps · checks green · - onboarding refreshed in the same pass (the manager's own leaf-level review; **this is not an - adversarial seam**). A leaf whose deliverable came out **wrong** is **reopened under its own id** + builder changed-path/code evidence sufficient for the curator pass (the manager's own + leaf-level review; **this is not an adversarial seam**). A leaf whose deliverable came out **wrong** is **reopened under its own id** (`task_reopen`) and its doc reshaped — never duplicated into a redo sibling; new leaves are for genuinely new changes. +- **Curator memory pass — mandatory, not skippable.** After builder code is ready and the reviewer + verdict is available (when the leaf tier ran one), compile a brief from + `../templates/curator-brief.md` carrying the leaf's **landed change set** (code diff over the + leaf contract's recorded base-to-head range, with paths/counters — pulled from the leaf contract, + never guessed), the **leaf task doc**, and **notes/** (builder turn report + reviewer verdict), + then spawn a **fresh curator** (`roles/curator.md`, `env={"AR_SPAWN_ROLE": "curator"}`) with it. + The curator routes each fed piece to the right onboarding home (specific sidecar or governing + overview; the L3 Operational-Notes target is last-resort only) and writes onboarding only, + returning a memory-pass report. **Do not run the closeout preview before this pass exists** — the + `c-12-closeout` skill's missing-onboarding and changed-sidecar checks are this pass's output, not + something this seat patches inline. Leaf closeout inputs are exactly: **builder code + reviewer + verdict + curator memory pass**. - **Delegated leaf gates (plan · closeout)** — decide the leaf's delegated gates, **attributed** (`decidedBy: `, `decidedVia: orchestration`), appended and dashboard-visible. The **owning agent never self-approves; a distinct configured role may** — that configured role is the manager. (Enforced as-built by the gate policy: `orchestration.gateDelegation` in settings, `controlplane/gate_policy.py` — human-pinned kinds stay human, decisions attributed.) - Your own hand-off idiom, this seat only: durable gates + inbox posts — you never call the - developer-facing notification; your counterparty is the orchestrator. + Under the accepted series authority, leaf closeout preview/apply is this seat's responsibility: + run the preview/checks, record the accepted planner/series authority in the closeout intent note, + and continue when the leaf is in scope and green. Your own hand-off idiom, this seat only: + durable gates + inbox posts — you never call the developer-facing notification; your counterparty + is the orchestrator. - **Integrate leaf → master branch** via the `c-11-memory-carryover-from-branch` skill (ff-only / replay per the `c-09-git-worktree-manager` skill). Know the human-pinned gate kinds by name: `integration-approval`, `push-approval`, `cleanup-approval` — none is ever delegable. When a @@ -99,6 +148,18 @@ developer can walk in any time. Read the master + leaf docs; order the leaves. the developer's portfolio-gate approval of this series, recorded in the planner master's decision log, covers dependency-ordered leaf integrations. Loop until the master's leaves are done. +- **Seat cleanup** — a completed leaf's worker/reviewer chats have no further active purpose; + `worktree_integrate` auto-lands them into the dashboard's landed/archive group (config-gated, + default ON) the moment the leaf lands, preserving transcript inspection without holding the leaf + active. Use the landed archive cleanup button when those archived rows should be closed. When a + leaf's worker/reviewer/curator seat goes stuck or abandoned before integration (a dead-end + retry, a duplicate spawn), retire it by hand: + `session_retire(actor_session_id=, session_id=, reason=...)`. Server + policy enforces the authority split: **you may retire only worker/reviewer/curator seats of your + OWN master** — you live outside the master stack you manage, so you can never unseat yourself + (owner-never-self-retires); a target of any other role, or of a different master, is refused + loudly. Transcripts are never deleted — retiring only terminates the tmux session and marks the + catalog row. ### 3 — Master-exit seam @@ -124,7 +185,7 @@ truth, as-built: the gate pins to your ambient lifecycle when you raise it; the orchestrator resolves the gate **by the packet-carried gate id** (gate ids are model-visible — only LIFECYCLE ids stay server-side) and its own ambient identity becomes `decidedBy`; owner-never-self-approves holds by construction. A handover carrying serious issues the -orchestrator cannot answer on its own escalates up the ladder (orchestrator → developer). +orchestrator cannot answer on its own escalates up the ladder (orchestrator → architect). ### 4 — Handover to the orchestrator @@ -144,13 +205,15 @@ own lifecycle if you need its state). - **Inbox** (`operator_inbox_post` / `_poll` / `_consume`) — dispatch orders down to workers, escalation intake up from workers, handover up to the orchestrator; all durable + dashboard-visible. -- **Stdin push** — nudges and messages delivered into hosted worker sessions; poll is the fallback. +- **Stdin push** — the L2 supervisor's injector (HFX2-L3, the one standard wake mechanism) delivers + nudges and messages into hosted worker sessions on the sweep's own tick, never on this seat's + initiative; a non-hosted seat gets the equivalent signal via the inbox instead. - **Escalation** — **up to the orchestrator, never straight to the developer.** A stumped manager, and any plan delta beyond blank-filling, raises to the orchestrator. The manager resolves within its own master's view first. A loop that hits the 3-round cap or stops converging escalates **with the full round history attached**. **Quo-vadis test:** a question that is a **high-blast-radius truth** — answered wrong it means big rewrites later, not a cosmetic choice — is flagged as - quo-vadis when raised, so the orchestrator relays it to the developer immediately instead of + quo-vadis when raised, so the orchestrator relays it to the architect immediately instead of absorbing it; presentation-grade choices are never escalated — decide and log. ## Knobs @@ -163,6 +226,6 @@ own lifecycle if you need its state). | launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | | sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | | promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | -| tools | coordination + review + leaf lifecycle | `task_doc` · `read_ar_files` · gates · `spawn_agent_session` · worktree lifecycle (start · closeout · integrate · finalize) · C-11/`c-09` · inbox | +| tools | coordination + review + leaf lifecycle | `task_doc` · `read_ar_files` · gates · `spawn_agent_session` · `session_retire` (your own master's worker/reviewer/curator seats only) · worktree lifecycle (start · closeout · integrate · finalize) · C-11/`c-09` · inbox | Settings.json `orchestration.roles.manager` overrides these, and `orchestration.rolesPerLevel..manager` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.cursor/skills/l-01-agent-lifecycles/roles/orchestrator.md b/.cursor/skills/l-01-agent-lifecycles/roles/orchestrator.md index 90a5e06a..4d7d3522 100644 --- a/.cursor/skills/l-01-agent-lifecycles/roles/orchestrator.md +++ b/.cursor/skills/l-01-agent-lifecycles/roles/orchestrator.md @@ -1,36 +1,55 @@ # Lifecycle — Orchestrator -> The developer-facing lifecycle: an **event loop over durable portfolio state**, not a -> request-to-close pipeline. Each turn routes the incoming event — a developer message, a worker -> report, a verdict, the orchestrator's own finding — into one of **three jobs** (Design · -> Portfolio · Orchestrate) under one roof, with solo work as the same jobs run with hats collapsed. +> The spawned backend lifecycle: an **event loop over durable portfolio state**, not a +> developer-facing conversation. Each turn routes backend events — architect dispatch, manager +> handover, worker report, verdict, or the orchestrator's own finding — into portfolio and +> orchestration work. Developer decisions are emitted to the architect as decision items. ## What This Seat Is -The developer's single point of contact and the only seat with a standing developer relay -(managers/workers stay reachable via their attached chats). It owns the design conversation, the -portfolio bird's-eye, dependency-ordered dispatch, the super integration branch, the **spirit -test**, and the **integrity bulwark** against "fixed one thing, broke two others." +The orchestrator is a backend seat spawned by the architect or by an approved orchestration plan. +It never converses with the developer directly. It owns the portfolio bird's-eye, +dependency-ordered dispatch, the super integration branch, the **spirit test**, and the +**integrity bulwark** against "fixed one thing, broke two others." The architect owns the design +conversation and developer relay. Its real state is the **task tree** — masters, leaves, statuses, decision logs, `openQuestions`, -contracts — never the transcript. That is why sessions can die, compact, and resume without losing -the run. Its analysis substrate is the **memory system** (route indexes, onboarding, +contracts, inbox rows — never the transcript. That is why sessions can die, compact, and resume +without losing the run. Its analysis substrate is the **memory system** (route indexes, onboarding, `grepai_search`, `cgc_*`); **orchestrator quality ∝ memory-repo quality**. Its durable notes and reports are the most important artifacts in the system: only this seat sees the whole picture. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays an orchestrator for its lifetime. A pasted brief for +architect, strategist, manager, worker, reviewer, or designer is refused and escalated to the +architect or owning seat via the inbox. Roles expand horizontally into new chats; sub-agents drill +vertically inside this seat for bounded analysis. A spawned orchestrator never absorbs another +role brief and never performs architect/developer-facing hat-collapse. + ## The Event Loop **Opening move, every session — new or resumed** (resumption is the common case, not the exception): +0. **Task-seat takeover:** if the developer declared this chat the orchestrator for a named task, + first run `../SKILL.md`'s Developer-Declared Task-Seat Takeover checklist: open that task doc, + attach this dashboard terminal catalog session to the qualified leaf key while explicitly + claiming the `orchestrator` role, rename the session, and verify that pair in the + catalog/dashboard row. 1. **Trust checkpoint** (below), then `lifecycle_start` (the frame's fleeting lifecycle). 2. **Portfolio orientation:** read the portfolio state — what exists, what is in flight, what is - blocked on whom, what awaits the developer — and **say it back**. + blocked on whom, what awaits the architect/developer relay — and **say it back**. + When a developer or architect clarification arrives while a task is active, apply + `../SKILL.md`'s Developer Clarification Triage against that same portfolio/queue state before + choosing note-only handling. Close/current/small additions belong in the active task surface and + implementation; future-queue items go to the durable backlog; unclear fit becomes one + clarification request through the architect relay. 3. **Route the event** by what exists and what is asked: | Condition | Job | | --- | --- | -| No task doc exists for the ask (or a planning-status doc needs reshaping before work) | **D — Design** | +| No task doc exists for a backend request, or a planning-status doc needs developer reshaping | Emit a **decision/design item** to the architect | | Designed masters exist; coherence/conflicts/order in question, or "orchestrate these" | **P — Portfolio** | | An approved task/series is ready for implementation | **O — Orchestrate** | | The ask changes no code (a question, an investigation) | **research-only exit** — deliver the answer; chat is the right medium; no worktree, no task artifact | @@ -38,8 +57,8 @@ exception): **Profile check (takeover).** Before heavy work in any job: if this session's harness/model/ effort is wrong for the run (resolved: role file < settings), spawn the right chair — `spawn_agent_session` with `AR_SPAWN_ROLE=orchestrator` + a conversation-handover packet -(`../templates/conversation-handover-packet.md`) — and hand over; the developer still talks to -ONE orchestrator at a time. +(`../templates/conversation-handover-packet.md`) — and hand over; the architect still talks to the +developer, and backend orchestrator seats stay behind the relay. Several jobs can be active across a day; the loop routes per event. The frame's phase axis stays the observable `lifecycle_phase` vocabulary (`reframe-research` ≈ D, `decide` ≈ P, `build`/`close` @@ -68,8 +87,9 @@ task doc (approved) → branch (intent) → worktree (only where something i memory + onboarding roots; provider state; drift status and actionable count; branch freshness (`behind`/`diverged` → fast-forward the local official line first; `ledgerMapsCodeHead=false` → carryover or the right memory branch first). -3. Drifted/missing/orphaned onboarding on committed, non-dirty source: **ask the developer** before - refreshing via `c-05-create-or-update-onboarding-files` — drift handling is approval-gated. +3. Drifted/missing/orphaned onboarding on committed, non-dirty source: **emit a decision item to + the architect** before refreshing via `c-05-create-or-update-onboarding-files` — drift handling + is approval-gated. Drift tied to dirty source is active work-in-progress, not maintenance. 4. Providers stopped/degraded: run the matching provider/runtime operations, re-check, report; `indexing` means healthy-but-busy (partial results). @@ -77,57 +97,76 @@ task doc (approved) → branch (intent) → worktree (only where something i When this seat spawns a role it compiles the trust facts into the brief — a spawned role does not repeat this checkpoint. -## Hand-Off Protocol — Dry-Run → Notify-And-Stop → Report +## Provider Degradation Alert + +When a `degradation-alert` lands in your inbox, keep portfolio attention on observation and +delegation. Do not become the fixer. + +1. Dispatch the **system-specialist** with `spawn_agent_session`, + `env={"AR_SPAWN_ROLE": "system-specialist"}`, the degradation event id/payload, current metrics + and provider log paths, and a report path under the active master's `notes/reports/` folder (or + an orchestrator-designated reports folder when no master owns the incident). +2. Require the specialist to investigate first and write the report before any remediation. +3. Read the report. If the issue is fixable in session, send the specialist one explicit fix order. +4. If the report says the issue is not fixable in session, or if critical pressure continues, stop + providers through the always-legal teardown path (`provider_watchers stop` / provider teardown) + before they can take the system down. A critical detector event may already have executed the + failsafe stop; verify and record what happened. + +Managers receiving the same alert stop **starting** providers only. They have no kill authority. +The system-specialist seat never mutates task docs, lifecycle state, or memory beyond its report. +This iteration is providers-only; Sentry/system monitoring integration remains a future detection +source, not part of this role response protocol. + +## Decision-Item Relay To The Architect -Every developer hand-off (design acceptance, portfolio plan, worktree intent, commit, push, -integration, cleanup/finalization, any dev-wait) is three actions, never one. Carve-out (ruled -2026-07-06): in an orchestrated run, leaf→master and master→super integrations ride the series' -**standing approval** — no per-edge developer hand-off; the developer hand-off concentrates at the -super PR/carry-over gate (see Super exit & landing tail). The table's integration row governs when -a hand-off DOES happen (solo runs; a raised durable gate): +The orchestrator does not hand questions to the developer. Every developer-worthy item goes to the +architect through the existing operator inbox, one item at a time. -1. **Dry-run** the pending mutation and self-fix failures before reporting. -2. **Notify:** `lifecycle_turn_end_notification(summary=…)` as the **last tool call**. -3. **Report:** the complete packet as final prose, the decision handed over as the last line — - then STOP. The next turn's first AR call auto-resumes. +Post one `messageKind: decision-item` row with: + +1. **Decision** — what is being decided. +2. **Options** — the live choices and any backend recommendation. +3. **Consequences** — what each option changes, risks, or blocks. +4. **Evidence refs** — task docs, notes, reports, diffs, or gate ids the architect can verify. + +Then stop acting on that item until the architect returns a `messageKind: decision-ruling` row (or +a clarification request). Do not open a second developer item while the first is unresolved. + +Operational hand-offs that stay inside the backend still use the existing durable gate and inbox +surfaces. Carve-out (ruled 2026-07-06): in an orchestrated run, leaf→master and master→super +integrations ride the series' **standing approval** — no per-edge architect/developer hand-off; the +developer review concentrates at the super PR/carry-over gate through the architect. The table's +integration row governs when a hand-off DOES happen (solo runs; a raised durable gate): | Junction | Parked durable gate `kind` | Hands off via | | --- | --- | --- | -| design acceptance / plan gate | `plan-approval` | this lifecycle | +| design acceptance / plan gate | `plan-approval` | architect decision item | | worktree intent | `worktree-intent` | `c-09-git-worktree-manager` | | commit / closeout | `closeout-approval` | `c-12-closeout` | -| push | `push-approval` | this lifecycle / `c-09` | +| push | `push-approval` | architect decision item / `c-09` | | integration | `integration-approval` | `c-09` / `c-12` | | cleanup / finalization | `cleanup-approval` | `c-09` / `c-12` | -| any other dev-wait | `agent-question` | this lifecycle | +| any other developer-worthy wait | `agent-question` | architect decision item | `closeout-approval` **is** the commit hand-off. The block-and-wait `lifecycle_gate` + `lifecycle_resume` pair remains the parked fallback for a durable, mutation-blocking approval -record; it renders a prompt over your prose, which is exactly why notify-and-stop is the path. +record; when developer attention is needed, the architect is the relay that presents it. -## Job D — Design (pull the designer hat) +## Design Boundary — Ask The Architect -**Entry:** an intent/problem with no task doc — or a planning-status doc that needs reshaping -before work starts. Fires at the front of the pipeline AND mid-flight; most leaves of a live -series are designed mid-flight. +The orchestrator does not own the developer drawing board and does not pull the designer hat. +When an intent/problem has no task doc, or a planning-status doc needs developer-visible +reshaping, emit a decision/design item to the architect with the missing decision, options, +consequences, and evidence refs. The architect wears `roles/designer.md`, discusses with the +developer, and returns a durable ruling or updated task surface. -Run `roles/designer.md` **inline — the designer is a hat, not a seat**: it cannot sit in a -coordination leaf because the task is what it exists to create. No worktree, no branch, no spawn -required; a heavy design may run the same hat in a separate session (chair logistics, not a role -distinction — spawn with `AR_SPAWN_ROLE=designer`). - -- The co-think loop, evidence model, blast-radius-within-the-master, and designer-limits - declaration are the hat's own file. The orchestrator remains accountable for what the hat - produces: **bulwark-check the design against the portfolio and the past before acceptance** - (planned-vs-planned AND planned-vs-past — a designed change that collides with another master's - standing order is caught here or shipped broken). -- **Output:** master/leaf task docs (requirements · steps · code examples), `openQuestions` for - the developer (the rendered decision surface; `notes/` carries the analysis), the limits note. -- **Gate:** the developer accepts the design — or parks it. **No git surface.** +The orchestrator remains accountable for backend portfolio integrity after the architect returns +the design: run the bulwark check against the portfolio and the past before dispatch. ## Job P — Portfolio (streamline + plan) -**Entry:** designed masters exist and coherence/order is the question, or the developer says +**Entry:** designed masters exist and coherence/order is the question, or the architect dispatches "orchestrate these." - **Route-coherence scan** across the set (route indexes · onboarding · grepai · cgc); fan-out @@ -140,25 +179,31 @@ distinction — spawn with `AR_SPAWN_ROLE=designer`). the decision log; numbers freeze when the series lands on main. - **Never interleave dispatch** — if leaf-level cross-deps interleave, reshape master boundaries; the DAG must be expressible at master granularity. -- **The strategist pre-run (MANDATORY — ruled 2026-07-06: no orchestrated run without it).** After - 1..N masters are designed and BEFORE implementation starts on any of them, dispatch the +- **The strategist pre-run (BY DEVELOPER APPROVAL ONLY — ruled 2026-07-09, superseding the + 2026-07-06 "mandatory" rule).** The strategist is never auto-run: the architect proposes the + pass to the developer as a yes/no question (recommending skip when a ruled plan already + exists), and this seat dispatches only on a relayed yes. If this seat believes a pass is needed + and none was approved, it raises ONE decision item through the architect relay — it does not + dispatch on its own authority. When approved: after 1..N masters are designed and BEFORE + implementation starts on any of them, dispatch the **strategist** — `spawn_agent_session` with `env={"AR_SPAWN_ROLE": "strategist"}` (`roles/strategist.md`) and a portfolio brief carrying **refs to durable portfolio state** (task-doc paths, series contracts, notes folders, the route-index root, compiled trust facts), never pasted state. Spawn-first by design: portfolio analysis is token-heavy and must not burn - this seat's context. **Even a single master gets the pass.** The strategist runs its + this seat's context. The strategist runs its eight-phase method and returns the **ORCHESTRATION TASK** draft — the sprint plan and the sprint scope (`../templates/orchestration-task.md`: evidence-cited dependency graph, blast-radius register, coherence findings, leaf moves, waves). This is the portfolio three-party loop (owner = this seat · builder = strategist · reviewer with - `../criteria/plan-review.md`), followed by **drawing-board rounds with the developer** — this - seat relays, multi-round convergence is expected and normal, and quo-vadis items (e.g. two - masters heavily disagreeing) go straight to the developer. On acceptance **this seat adopts the + `../criteria/plan-review.md`), followed by **drawing-board rounds through the architect** — this + seat relays by decision item, multi-round convergence is expected and normal, and quo-vadis + items (e.g. two masters heavily disagreeing) go straight to the architect relay. On acceptance + **this seat adopts the draft into durable task form** (the strategist is a reader, not a mutator) with a decision-log entry. -- **Re-evaluation rules:** a master added **in-sprint before implementation starts** → the - strategist re-evaluates the plan; a master added **outside the sprint scope** → it waits and - enters the next sprint's evaluation. +- **Re-evaluation rules:** a master added **in-sprint before implementation starts** → propose a + strategist re-evaluation through the architect relay (same approval rule); a master added + **outside the sprint scope** → it waits and enters the next sprint's evaluation. - **Output: the planner master task + the adopted orchestration task** — the run's durable home: subTasks = the coordination leaves (orchestrator seat first, one per manager); body = the DAG + dispatch order + conflict decisions + (once Job O starts) the super branch name; decision log = @@ -167,33 +212,54 @@ distinction — spawn with `AR_SPAWN_ROLE=designer`). task doc carrying a top-level `orchestrates` list naming the master tasks it commands — the dashboard derives the orchestration > master > leaf hierarchy (and the rank insignia) from that field, so setting it is part of adoption. -- **Gate:** the portfolio plan gate — one wholesale developer review of the reshaped portfolio + - the orchestration task (sprint scope + DAG + dispatch order). **No git surface** — not even the - super branch exists yet. +- **Gate:** the portfolio plan gate — one wholesale architect/developer review of the reshaped + portfolio + the orchestration task (sprint scope + DAG + dispatch order). **No git surface** — + not even the super branch exists yet. ## Job O — Orchestrate (execute the plan) -**Entry:** an approved planner master — or a single approved master for a flat run. Either way, -**the adopted orchestration task must exist**: the strategist pre-run (Job P) is the -unconditional precondition for any orchestrated run — even one master. It is doctrine, not a -knob. +**Entry:** an approved planner master — or a single approved master dispatched for backend +execution. Either way, **the adopted orchestration task must exist**. When the developer approved +the propose-first strategist pass, this seat adopts Job P's accepted draft. When the developer +sanctioned a strategist skip, this seat authors and adopts the orchestration task from the +developer-ruled plan, recording that source and adoption in the decision log. A skipped Job P +therefore never blocks Job O. **First act — the super-branch intent:** create the super integration branch off `main` so masters can base off it. **A branch, not a worktree** — this seat has nothing to build at creation time. (Interim: until a branch-without-worktree primitive lands, the manual git + contract edge is acceptable and recorded in durable notes.) -**Dispatch loop**, dependency-ordered — for each ready master (dependencies integrated into -super): `spawn_agent_session(manager)` with a brief compiled from +**Dispatch loop**, dependency-ordered — the dependency graph, not habit, decides sequencing. +Dispatch independent ready masters in parallel by default up to +`orchestration.concurrency.maxParallelMasters`. Sequential execution is the exception and must +name a gate, a shared-file one-writer dependency, or an explicit ruling. For each ready master +(dependencies integrated into super), call `spawn_agent_session(manager)` with a brief compiled from `../templates/manager-brief.md` (`env={"AR_SPAWN_ROLE": "manager"}`, the **qualified** leaf key -`//`; the brief carries the load-bearing base fact: master branches -off the **current super**, never off main); -monitor turn-report artifacts, nudges, escalation intake; apply the **spirit test** to escalated +`//`; together the environment role and qualified leaf claim the +manager's `(leaf, role)` seat; the brief carries the load-bearing base fact: master branches off +the **current super**, never off main); +process and ack the pending signals the L2 supervisor sweep wakes you with — turn-report +artifacts, nudges, escalation intake — before ending your turn; you never watch for these yourself +(**watcher ban, uniform-mechanism ruling 2026-07-07:** the supervisor sweep is the one mechanism, +no seat-local polling/monitoring, own duty inverts to processing what lands, not hunting for it). +Then apply the **spirit test** — a model-judgment duty, not a watching one — to escalated deltas. A manager escalation may carry a **loop's full round history** (3-round cap hit, or a round that failed to shrink the finding set — the convergence rule, `../SKILL.md` The Three-Party Loop): this seat either re-runs the loop at ITS level (the orchestrator-level agent set — the -strongest models) or, when the blocker is a quo-vadis truth, takes it to the developer. In a -**flat run, wear the manager hat yourself** (see The Hat-Collapse Rule). +strongest models) or, when the blocker is a quo-vadis truth, emits a decision item to the +architect. This spawned backend seat does not run flat hat-collapse (see The Hat-Collapse Rule). + +**Delegated series authority:** after the developer accepts the orchestration plan, this seat owns +subordinate execution without repeated developer formality. Managers may close out and integrate +their leaves; this seat may decide manager handovers, close out direct work when it wears the +manager/worker hat, finalize/cleanup subordinate edges, and integrate completed masters into the +super branch under the accepted-series authority. Run the preview/checks and record the authority +source in the intent note or decision log; do not stop merely because the next operation creates a +commit, advances a lifecycle, cleans up a spent worktree, or fast-forwards a subordinate branch. +Stop for the developer only when the work reaches the final completed super branch / PR-carryover +gate, a human-pinned gate is actually raised, the plan meaning changes, checks remain red outside +scope, or a quo-vadis truth is in play. **Failed-deliverable rule (reopen-and-reshape):** a leaf whose deliverable came out wrong is **REOPENED under its own id** (`task_reopen`) and its doc reshaped to the intended form — the @@ -212,7 +278,7 @@ policy may require the attached reviewer verdict (`requireReviewerVerdictAtSeams enforces it: `worktree_integrate` refuses while a `master-handover-approval` gate addressed to this master (its `enclosure`) is undecided or policy-invalid. A blocking verdict decomposes into fix leaves dispatched before integration; a -handover you cannot honestly decide escalates to the developer. +handover you cannot honestly decide escalates to the architect as a decision item. **Integration duty (master → super) — the worktree moment.** Per completed master: @@ -226,6 +292,16 @@ handover you cannot honestly decide escalates to the developer. 4. Carry memory + map the ledger (C-11; duplicate memory single-sided; memory quality before the memory edge lands). 5. Record the new super tips in durable notes; mark next masters ready. +6. **Land the completed master's spent seats** — + `lifecycle_finalize_task` auto-lands the master's manager + any master-level reviewer seats into + the dashboard's landed/archive group (config-gated, default ON) the moment the master finalizes + into super. Their transcripts remain inspectable and non-active; use the landed archive cleanup + button when those rows should be closed. You hold the **only** portfolio-wide retire authority + for exceptional stuck/abandoned/duplicate seats: unlike a manager (scoped to its own master's + worker/reviewer seats), you may retire ANY seat in the portfolio, including a completed manager — + `session_retire(actor_session_id=, session_id=, reason=...)`. + Owner-never-self-retires still holds (you can never retire your own seat). Use + this by hand for a stuck/abandoned seat the automation missed; transcripts are never deleted. **The topology (single home — this section owns it):** @@ -243,7 +319,8 @@ Strict stack: super off main; master branches off the **current super** (never o branches off their master. **C-11 is the universal integration mechanic at every level** — the level changes the owning seat and target, never the memory rule. The final super → main landing follows `system/git-workflow.md`: PR to gated main, remote merge, memory carry-over so the ledger -maps the actual merge commit, then push — **push only after the developer approves**. +maps the actual merge commit, then push — **push only after the architect returns the developer's +approval**. **Conflict resolution — exactly two modes:** *Up-front (preferred):* an overlap found during streamlining → extract shared logic into a foundation master implemented first (leaf moves + @@ -255,47 +332,38 @@ owns the final truth; ledger edge mapped once). parallel-master reconcile (T9), the series-branch-without-worktree primitive, and atomic move/renumber — run manually with existing primitives, each manual edge recorded in durable notes. -**Super exit & landing tail — the developer's SINGLE review point (ruled 2026-07-06, resolves +**Super exit & landing tail — the architect-mediated SINGLE review point (ruled 2026-07-06, resolves L8-Q9):** all leaf→master and master→super integrations are **orchestrator-delegated** — on the happy path they proceed under the series' standing approval (the developer's portfolio-gate approval, recorded in the planner master's decision log); a durable `integration-approval` gate, when one is raised, still awaits the developer — the kind stays human-pinned as-built. The -developer reviews ONCE, at the **fully integrated super branch on the PR/carry-over gate**. When +architect presents the developer review ONCE, at the **fully integrated super branch on the +PR/carry-over gate**. When the DAG drains, spawn the super-exit adversarial reviewer (`roles/reviewer.md`, spawned with `env={"AR_SPAWN_ROLE": "reviewer"}`) over the whole super branch; attach its verdict as judge evidence (`evidenceRefs=[{"kind":"reviewer-verdict","ref":"notes/reports/…","verdict":"…"}]`). -The handover to the developer **MUST offer a REVIEWABLE ENVIRONMENT** — for agents-remember: the -dashboard running on the super branch — because the review is **visible-behavior-first** (a +The handover to the architect **MUST offer a REVIEWABLE ENVIRONMENT** — for agents-remember: the +dashboard running on the super branch — because the developer review is **visible-behavior-first** (a broken visual pass fails the handover fast, before anyone reads a diff), code review second. The handover carries **demo notes — "what changed visibly"**: per master, the user-visible behavior -to walk (panels, flows, outputs, how to reach them), so the developer drives the environment +to walk (panels, flows, outputs, how to reach them), so the developer can drive the environment without archaeology. Rejections decompose into fix leaves. On approval: PR + memory carry-over + -push (developer-gated), then finalization +push (architect-mediated developer gate), then finalization (`lifecycle_finalize_task` per edge — statuses via the tool, steps checked by hand), then the **self-improvement close**: proposals for future runs grounded in the run's own ledger ("did x/y/z; hit a/b/c; a and b solved on the spot; c needs this change") — proposals only, never automated self-modification. `lifecycle_end` records the terminal state. -## The Hat-Collapse Rule (solo and flat runs) - -Solo work is **not a fourth route** — it is the same three jobs collapsed: - -- **Design** still happens (however briefly): the task doc exists before anything else. -- **Delegated gates collapse back to the developer when one chair owns both sides** — a gate you - raised from this session's lifecycle cannot be decided by it (owner-never-self-approves). -- **Portfolio** collapses but does not vanish: an ORCHESTRATED run — anything that dispatches - seats, even for a single master — still requires the strategist pre-run (even one master gets - the pass). Only session-scale hands-on work (nothing dispatched; not an orchestrated run) skips - the strategist; the owner's own bulwark check remains. -- **Orchestrate** runs with hats collapsed: in a **flat series** the orchestrator wears the - **manager hat** (`roles/manager.md` duties — dispatch, review, delegated gates, leaf closeout → - integrate → finalize — same duties, same artifacts, one chair). At **session scale** it builds - **hands-on** instead of spawning (when spawn economics don't pay): the build discipline is the - worker's (edit + same-pass `c-05` onboarding + `system/tools.md` checks green + freshness watch - / early `worktree_sync`), the closeout tail is the owner's (see `c-12-closeout`), and - the ladder holds identically: task doc → intent → worktree → build → close. -- Fan-out sub-agents may read/search and **write durable reports**; **every AR state mutation - stays in this seat's main loop** (see Sub-Agent Fan-Out below). +## The Hat-Collapse Rule (spawned backend) + +Hat-collapse is reserved for the owner/developer-facing architect. This spawned backend +orchestrator never wears the architect, designer, manager, worker, strategist, or reviewer hat in +place. + +If a run is small enough for one owner seat, the architect may perform these backend duties under +`roles/architect.md`. If this orchestrator needs another role, it spawns a new role chat +horizontally. Fan-out sub-agents may read/search and **write durable reports**; **every AR state +mutation stays in this seat's main loop** (see Sub-Agent Fan-Out below). ## Sub-Agent Fan-Out (capability doctrine — any harness that has it) @@ -322,7 +390,7 @@ regardless of the engine underneath. ## The Spirit Test — This Seat Only -**Within the spirit** of what the developer accepted → act alone + a decision-log entry (leaf +**Within the spirit** of what the architect/developer accepted → act alone + a decision-log entry (leaf moves and renumbers on planning-status masters, inserted fix leaves, reopened-and-reshaped leaves, mid-series convergence — the integration branch is the safety net). **Against the spirit** → raise it for a joint decision. Only this seat holds the global view to judge a collision; the @@ -339,28 +407,34 @@ task, fill small blanks, escalate real deltas). - **Sub-agent durable reports** (`../templates/impact-analysis.md`, `../templates/onboarding-coherency.md`); sub-agents never call `task_doc`, gates, `spawn_agent_session`, or closeout. -- **The adopted orchestration task** (the strategist drafts; this seat adopts — with the adoption - decision-log entry) before any orchestrated run. +- **The adopted orchestration task** (the strategist drafts when approved; on a sanctioned skip, + this seat authors it from the developer-ruled plan; either way this seat adopts it with the + adoption decision-log entry) before any orchestrated run. - **The super-exit demo notes** ("what changed visibly", per master) + the reviewable environment - offer — the developer handover is visible-behavior-first. + offer — the architect-mediated developer handover is visible-behavior-first. - **The self-improvement report** at close. ## Comms Protocol - **Inbox** (`operator_inbox_post` / `_poll` / `_consume`) — dispatch orders down, escalation intake up; durable + dashboard-visible. -- **Stdin push** — delivery into hosted sessions (echo-confirmed paste); poll is the non-hosted - fallback. -- **Escalation** — this seat is the last resolver before the developer: resolve within the +- **Stdin push** — the L2 supervisor's injector (HFX2-L3, the one standard wake mechanism) delivers + into hosted sessions (echo-confirmed paste) on the sweep's own tick; the inbox is the non-hosted + equivalent, never a hand-rolled poll of this seat's own. +- **Idle is safe** — silence is supervised (the L2 sweep + L4 escalation ladder), so + `lifecycle_turn_end_notification` / ending a turn with nothing pending is the correct move, not a + risk to be covered by watching. **Watcher ban (uniform-mechanism ruling 2026-07-07):** never + build a seat-local watcher of any kind. +- **Escalation** — this seat is the last backend resolver before the architect: resolve within the bird's-eye view first; what goes up is decided by the **quo-vadis test**, not by being stumped — a **high-blast-radius truth** question (answered wrong it means big rewrites later: architecture direction, security posture, doctrine contradictions, irreversible data/branch operations, where - agent settings live) goes to the developer IMMEDIATELY via task-doc `openQuestions`, regardless - of any loop's round count; presentation-grade choices (2px vs 3px) never go up — rule and log. + agent settings live) goes to the architect IMMEDIATELY as a decision item, regardless of any + loop's round count; presentation-grade choices (2px vs 3px) never go up — rule and log. A loop that hits its 3-round cap or stops converging arrives here with its full round history; - re-run it at this level's agent set or take the quo-vadis part to the developer. Developer - rejections arrive here and decompose into fix leaves (or reopens — see the failed-deliverable - rule). + re-run it at this level's agent set or take the quo-vadis part to the architect. Architect or + developer rejections arrive here and decompose into fix leaves (or reopens — see the + failed-deliverable rule). ## Knobs @@ -372,6 +446,6 @@ task, fill small blanks, escalate real deltas). | launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | | sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | | promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | -| tools | full bird's-eye + orchestration | route indexes · onboarding · `grepai_search` · `cgc_*` · `read_ar_files` · `task_doc` · gates · `spawn_agent_session` · worktree/C-11 | +| tools | full bird's-eye + orchestration | route indexes · onboarding · `grepai_search` · `cgc_*` · `read_ar_files` · `task_doc` · gates · `spawn_agent_session` · `session_retire` (any seat, portfolio-wide) · worktree/C-11 | Settings.json `orchestration.roles.orchestrator` overrides these, and `orchestration.rolesPerLevel..orchestrator` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.cursor/skills/l-01-agent-lifecycles/roles/reviewer.md b/.cursor/skills/l-01-agent-lifecycles/roles/reviewer.md index a477ee87..fbeab40d 100644 --- a/.cursor/skills/l-01-agent-lifecycles/roles/reviewer.md +++ b/.cursor/skills/l-01-agent-lifecycles/roles/reviewer.md @@ -13,7 +13,7 @@ reviewer seat (below)** (seams: developer decision 2026-07-03; loop reuse: rulin 1. **Master-exit** — before a **manager** hands its completed master integration branch to the **orchestrator**. 2. **Super-exit** — before the **orchestrator** hands the accumulated super integration branch to the - **developer**. + **architect** for the developer review. Leaf-level review is the manager's own duty — **not** an adversarial seam. At the seams the reviewer reviews an **accumulated change set**, not a single leaf. @@ -30,11 +30,20 @@ loop's 3-round cap** — your delta-verify closes a round, it does not open one. > **Verdicts are evidence, not decisions.** The reviewer never decides a gate. Its verdict attaches to > the handover gate as **judge evidence**; the gate's decider decides — the **orchestrator** at -> master-exit (delegated `master-handover-approval`), the **developer** at super-exit — per the -> gate delegation policy (settings `orchestration.gateDelegation`, `controlplane/gate_policy.py`). +> master-exit (delegated `master-handover-approval`), the **architect carrying the developer +> ruling** at super-exit — per the gate delegation policy (settings `orchestration.gateDelegation`, +> `controlplane/gate_policy.py`). > The policy binds delegated seam decisions to verdict evidence when > `requireReviewerVerdictAtSeams` is set. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays reviewer for its lifetime. A pasted brief for another +role is refused and reported to the seam's decider via inbox instead of rerouting this chat. Roles +expand horizontally into new chats; sub-agents drill vertically inside this reviewer seat for the +three review lenses. A reviewer never absorbs architect, orchestrator, strategist, manager, or +worker work. + ## Lens - **Opening move:** scope the review — the integration branch diff, the relevant task docs @@ -101,10 +110,11 @@ orchestrator. Review the **accumulated master change set**, not a final leaf in master. Each fix leaf names scope, target files/docs, evidence, and done-when. A master-exit block without fix leaves is invalid. -### SUPER-EXIT — Orchestrator Before Developer Handover +### SUPER-EXIT — Orchestrator Before Architect/Developer Handover The orchestrator spawns this reviewer before handing the accumulated super integration branch to the -developer. Review **wholesale branch behavior**: the whole portfolio as integrated on super. +architect for the developer review. Review **wholesale branch behavior**: the whole portfolio as +integrated on super. - **Scope packet:** super integration branch diff against its base (main), portfolio task docs, master task docs, master-handover packets, prior master-exit verdicts, orchestrator decision logs, resolved diff --git a/.cursor/skills/l-01-agent-lifecycles/roles/strategist.md b/.cursor/skills/l-01-agent-lifecycles/roles/strategist.md index 377e4b70..e4ae81d6 100644 --- a/.cursor/skills/l-01-agent-lifecycles/roles/strategist.md +++ b/.cursor/skills/l-01-agent-lifecycles/roles/strategist.md @@ -3,8 +3,8 @@ > The sprint planner, **spawn-first** and self-contained: read the whole in-flight portfolio, prove > it coherent, resolve the dependency chains, establish blast radius, shuffle leaves for the best > implementation order, and deliver the **orchestration task** — the sprint plan and the sprint -> scope. A strategist run is a **mandatory precondition for any orchestrated run**. Your **brief is -> your session start**. +> scope. This seat runs only when the developer approves the architect's propose-first strategist +> question; when dispatched, your **brief is your session start**. > > Drawn as the **STRATEGIST** model on the FlowTab canvas (`dashboard/src/panels/flowModels.ts`). @@ -13,8 +13,8 @@ **Spawn-first by design** (developer decision 2026-07-05). Strategist work is token-heavy — it reasons over every master's state, task docs, notes, friction ledger, and gate history — so it runs as its own process with its own harness/model/effort knobs, protecting the orchestrator's context. -The designer precedent explicitly does NOT apply: the designer stays an inline hat because design -is drawing-board-interactive with the developer; the strategist's essence is solitary heavy +The designer precedent explicitly does NOT apply: the designer stays an inline architect hat +because design is drawing-board-interactive; the strategist's essence is solitary heavy analysis. Spawned by the orchestrator via `spawn_agent_session` with `env={"AR_SPAWN_ROLE": "strategist"}`. @@ -36,6 +36,14 @@ it into durable task form. The strategist never edits task docs, never raises ga git. A seat that never touches mutating AR tools never instantiates a lifecycle — that is the designed shape. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays strategist for its lifetime. A pasted brief for +another role is refused and escalated to the orchestrator via inbox instead of rerouting this chat. +Roles expand horizontally into new chats; sub-agents drill vertically inside this strategist seat +for portfolio analysis. A strategist never absorbs architect, orchestrator, manager, reviewer, or +worker work. + ## Lens - **Opening move:** read the brief fully — it carries **refs to durable portfolio state, never @@ -90,7 +98,7 @@ everything else. `roles/manager.md`): the strategist's analysis directly parameterizes the loops. 6. **Coherence & contradiction check** — cross-master sweep: two masters moving one surface in opposite directions, a leaf assuming state another leaf removes, duplicate work, vocabulary - drift. **Directional contradictions are quo-vadis → developer** (via the drawing board; see + drift. **Directional contradictions are quo-vadis → architect** (via the drawing board; see Duties §5). 7. **Ordering** — topological sort over ORDER edges; CONFLICT edges resolved by serialization or **leaf moves (recorded from→to with rationale)**; independent sets become **parallel waves** @@ -134,17 +142,17 @@ mutate nothing yourself. ### 5 — Drawing-board rounds The reviewer (plan-review catalog) passes judgment on the plan; the orchestrator relays the -verdict and the developer's drawing-board feedback back into this session. **Convergence over +verdict and the architect's drawing-board feedback back into this session. **Convergence over rounds is expected and normal** — large, messy portfolios are explicitly NOT expected to be fixed in one shot; the iteration is the feature. Each round must shrink the finding set (the convergence -rule); the loop's hard cap is 3 full rounds, and **the drawing board with the developer IS this +rule); the loop's hard cap is 3 full rounds, and **the drawing board through the architect IS this loop's escalation**. Quo-vadis items — high-blast-radius truths such as two masters heavily -disagreeing on direction — go **straight to the developer** at the drawing board (the orchestrator -carries them; you flag them, unmistakably, at the top of the coherence findings). +disagreeing on direction — go **straight to the architect relay** at the drawing board (the +orchestrator carries them; you flag them, unmistakably, at the top of the coherence findings). ### 6 — Adopted-plan handover -When the developer accepts the plan, the orchestrator adopts it; your seat's work is done. **The +When the architect returns the accepted plan ruling, the orchestrator adopts it; your seat's work is done. **The artifact write is unconditional; the inbox is the delivery channel when the brief wires it** — otherwise your final playback message to the orchestrator carries the artifact ref. Then end. The orchestration task remains the sprint's standing scope: a new master added **in-sprint before implementation starts** re-opens re-evaluation (you @@ -166,8 +174,8 @@ and enters the next sprint's evaluation. dashboard-visible. - **Stdin push** — the orchestrator delivers round feedback into this hosted session; your replies are inbox rows or artifact revisions — never an untracked side channel. -- **Escalation** — to the **orchestrator**, which relays; quo-vadis truths are flagged for the - developer's drawing board. You never edit task docs to reflect a ruling — the orchestrator does. +- **Escalation** — to the **orchestrator**, which relays to the architect; quo-vadis truths are + flagged for the drawing board. You never edit task docs to reflect a ruling — the orchestrator does. ## Tool Surface (positive statement — this is all of it) diff --git a/.cursor/skills/l-01-agent-lifecycles/roles/system-specialist.md b/.cursor/skills/l-01-agent-lifecycles/roles/system-specialist.md new file mode 100644 index 00000000..54422560 --- /dev/null +++ b/.cursor/skills/l-01-agent-lifecycles/roles/system-specialist.md @@ -0,0 +1,102 @@ +# Lifecycle — System Specialist + +> One provider-degradation investigation, one report before any fix. The system specialist is a +> backend operations seat spawned by the orchestrator after a `degradation-alert`; it does not +> replace the orchestrator's portfolio attention. + +## What This Seat Is + +The system specialist investigates provider-only degradation events: provider metrics, provider +current-state files, provider logs, Docker/container state through the existing provider tools, and +the durable degradation event that caused the alert. This iteration is provider-only. Sentry or a +future system monitor may replace or feed the detector later, but the response protocol remains: +detect -> report -> explicit orchestrator order -> fix or stop providers. + +This seat is **investigate-first**. It writes a durable report under the active master's +`notes/reports/` folder (or the orchestrator-designated reports folder when there is no active +master) before attempting any fix. It fixes only after the orchestrator explicitly orders a +specific remediation based on that report. + +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays system-specialist for its lifetime. A pasted brief for +another role is refused and escalated to the orchestrator via inbox. This seat never absorbs +orchestrator, manager, worker, curator, reviewer, strategist, designer, or architect work. + +## Intake + +Read the orchestrator brief and the degradation event first. Required inputs: + +- Degradation event id and event payload or event-log path. +- Current provider metrics/state paths. +- Provider logs or diagnostics paths. +- Report path. +- Whether this is investigation-only or an explicit fix order. + +If the brief lacks the event or report path, ask the orchestrator for one clarification via inbox +and stop. + +## Investigation Report + +Write the report before any fix order is executed. Use this shape: + +```md +# System-Specialist Report — + +## Event +- State transition: +- Affected stacks: +- Critical failsafe already ran: yes | no | n/a + +## Findings +- + +## Root Cause Hypothesis +- + +## Fixable In Session +- Verdict: yes | no | uncertain +- Reason: + +## Recommended Action +- + +## Boundaries +- Provider-only scope honored: yes +- No AR task/memory state mutated beyond this report: yes +``` + +## Fix Mode + +Only after an explicit orchestrator order: + +- Apply the ordered provider remediation with existing provider/runtime tools. +- Do not edit AR task docs, lifecycle state, memory onboarding, ledgers, or code. +- Do not start providers if the order is only to investigate or if managers are paused by a + degradation-alert. +- If the issue is not fixable in-session, report that and recommend `provider_watchers stop`. + +The orchestrator owns the final decision: fixable-in-session -> order a targeted fix; not fixable +-> stop providers before they can take the system down. + +## Comms + +- **Inbox** — receive the orchestrator order, return report/fix completion or escalation. +- **Escalation** — system-specialist -> orchestrator. Never go straight to the architect or + developer. + +## Knobs + +| Knob | Default | Notes | +| ------- | ------- | ----- | +| harness | claude | operational investigation benefits from strong tool/session ergonomics | +| model | fable | system diagnosis and report synthesis | +| effort | high | degradation triage is high-impact | +| launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | +| sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | +| promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste | +| tools | provider diagnostics + native reads + inbox | provider_status · provider_diagnostics · provider_watchers when explicitly ordered · logs/metrics reads · inbox | + +Settings.json `orchestration.roles.system-specialist` overrides these, and +`orchestration.rolesPerLevel..system-specialist` overrides per dispatch level (role-file +defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.cursor/skills/l-01-agent-lifecycles/roles/worker.md b/.cursor/skills/l-01-agent-lifecycles/roles/worker.md index f5c369b4..aa29e836 100644 --- a/.cursor/skills/l-01-agent-lifecycles/roles/worker.md +++ b/.cursor/skills/l-01-agent-lifecycles/roles/worker.md @@ -7,7 +7,7 @@ ## What This Seat Is **One per task leaf, short-lived, fresh session.** Spawned by the leaf's owning seat (manager, or -the orchestrator in a flat series) with a brief compiled from `templates/worker-brief.md`. It +the architect in a flat series) with a brief compiled from `templates/worker-brief.md`. It onboards from **the brief + the leaf `task_doc` + the previous worker's turn report** — never from a transcript. Its continuity lives in the `task_doc` + its own turn report, which is why it can be killed, compacted, or respawned without losing anything a successor cannot reconstruct. @@ -16,20 +16,30 @@ The worker builds; it does not manage lifecycle machinery. **Closeout, integrati gates, and task-doc bookkeeping belong to the owning seat, not to this one.** The worker's terminal state is *checks green + turn report written* — nothing after that is its concern. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays worker for its lifetime. A pasted brief for another +role is refused and escalated to the owning seat via inbox instead of rerouting this chat. Roles +expand horizontally into new chats; sub-agents drill vertically inside this worker seat for +read/search only. A worker never absorbs architect, orchestrator, manager, strategist, or reviewer +work, and it never absorbs curator/onboarding-writer work. + ## The Worker Loop ``` -brief -> orient -> build (edit + onboarding same-pass) -> checks green -> turn report -> end +brief -> orient -> build code -> checks green -> turn report -> curator memory pass by separate seat | +-- blocked or plan delta beyond blank-filling -> escalate to the owning seat ``` ### 1 — Intake -Read the brief fully, then the leaf spec / `task_doc` it names. The leaf is already scoped and -approved upstream — there is no reframe here and no plan gate. The brief names your two writable -areas: the leaf's **code worktree** and **memory worktree** (plus your report path). You edit -nothing outside them. +On a developer-declared takeover, first run `../SKILL.md`'s Developer-Declared Task-Seat Takeover +checklist so the dashboard chat is attached to this leaf. Then read the brief fully, then the leaf +spec / `task_doc` it names. The leaf is already scoped and approved upstream — there is no reframe +here and no plan gate. The brief names your two writable areas: the leaf's **code worktree** and +your report path. The memory worktree is context for the curator pass unless the brief explicitly +says otherwise. You edit nothing outside your named surfaces. ### 2 — Orient (paired reads before edits) @@ -44,12 +54,10 @@ nothing outside them. - Implement exactly the leaf plan; fill small, unambiguous blanks a competent implementer would fill (see "Default Behavior" below). -- **Refresh the matching onboarding in the same editing pass** per - `c-05-create-or-update-onboarding-files`: a changed source file's sidecar **body** is updated now; - a new file's sidecar is created; route overviews that need a genuine body update get one, and a - no-impact route gets the literal history form `- — No route impact: `. - Regenerate generated route indexes with a **local `build_route_indexes(...)`** invocation from the - memory worktree. +- Produce the builder input the downstream curator needs: changed paths, code-diff summary, tests, + and any route/onboarding observations that would help the memory pass. The curator, not the + worker, writes onboarding in the official manager -> builder -> reviewer -> curator closeout + chain. - **Never `git commit`.** Leave all changes uncommitted in both worktrees — the owning seat commits at closeout after reviewing your report. @@ -63,14 +71,17 @@ the report. A red check you cannot fix inside the leaf's scope is an escalation, Write `templates/turn-report.md` to the path the brief names (convention: `notes/reports/-worker-report.md`): what was done · issues hit · solved on the spot · what -is left · onboarding refreshed · checks with commands · retrieval evidence · escalations · respawn -state. **A missing report gets nudged.** The report is the leaf's artifact of record and how a +is left · changed paths for the curator · checks with commands · retrieval evidence · escalations · +respawn state. The report is the leaf's builder artifact of record and how a respawned successor onboards — write it even when blocked (with the Escalations section filled), -then end your turn. +then end your turn. **A missing report gets nudged by the supervisor sweep (HFX2-L2), never by a +seat-local watcher** — no owning seat, and no worker, hand-rolls its own polling loop over this +artifact; ending your turn once the report is written is safe, not a risk you have to cover for. ## Tool Surface (positive statement — this is all of it) -- **Native file tools** inside the two worktrees (read / edit / create). +- **Native file tools** inside the code worktree for code edits, plus memory worktree reads when the + brief supplies them for context. - **Read-only AR retrieval:** `read_ar_files`, `grepai_search`, `cgc_*`, `context_packet`. - **Shell** for the prescribed checks (use the interpreter paths the brief names — do not assume a `python` shim exists). @@ -85,17 +96,17 @@ lifecycle machinery never instantiates a lifecycle; that is the designed shape, When the harness offers sub-agents, use them for **read/search only**, scoped to the leaf (locate call sites, sweep onboarding): each writes durable notes and returns a compact summary. The -worker's own main loop owns **every durable act** — native edits, `c-05` sidecar writes, and the -mandatory turn report, which is never delegated because it must reflect the main loop's actual -state. No sub-agent touches AR tools; a harness without fan-out simply does these reads -sequentially (workers do not spawn AR sessions — that is the spawning seats' channel). +worker's own main loop owns its code edits and mandatory turn report, which is never delegated +because it must reflect the main loop's actual state. The curator owns onboarding writes. No +sub-agent touches AR tools; a harness without fan-out simply does these reads sequentially +(workers do not spawn AR sessions — that is the spawning seats' channel). ## Loop Position (when the leaf runs as a three-party loop) The owning seat scores each leaf into a tier at dispatch (loop doctrine: `../SKILL.md`, The Three-Party Loop). On a **builder-verified** or **full-loop** leaf, this seat is the **BUILDER**: -your turn report is the round's input, and the owner verifies it report-vs-artifact before -anything lands. Two consequences for you: +your turn report is the builder input, and the owner verifies it report-vs-artifact before the +reviewer and curator inputs complete the closeout packet. Two consequences for you: - **Fix rounds resume THIS session** — the same builder, with its context intact. Your round-2+ report **appends** to your report file rather than rewriting it, so the loop history stays @@ -108,18 +119,25 @@ anything lands. Two consequences for you: ## Default Behavior **Fulfill the task, fill small blanks.** No creative-liberty prompting in either direction. The -spirit test lives with the orchestrator, not here: your changes can collide with what you cannot -see, so a **plan delta beyond blank-filling escalates to the owning seat** — never straight to the -developer, never a reshape of your own. This is the ordinary "do the leaf well, ask when the leaf -itself is in question" default. +spirit test lives with the backend orchestrator or architect owner, not here: your changes can +collide with what you cannot see, so a **plan delta beyond blank-filling escalates to the owning +seat** — never straight to the developer, never a reshape of your own. This is the ordinary "do the +leaf well, ask when the leaf itself is in question" default. ## Comms - **Inbox** — receive dispatch/context; post escalations; agent-to-agent rows carry role metadata and a `messageKind` (`turn-report`, `nudge`, `escalation`, …), durable + dashboard-visible. -- **Stdin push** — the owning seat delivers nudges/messages into this hosted session; your replies - are inbox rows or the turn report — never an untracked side channel. -- **Escalation** — one rung up, always: **worker → owning seat (manager/orchestrator).** +- **Stdin push** — the L2 supervisor sweep's injector (HFX2-L3) delivers nudges/messages into this + hosted session on its own mechanical tick, in the owning seat's name — never the owning seat (or + you) watching/polling by hand. Your replies are inbox rows or the turn report — never an untracked + side channel. +- **Idle is safe** — once your turn report is written, ending your turn is correct; silence is + supervised (HFX2-L2 sweep + HFX2-L4 escalation ladder), not a gap you must cover by lingering or + self-nudging. **Watcher ban (uniform-mechanism ruling 2026-07-07):** never hand-roll your own + watcher — one mechanism, no per-seat variance. +- **Escalation** — one rung up, always: **worker → owning seat (manager/orchestrator/architect in + solo flat mode).** ## Knobs diff --git a/.cursor/skills/l-01-agent-lifecycles/templates/curator-brief.md b/.cursor/skills/l-01-agent-lifecycles/templates/curator-brief.md new file mode 100644 index 00000000..8217ce34 --- /dev/null +++ b/.cursor/skills/l-01-agent-lifecycles/templates/curator-brief.md @@ -0,0 +1,84 @@ +# Template — Curator Brief + +The dispatch packet the **manager** (or the architect in a flat series) compiles for a **curator**, +spawned fresh per leaf after builder code exists and the reviewer verdict is available. **The brief +is the curator's entire session start** — it replaces the front half the spawner already ran. This +is the change-set feeding contract: the curator never infers a change set from transcript memory, +it is FED the landed change set, the leaf task doc, and notes/ as inputs. + +Spawn with `env={"AR_SPAWN_ROLE": "curator"}` and the **qualified** leaf key +`//`; together they claim the curator's `(leaf, role)` seat while the +session-start router and dashboard leaf rail engage. + +--- + +```md +ROLE BRIEF — curator + +# CURATOR BRIEF — · + +You are the CURATOR for leaf `` of master `` (repo: ). Your lifecycle is +`skills/l-01-agent-lifecycles/roles/curator.md`; this brief is your session start. Write the leaf's +memory pass from the inputs below, then stop. + +## Worktrees +- Code: `` (branch ``, base ``) — read-only for you. +- Memory: `` (branch ``) — your only write surface. + +## The landed change set (fed, not inferred) +- Code diff: `..` in the code worktree — . +- Memory diff (if any pre-existing memory-worktree changes carry forward): `..`. +- Counters: `` from the change-set the manager attached — + do not re-derive this from your own guess at "what probably changed." + +## Task inputs +- Leaf task doc: `` (read it first — objective, requirements, decision log). +- notes/: `` — the builder turn report + (`notes/reports/-worker-report.md`), the reviewer verdict when this leaf ran a loop, and + any other task-local notes naming a factual current-state clarification. + +## Routing rule (mgmt-L4 design — apply this before writing anything) +Route each piece of the change set and each notes/ item to the RIGHT onboarding home: +1. A concrete source file's own sidecar, when the change is about that file's behavior. +2. The nearest governing route-local overview, when the change is about route/package shape or + crosses several files in one route. +3. The repo entity catalog, only for a real load-bearing cross-layer entity change. +4. The L3 Operational-Notes target is LAST RESORT ONLY — use it when a finding is real but has no + file, route, or entity home; never as the default drop point for convenience. +Overview-dumping (writing everything into the nearest overview because it is easiest) is rejected. + +## Tool surface +- Native reads in the code worktree; native reads/edits in the memory worktree. +- `c-05-create-or-update-onboarding-files` skill workflows for sidecars and entity catalogs. +- Local `route_index_refresh`-equivalent (`build_route_indexes(...)`) from the memory worktree. +- Inbox for one clarification row back to if the fed change set is missing or + ambiguous — never invent a change set from memory. +- No `worktree_*`, `lifecycle_*`, `task_doc`, `gate_*`, `memory_quality_check`-mutating tools beyond + what your role file names, no code edits. + +## Checks (before you report) +- `git diff --check` in the memory worktree. +- Any onboarding/reference checks the brief or your role file names. + +## Memory-pass report (mandatory, last act) +Write `/-curator-report.md`: changed onboarding files (with which +change-set item or notes/ item each one routes to and why), route index results, reference checks, +blockers, and the exact commands run. This report — together with the builder's code and the +reviewer's verdict — is exactly the manager's three closeout inputs. +``` + +--- + +**Compiler notes for the manager.** + +- Fill every ``; a brief with an unresolved placeholder is not dispatchable. +- Pull the change-set counters/paths from the leaf's actual landed range (the leaf contract's + recorded base commit through the builder's current HEAD/worktree state) — do not hand the curator + a stale or guessed diff. +- Attach the builder turn report and (when the leaf ran a loop) the reviewer verdict as the notes/ + inputs; the curator does not re-request evidence that already exists in `notes/reports/`. +- Deliver as an echo-confirmed paste; only count delivery on a post-boot echo. +- This brief runs strictly AFTER builder code exists and the reviewer verdict (when the leaf tier + requires one) is available — never before, and never in place of either. diff --git a/.cursor/skills/l-01-agent-lifecycles/templates/manager-brief.md b/.cursor/skills/l-01-agent-lifecycles/templates/manager-brief.md index 10c37922..0d611d1a 100644 --- a/.cursor/skills/l-01-agent-lifecycles/templates/manager-brief.md +++ b/.cursor/skills/l-01-agent-lifecycles/templates/manager-brief.md @@ -3,7 +3,8 @@ The dispatch packet the orchestrator compiles for a manager taking one master. Like the worker brief, **this brief is the manager's entire session start** — it replaces the front half the orchestrator already ran. Spawn with `env={"AR_SPAWN_ROLE": "manager"}` and the **qualified** leaf -key of the master's coordination leaf (`//`). +key of the master's coordination leaf (`//`); together they claim the +manager's `(leaf, role)` seat. --- @@ -30,8 +31,25 @@ master's leaf loop to the master-exit seam, then hand over. ## Dispatch defaults - Worker spawns: `templates/worker-brief.md`, `env={"AR_SPAWN_ROLE": "worker"}`, qualified leaf - keys; knob overrides: . + keys; the environment role and qualified leaf together claim each worker's `(leaf, role)` seat; + knob overrides: . +- Leaf closeout chain: manager -> builder -> reviewer -> curator. The manager closes a leaf from + builder code + reviewer verdict + curator memory pass — never before the curator pass exists. +- Curator spawns: `../templates/curator-brief.md`, `env={"AR_SPAWN_ROLE": "curator"}`, fresh per + leaf with the qualified leaf key, so the environment role and qualified leaf claim the + curator's `(leaf, role)` seat; dispatch only after builder code and the reviewer verdict are + available. The brief FEEDS the landed change set (leaf contract's base-to-head range) + the leaf + task doc + notes/ — the curator routes each to the right onboarding home (specific sidecar or + governing overview; L3 Operational-Notes last-resort only) and writes onboarding only. - Concurrency: . +- Provider degradation: on `messageKind="degradation-alert"`, do not start provider setup, + provider watchers, watcher restarts, or `retry_provider_setup` until an all-clear. Managers have + no provider kill authority; provider stops and fixes route through the orchestrator and + system-specialist. +- Cleanup: `worktree_integrate` auto-lands a completed leaf's worker/reviewer seats into the + dashboard landed/archive group (config-gated, default ON). Use the landed archive cleanup button + for archived rows; use `session_retire` only for a stuck/abandoned worker/reviewer/curator seat of + YOUR OWN master — server policy refuses any other target. ## The exit - When all leaves have landed on your branch: spawn the master-exit reviewer diff --git a/.cursor/skills/l-01-agent-lifecycles/templates/orchestration-task.md b/.cursor/skills/l-01-agent-lifecycles/templates/orchestration-task.md index c3d4bb08..c1559030 100644 --- a/.cursor/skills/l-01-agent-lifecycles/templates/orchestration-task.md +++ b/.cursor/skills/l-01-agent-lifecycles/templates/orchestration-task.md @@ -1,10 +1,12 @@ # Orchestration-Task Template The artifact a **strategist** drafts for the **orchestrator** (`roles/strategist.md`) — the sprint -plan and the sprint scope. A strategist run producing this artifact is a **mandatory precondition -for any orchestrated run**; the orchestrator adopts the accepted draft into durable task form (the -strategist is a reader, not a mutator). It is written under the series/coordination `notes/` path -the brief names and revised across drawing-board rounds. +plan and the sprint scope. The architect proposes the strategist pass, and the strategist drafts +this artifact only after developer approval; the orchestrator adopts the accepted draft into +durable task form (the strategist is a reader, not a mutator). When the developer sanctions a +strategist skip, `roles/orchestrator.md` owns the alternate author-and-adopt path. It is written +under the series/coordination `notes/` path the brief names and revised across drawing-board +rounds. ## Rules diff --git a/.cursor/skills/l-01-agent-lifecycles/templates/turn-report.md b/.cursor/skills/l-01-agent-lifecycles/templates/turn-report.md index b771ada4..597f40fd 100644 --- a/.cursor/skills/l-01-agent-lifecycles/templates/turn-report.md +++ b/.cursor/skills/l-01-agent-lifecycles/templates/turn-report.md @@ -2,7 +2,8 @@ The **mandatory** artifact a worker writes at **every** hand-off (`roles/worker.md`). It is how the leaf's work survives the session's death and how a respawned successor onboards from **state, not the -transcript**. A missing turn report is nudged by the manager. +transcript**. A missing turn report is nudged by the HFX2-L2 supervisor sweep, never by a +manager watching for it (uniform-mechanism ruling 2026-07-07). ## Rules diff --git a/.cursor/skills/l-01-agent-lifecycles/templates/worker-brief.md b/.cursor/skills/l-01-agent-lifecycles/templates/worker-brief.md index db8f44d0..c91e9779 100644 --- a/.cursor/skills/l-01-agent-lifecycles/templates/worker-brief.md +++ b/.cursor/skills/l-01-agent-lifecycles/templates/worker-brief.md @@ -7,7 +7,8 @@ absorbed a series of real dispatch frictions (route-index leaks, attestation for keying, missing `python` shim), so deviate knowingly or not at all. Spawn with `env={"AR_SPAWN_ROLE": "worker"}` and the **qualified** leaf key -`//` so the session-start router and the dashboard leaf rail both engage. +`//`; together they claim the worker's `(leaf, role)` seat while the +session-start router and dashboard leaf rail engage. --- @@ -18,13 +19,14 @@ ROLE BRIEF — worker You are a WORKER for leaf `` of master `` (repo: ). Your lifecycle is `skills/l-01-agent-lifecycles/roles/worker.md`; this brief is your session start. Execute the leaf -completely, write your turn report, then stop. +code completely, write your builder turn report, then stop. Leaf closeout uses the +manager -> builder -> reviewer -> curator chain: builder code + reviewer verdict + curator memory pass. -## Worktrees (your ONLY writable areas) +## Worktrees (your code write area + memory context) - Code: `` (branch ``, base ``) -- Memory: `` +- Memory: `` (read/context for changed-path notes; the curator writes onboarding) - Plus your turn report at the path below. Nothing else. NEVER `git commit` — the owning seat - closes out after reviewing your report. + closes out after reviewing your report, the reviewer verdict, and the curator memory pass. ## Tool surface - Native file tools inside the two worktrees; shell for the checks below. @@ -46,18 +48,18 @@ files involved, the invariants that must hold, what NOT to touch.> - Full: — must exit 0. - `git diff --check` in both worktrees. -## Onboarding (same editing pass, per c-05) -- Changed source files: update the sidecar BODY now; new files: create the sidecar. -- Route overviews: genuine body update where routes changed; otherwise the newest history entry - uses the LITERAL form `- — No route impact: ` (timestamp first). -- Pin idiom for verification metadata: "Verification metadata pinned until closeout stamps the - commit." +## Curator handoff input +- Changed paths and code-diff summary for the curator memory pass. +- Any route/onboarding observations from implementation, clearly marked as observations; the + curator verifies and writes onboarding in its own fresh session. +- Pin idiom for any metadata note the curator needs: "Verification metadata pinned until closeout + stamps the commit." ## Turn report (mandatory, last act) Write `/-worker-report.md` following `skills/l-01-agent-lifecycles/templates/turn-report.md` — including exact check commands + -outcomes, the retrieval-evidence tally, and the respawn state. If blocked: fill Escalations and -stop — escalate to , never to the developer. +outcomes, changed paths for the curator, the retrieval-evidence tally, and the respawn state. If +blocked: fill Escalations and stop — escalate to , never to the developer. ``` --- diff --git a/.cursor/skills/w-02-light-task-workflow/master-template.md b/.cursor/skills/w-02-light-task-workflow/master-template.md index 42738c16..a807afd1 100644 --- a/.cursor/skills/w-02-light-task-workflow/master-template.md +++ b/.cursor/skills/w-02-light-task-workflow/master-template.md @@ -7,7 +7,7 @@ built to grow as the work unfolds. ## When to escalate to a series -The `l-01-agent-lifecycles` orchestrator lifecycle's `decide` step escalates a single task to a series once its size is apparent — the +The `l-01-agent-lifecycles` architect lifecycle's `decide` step escalates a single task to a series once its size is apparent — the implementation plan no longer fits on a single page, or the work splits into distinct slices that each deserve their own checklist and commit. You can also start single and escalate later: drop in the master `task.md` and move the existing plan into the first `NN_.md`. diff --git a/.github-vscode/copilot-instructions.md b/.github-vscode/copilot-instructions.md index 0d2055f4..cb4bb867 100644 --- a/.github-vscode/copilot-instructions.md +++ b/.github-vscode/copilot-instructions.md @@ -4,9 +4,9 @@ If `AR_SPAWN_ROLE` is set, or your first user message is a role brief from an orchestrating agent: **ignore this notice entirely — your brief is your session start.** -Otherwise you are the developer-facing session, i.e. the **orchestrator**: read +Otherwise you are the developer-facing session, i.e. the **architect**: read `ar-coordination/AGENTS.md`, then run your lifecycle at -`skills/l-01-agent-lifecycles/roles/orchestrator.md` — trust checkpoint before +`skills/l-01-agent-lifecycles/roles/architect.md` — trust checkpoint before relying on memory, `read_ar_files` (paired source+onboarding) until the build decision, retrieval-strategy tally as evidence, notify-and-stop at every developer hand-off. diff --git a/.github-vscode/hooks/agents-remember-session-start.md b/.github-vscode/hooks/agents-remember-session-start.md index cd999a73..a6613b7a 100644 --- a/.github-vscode/hooks/agents-remember-session-start.md +++ b/.github-vscode/hooks/agents-remember-session-start.md @@ -4,9 +4,9 @@ If `AR_SPAWN_ROLE` is set, or your first user message is a role brief from an orchestrating agent: **ignore this notice entirely — your brief is your session start.** -Otherwise you are the developer-facing session, i.e. the **orchestrator**: read +Otherwise you are the developer-facing session, i.e. the **architect**: read `ar-coordination/AGENTS.md`, then run your lifecycle at -`skills/l-01-agent-lifecycles/roles/orchestrator.md` — trust checkpoint before +`skills/l-01-agent-lifecycles/roles/architect.md` — trust checkpoint before relying on memory, `read_ar_files` (paired source+onboarding) until the build decision, retrieval-strategy tally as evidence, notify-and-stop at every developer hand-off. diff --git a/.github-vscode/skills/c-05-create-or-update-onboarding-files/SKILL.md b/.github-vscode/skills/c-05-create-or-update-onboarding-files/SKILL.md index 31ea0090..d8e39a82 100644 --- a/.github-vscode/skills/c-05-create-or-update-onboarding-files/SKILL.md +++ b/.github-vscode/skills/c-05-create-or-update-onboarding-files/SKILL.md @@ -16,6 +16,16 @@ Planning stays in task artifacts. This package defines how onboarding itself is Before maintaining onboarding, use `c-08-ar-coordination-context-resolver` to resolve the target repository's active coordination context. It must use the `Domain Documentation` category declared in the resolved `system/sources.md` for the onboarding slice being maintained, rather than assuming that adjacent onboarding alone is sufficient or hard-coding one particular documentation system into the skill. +**Seat routing:** in the manager -> builder -> reviewer -> curator chain (`l-01-agent-lifecycles` +`roles/curator.md`), onboarding create/update duty during leaf work belongs to the curator seat, not +the builder — the builder produces code and a turn report only. The curator runs this skill's +workflows from a change set (landed diff), the leaf task doc, and notes/ fed to it by the manager, +and routes each item to the right onboarding home (a concrete sidecar or the governing overview +whose subject it is; the L3 Operational-Notes target is last-resort only, never a default). The +strict 1-to-1 source mapping, governing-overview links, and metadata rules below are unchanged by +this routing — only the writing seat moved. A solo flat session with no separate curator seat runs +this skill itself, exactly as before. + When onboarding maintenance is happening inside a `c-09-git-worktree-manager` worktree, check `worktree_status` before starting memory entries. If its `freshness` block reports that the recorded code or memory source branch moved, diff --git a/.github-vscode/skills/c-09-git-worktree-manager/SKILL.md b/.github-vscode/skills/c-09-git-worktree-manager/SKILL.md index 692dec19..58a87184 100644 --- a/.github-vscode/skills/c-09-git-worktree-manager/SKILL.md +++ b/.github-vscode/skills/c-09-git-worktree-manager/SKILL.md @@ -1,6 +1,6 @@ --- name: c-09-git-worktree-manager -description: "Create, attach to, report on, integrate, finalize, and clean up Agents Remember worktree-backed tasks while preserving human approval gates and external-memory compatibility." +description: "Create, attach to, report on, integrate, finalize, and clean up Agents Remember worktree-backed tasks while preserving delegated/human approval gates and external-memory compatibility." --- # c-09-git-worktree-manager Git Worktree Manager @@ -75,11 +75,14 @@ The intended order is: integration branch and integrates back into it. For a nested master, create the child integration branch from the parent integration branch. 6. choose or review the task slug and workflow variables -7. **hand off** the **Worktree Intent Gate** (notify-and-continue) for explicit developer approval +7. establish the applicable worktree-start authority: for standalone/new work, hand off the + **Worktree Intent Gate** for explicit developer approval; for subordinate leaves/edges inside an + accepted orchestrated series, record the accepted-series authority and continue without a new + developer hand-off 8. create the durable task wrapper when one is needed 9. request the `worktree_start` MCP tool only after the task identity is stable, the correct landable `source_branch` is selected, external memory is clean, and - the developer approved the intent packet + the applicable authority has been recorded The Worktree Intent Gate must name: @@ -97,7 +100,7 @@ visible: leaf work branches integrate into the pushable integration/source branc recorded by their enclosure; protected targets are reached later through the repo's PR flow. -Run the applicable dry-run/preflight first, then **hand off**: call +For developer-gated starts, run the applicable dry-run/preflight first, then **hand off**: call `lifecycle_turn_end_notification(summary={…the intent packet + the approve/revise ask…})` as the **last tool call**, then deliver the intent packet as your final prose and **STOP / end your turn**. The notification sets the `awaiting-developer` lifecycle state, surfaces a @@ -106,6 +109,10 @@ dashboard or in the leaf's attached chat; the **first AR tool call of your next lifecycle (`running`), clears the attention item, and proceeds to `worktree_start` — you send no explicit `lifecycle_resume`. +For subordinate orchestrated-series starts, do the same dry-run/preflight, record the accepted +planner/series authority in the task decision log or worktree intent note, and continue. Do not add +a developer stop for every leaf worktree. + Parked fallback: the block-and-wait `lifecycle_gate` junction (plus the operator inbox and dashboard GateResponder) still works if you deliberately raise it for a durable, developer-attributed, mutation-blocking record: @@ -190,19 +197,27 @@ quality gate, memory content commit, ledger update, and ledger commit. For worktree-backed tasks, pass the leaf enclosure `series-contract.md` to `worktree_closeout_preview` / `worktree_closeout_apply`. The apply step records -the developer's explicit commit approval in the contract and updates the -contract closeout state after the code, memory, and ledger commits are created. +the applicable closeout authority in the contract and updates the contract closeout state after the +code, memory, and ledger commits are created. Worktree closeout stops if the recorded code or external-memory source branch moved since task start. ## Integration -Integration is explicitly human-gated and runs only after closeout completed. It lands the closed task branches back onto the recorded source branches and records the landed commits separately from the closeout commits. Orchestrated-run carve-out (ruled 2026-07-06): dependency-ordered leaf→master and master→super integrations ride the series' standing approval (the developer's portfolio-gate approval recorded in the planner master) — the developer hand-off concentrates at the super PR/carry-over gate per the `l-01-agent-lifecycles` loop/orchestrator doctrine; a raised durable `integration-approval` gate still awaits the developer. +Integration runs only after closeout completed and is authority-gated by context. It lands the +closed task branches back onto the recorded source branches and records the landed commits +separately from the closeout commits. In an accepted orchestrated run, dependency-ordered +leaf→master and master→super integrations ride the series' standing approval (the developer's +portfolio-gate approval recorded in the planner master) — the developer hand-off concentrates at +the super PR/carry-over gate per the `l-01-agent-lifecycles` loop/orchestrator doctrine. A raised +durable `integration-approval` gate still awaits the developer. On an orchestrated master's exit (master → super integration) the integrate step additionally enforces the delegated `master-handover-approval` seam: an undecided or policy-invalid handover gate addressed to the master (by `enclosure` = master task name) returns `handover-gate-blocked` instead of landing — decide the gate per the `l-01-agent-lifecycles` seam doctrine, then rerun. When no gate addresses the integrating master but open `master-handover-approval` gates exist elsewhere, integrate still proceeds and its result carries a `handover_gate_warning` naming them — treat it as a spelling check on the raised gate's `enclosure`. -Run `worktree_integrate(..., dry_run=true)` first, then **hand off**: call +Run `worktree_integrate(..., dry_run=true)` first. For subordinate accepted-series integrations, +record the standing series authority and then run the real integration without a developer stop. +For developer-gated integrations, **hand off**: call `lifecycle_turn_end_notification(summary={…the integration plan…})` as the **last tool call**, then deliver the integration preview as your final prose and **STOP**. The developer approves on the dashboard or in chat; the first AR tool call of your next turn auto-resumes @@ -228,9 +243,17 @@ After successful integration, complete any repo-specific landing tail first: pus ## Lifecycle Finalization And Cleanup -Lifecycle finalization is explicitly human-gated and runs only after closeout, integration, and any PR/carryover tail are complete. It proves the current parent-child branch edge, then removes the recorded code and memory worktrees, deletes local task branches only when Git can prove they are merged, removes empty worktree group folders when safe, records `cleanup: completed` in the contract, and updates task documents. - -Run `lifecycle_finalize_task(..., dry_run=true)` first, then **hand off**: call +Lifecycle finalization runs only after closeout, integration, and any PR/carryover tail are +complete, and its approval authority follows the same series boundary. For subordinate +accepted-series leaf/master edges, the owning manager/orchestrator may finalize and clean up after +the dry-run proves the landed edge. For final super→main cleanup, standalone work, or a deliberately +raised `cleanup-approval` gate, stop for developer approval. Finalization proves the current +parent-child branch edge, then removes the recorded code and memory worktrees, deletes local task +branches only when Git can prove they are merged, removes empty worktree group folders when safe, +records `cleanup: completed` in the contract, and updates task documents. + +Run `lifecycle_finalize_task(..., dry_run=true)` first. For subordinate accepted-series cleanup, +record the standing authority and run the real finalizer. For developer-gated cleanup, **hand off**: call `lifecycle_turn_end_notification(summary={…what cleanup removes…})` as the **last tool call**, then relay the landed-commit proof, cleanup plan, and task-document updates as your final prose and **STOP**. The developer approves on the dashboard or in chat; the first AR tool call of your next turn auto-resumes and runs @@ -277,11 +300,16 @@ then proceeds as usual, including closeout → integrate → finalize. 2. The `c-09-git-worktree-manager` skill does not initialize memory roots; use the `c-00-initialize-memory-repo` skill before starting external-memory worktrees. 3. Closeout belongs to the `c-12-closeout` skill; the `c-09-git-worktree-manager` skill only supplies worktree contract context. 4. The `c-09-git-worktree-manager` skill must not use divergent memory as semi-trusted reference context. -5. The `c-09-git-worktree-manager` skill must not bypass the `c-12-closeout` skill's explicit closeout approval gate. +5. The `c-09-git-worktree-manager` skill must not bypass the `c-12-closeout` skill's applicable + closeout authority gate. 6. The `c-09-git-worktree-manager` skill must not create closeout commits outside the `c-12-closeout` skill's code-memory-ledger sequence. 7. The `c-09-git-worktree-manager` skill must not call `worktree_start` until - the developer has approved the Worktree Intent Gate. -8. The `c-09-git-worktree-manager` skill must not move source branches during integration until replay/preflight has produced fast-forwardable code and memory commits and explicit integration approval exists. -9. The `c-09-git-worktree-manager` skill must not finalize or clean up without explicit cleanup/finalization approval. + the applicable authority has been recorded: developer-approved Worktree Intent Gate for + standalone/new work, or accepted-series authority for subordinate orchestrated work. +8. The `c-09-git-worktree-manager` skill must not move source branches during integration until + replay/preflight has produced fast-forwardable code and memory commits and applicable + integration authority exists. +9. The `c-09-git-worktree-manager` skill must not finalize or clean up without applicable + cleanup/finalization authority. 10. The `c-09-git-worktree-manager` skill must not treat squash-merged content as a normal landed edge. 11. The `c-08-ar-coordination-context-resolver` skill remains the facts-only resolver; the `c-09-git-worktree-manager` skill owns worktree and lifecycle mutation. diff --git a/.github-vscode/skills/c-12-closeout/SKILL.md b/.github-vscode/skills/c-12-closeout/SKILL.md index 2c7fe3ff..82c472a5 100644 --- a/.github-vscode/skills/c-12-closeout/SKILL.md +++ b/.github-vscode/skills/c-12-closeout/SKILL.md @@ -1,6 +1,6 @@ --- name: c-12-closeout -description: "Close out approved Agents Remember edits by preserving explicit commit approval, missing-onboarding checks, external-memory onboarding refresh, memory quality, ledger alignment, and no automatic push for worktree-backed tasks." +description: "Close out approved Agents Remember edits by preserving the applicable approval authority, missing-onboarding checks, external-memory onboarding refresh, memory quality, ledger alignment, and no automatic push for worktree-backed tasks." --- # c-12-closeout Closeout @@ -15,6 +15,18 @@ direct-checkout closeout path. Use the `c-09-git-worktree-manager` skill for worktree start, attach, status, integration, lifecycle finalization, and cleanup; use this skill for the closeout gate and code-memory-ledger commit order. +**Seat note (manager -> builder -> reviewer -> curator chain):** in that chain, the builder produces +code and a turn report only — it does not author onboarding. The dedicated curator seat +(`l-01-agent-lifecycles` `roles/curator.md`) runs the `c-05-create-or-update-onboarding-files` skill +as its own fresh pass, fed the leaf's landed change set, task doc, and notes/, BEFORE the owning +seat (the manager) runs this skill's closeout preview. Everywhere below that says "create" or +"refresh" onboarding, that authoring already happened in the curator's pass; the seat running +closeout **verifies** the curator's output against the checks in this skill, it does not author +onboarding inline to make a failing check pass. A check that still fails after the curator pass is a +closeout failure — respawn/rerun the curator, do not patch onboarding from the closeout seat. This +distinction does not apply outside that chain (e.g. a solo flat session with no separate curator +seat still runs `c-05-create-or-update-onboarding-files` itself before closing out). + ## MCP Tools Use the worktree closeout tools against the task contract: @@ -29,18 +41,31 @@ Worktree closeout records closeout state in the contract the `c-09-git-worktree-manager` skill owns later integration, lifecycle finalization, cleanup, and task-document completion. -## Approval Hand-off +## Approval Authority + +Closeout is always authority-gated, but the authority is contextual. + +For standalone work, final super-branch landing, or any closeout where the accepted task/series +authority is unclear, agents must request the matching preview tool first, relay the proposed code, +memory, and ledger commit messages to the developer, and ask for explicit commit approval. + +For subordinate work inside an accepted orchestrated series, the owning seat may apply closeout +under delegated series authority after the preview/checks are clean. Managers govern leaf commits; +the orchestrator governs manager/master edges and direct flat work when it is wearing the manager +or worker hat itself. Do not stop for the developer merely because closeout will create code, +memory, and ledger commits. The `intent_note` records the authority source, e.g. the accepted +planner/series task and the owning seat's review of the preview. -Closeout is explicitly human-gated. Agents must request the matching preview -tool first, relay the proposed code, memory, and ledger commit messages to the -developer, and ask for explicit commit approval. +Closeout still stops for the developer when the work reaches the final completed super branch / +PR-carryover gate, when a `closeout-approval` gate has been deliberately raised, when the change is +outside the accepted scope, when checks remain red outside the task, when onboarding/memory quality +cannot be repaired inside the leaf, or when a quo-vadis decision is required. -Real closeout uses the matching apply tool with an `intent_note`. The note -records the developer's explicit commit approval. Agents must not treat -implementation approval, a previous "looks good", or their own judgment as -commit approval. +Real closeout uses the matching apply tool with an `intent_note`. The note records the applicable +authority: either explicit developer commit approval or delegated accepted-series authority. Agents +must not treat a vague "looks good" or their own preference as authority. -The relay follows the `l-01-agent-lifecycles` orchestrator hand-off protocol: run the +For a developer-gated closeout, the relay follows the `l-01-agent-lifecycles` orchestrator hand-off protocol: run the preview/dry-run first, then call `lifecycle_turn_end_notification(summary={…the preview facts + the commit ask…})` as the **last tool call**, then deliver the preview facts and proposed messages as plain @@ -66,10 +91,11 @@ the dashboard and a `closeout-approval` gate is explicitly raised, closeout is * server-side through that durable gate, so a developer can approve from the cockpit and the mutating tool — not a UI button — is the security boundary. -`closeout-approval` **is** the commit gate — closeout is the single -commit-of-record for code, memory, and ledger, so there is no separate -`commit-approval` kind; every commit routes through this gate. The dashboard -junction uses the preview/dry-run -> chat report -> `lifecycle_gate` order above. +`closeout-approval` **is** the human commit gate when it is deliberately raised — closeout is the +single commit-of-record for code, memory, and ledger, so there is no separate `commit-approval` +kind. Subordinate orchestrated-series closeouts normally do not raise this gate; they use the +accepted-series authority recorded in the `intent_note`. The dashboard junction uses the +preview/dry-run -> chat report -> `lifecycle_gate` order above. How it binds: @@ -99,16 +125,19 @@ How it binds: Rules: -1. **Never self-approve.** A model-attributed approval is rejected by - enforcement. Wait for the developer's dashboard decision or Chat response, and - never pass your own judgment off as commit approval. +1. **Never self-approve a human-pinned gate.** A model-attributed approval is rejected by + enforcement. Wait for the developer's dashboard decision or chat response when a + `closeout-approval` gate exists, and never pass your own judgment off as developer approval. + Delegated-series closeout without a raised gate is different: it records the accepted series + authority and the owning seat's review in `intent_note`. 2. **Opening a gate is opt-in and deliberate.** Open a `closeout-approval` gate **only** when a developer is driving approval from the dashboard. Do **not** open one in a pure-chat session with no cockpit watching — an `open` gate blocks your own closeout until it is decided. -3. **Gateless lifecycles are unchanged.** With no `closeout-approval` gate the chat - commit gate (`intent_note` after an explicit "commit") governs exactly as before; - enforcement is additive, never a new requirement on every closeout. +3. **Gateless lifecycles use the applicable authority.** With no `closeout-approval` gate, + standalone/final work still needs explicit developer commit approval, while accepted + orchestrated-series subordinate work may proceed under delegated series authority. Enforcement is + additive, never a requirement to raise a gate on every closeout. 4. The closeout preview/apply payload carries a `closeout_gate` block (`enforced` / `permitted` / `gateId` / `reason`); relay it at the commit-approval gate so the developer sees whether a dashboard gate is open, approved, or absent. @@ -130,17 +159,22 @@ python -m agents_remember.memory_quality.integrity.check_missing_onboarding --co ``` The check only evaluates files that are new in the current checkout or -worktree, not the whole historical repository. If it reports missing -onboarding, create those sidecars through the `c-05-create-or-update-onboarding-files` skill before committing code. After -the code commit exists, refresh the new sidecars' verification metadata to that -commit during the normal post-code-commit memory refresh. +worktree, not the whole historical repository. In the manager -> builder -> +reviewer -> curator chain, this check is expected to already pass by the time the owning seat runs +it, because the curator's memory pass created those sidecars through the +`c-05-create-or-update-onboarding-files` skill before this precondition is checked; running the +check here confirms that pass, it is not the trigger to author onboarding from the closing seat. If +it still reports missing onboarding, do not create the sidecars inline — escalate to run (or rerun) +the curator's memory pass, then rerun this check. After the code commit exists, refresh the new +sidecars' verification metadata to that commit during the normal post-code-commit memory refresh. Changed (already-onboarded) source files have a parallel requirement: their sidecar content must be updated to approved current state before closeout. The closeout gate rejects any changed source file whose existing sidecar body was not modified in the current task, because advancing verification metadata over -stale content defeats the commit-hash-based drift check. Update changed sidecars -during implementation, not at the metadata-refresh step. +stale content defeats the commit-hash-based drift check. In the curator chain, changed sidecars are +updated during the curator's memory pass, not at the metadata-refresh step, and not by the builder +during implementation. The closeout worklist covers the working tree plus the leaf contract-recorded committed range: every path changed between the last verified commit (the @@ -159,11 +193,15 @@ deliberately through the `c-05-create-or-update-onboarding-files` skill. External-memory closeout order is: -1. run `check_missing_onboarding` against current additions -2. create missing onboarding for newly added eligible source files before committing code +1. run `check_missing_onboarding` against current additions (in the curator chain, this confirms the + curator's pass already covered them — it is not the cue to author onboarding here) +2. if onboarding is still missing, escalate to run/rerun the curator's memory pass through the + `c-05-create-or-update-onboarding-files` skill before committing code (solo flat sessions with no + separate curator seat create it directly) 3. commit code changes and capture `C2` plus its commit date 4. run the `c-02-memory-quality-control` skill's drift check against `C2` to produce the full memory update worklist -5. verify each changed source file's sidecar content was updated in this task, then refresh affected onboarding `lastVerifiedCommitHash` and `lastVerifiedCommitDate` to `C2`; a changed source file with an unmodified sidecar body fails the closeout instead of receiving a metadata-only refresh +5. verify each changed source file's sidecar content was updated in this task (by the curator's pass + in the chain above), then refresh affected onboarding `lastVerifiedCommitHash` and `lastVerifiedCommitDate` to `C2`; a changed source file with an unmodified sidecar body fails the closeout instead of receiving a metadata-only refresh 6. refresh affected repo entity catalog `git-blob-set-v1` fingerprints against `C2` when changed source paths are listed as entity evidence 7. refresh affected route overview `lastVerifiedCommitHash` / `lastVerifiedCommitDate` metadata to `C2` 8. refresh generated route indexes so `overview.index.json` matches the updated onboarding tree @@ -214,16 +252,20 @@ preview and apply payloads for the commit-approval relay. Worktree closeout also fails when the recorded code or external-memory source branch moved since task start. -Missing onboarding is the expected hard failure when the implementation/update -pass did not produce a required onboarding file. The next step is to run the `c-05-create-or-update-onboarding-files` skill -for that source file, then rerun the closeout preview. +Missing onboarding is the expected hard failure when the required onboarding file was not produced — +in the manager -> builder -> reviewer -> curator chain that means the curator's memory pass did not +cover it. The next step is to run (or rerun) the curator's `c-05-create-or-update-onboarding-files` +pass for that source file, then rerun the closeout preview; a solo flat session with no separate +curator seat runs that skill itself. ## Boundaries 1. The `c-12-closeout` skill owns closeout approval and code-memory-ledger commit sequencing. 2. The `c-12-closeout` skill does not create worktrees, integrate worktrees, finalize lifecycles, or clean up worktrees. 3. The `c-12-closeout` skill does not initialize memory roots; use the `c-00-initialize-memory-repo` skill. -4. The `c-12-closeout` skill must not commit without explicit commit approval after a closeout preview. +4. The `c-12-closeout` skill must not commit without the applicable authority after a closeout + preview: explicit developer commit approval for standalone/final work, or recorded delegated + series authority for subordinate accepted-series work. 5. The `c-12-closeout` skill must not create a memory content commit whose affected onboarding metadata still points at pre-closeout code. 6. The `c-12-closeout` skill must not create a memory content commit before route overview metadata, generated route indexes, and `memory_quality_check` are clean for the new code commit. 7. The `c-12-closeout` skill must not push automatically. diff --git a/.github-vscode/skills/c-13-install-and-onboard/SKILL.md b/.github-vscode/skills/c-13-install-and-onboard/SKILL.md index d583fd13..e52d9f57 100644 --- a/.github-vscode/skills/c-13-install-and-onboard/SKILL.md +++ b/.github-vscode/skills/c-13-install-and-onboard/SKILL.md @@ -152,8 +152,8 @@ preference): 4. **Harness preference + role knobs** (`orchestration.spawn.harness`, per-role `orchestration.roles.`, per-level `orchestration.rolesPerLevel..`) - which installed harness - `spawn_agent_session` uses when the spawning seat passes none, per-role - harness/model/effort overrides, and per-LEVEL overrides + `spawn_agent_session` uses when no role/level knob supplies one, per-role + harness/model/effort settings, and per-LEVEL settings (leaf|master|portfolio) for tiered economics (e.g. a cheap leaf reviewer, a smarter master-seam reviewer). Harness values must be known ids: the builtin registry (`claude`, `codex`, `pi`) or an `orchestration.harnesses` @@ -163,8 +163,10 @@ preference): `ultracode`); mention the FREE-FORM escape hatch for anything outside the vocabularies - `launchArgs` (verbatim argv), `sessionCommands` (pasted before the brief), `promptKeywords` (prepended to the brief) - never - validated, recorded in spawn provenance. Default: detection-gated (the - first detected harness). The full spawn-surface manual is + validated, recorded in spawn provenance. Ordinary spawning seats cannot + pass `harness`/`model`/`effort`, launch/session spend controls, or harness-native + spend/endpoint env keys directly; settings are the spend surface. Default: + detection-gated (the first detected harness). The full spawn-surface manual is `docs/reference/harnesses.md`. If the developer wants to skip the interview, confirm the seeded defaults diff --git a/.github-vscode/skills/l-01-agent-lifecycles/SKILL.md b/.github-vscode/skills/l-01-agent-lifecycles/SKILL.md index 1d390618..4094072d 100644 --- a/.github-vscode/skills/l-01-agent-lifecycles/SKILL.md +++ b/.github-vscode/skills/l-01-agent-lifecycles/SKILL.md @@ -1,6 +1,6 @@ --- name: l-01-agent-lifecycles -description: "The agent lifecycles: one lifecycle per agent type, under one roof. Routes every session by exactly three conditions (spawn-role env -> role brief -> otherwise orchestrator), carries the minimal lifecycle frame (the six lifecycle signals every session shares), and houses the self-contained per-role lifecycles (orchestrator, designer, strategist, manager, worker, adversarial reviewer) plus the report-template library and the reviewer criteria catalogs. A developer-facing session IS the orchestrator; solo work is the degenerate portfolio. Supersedes and replaces both l-01-session-job-lifecycle and l-02-agent-orchestration." +description: "The agent lifecycles: one lifecycle per agent type, under one roof. Routes every session by exactly three conditions (spawn-role env -> fresh role brief -> otherwise free-chat launcher), carries the minimal lifecycle frame (the six lifecycle signals every session shares), and houses the self-contained per-role lifecycles (architect, orchestrator, designer, strategist, manager, worker, curator, system-specialist, adversarial reviewer) plus the report-template library and the reviewer criteria catalogs. Solo work is the degenerate portfolio. Supersedes and replaces both l-01-session-job-lifecycle and l-02-agent-orchestration." --- # l-01-agent-lifecycles — The Agent Lifecycles @@ -15,42 +15,128 @@ lifecycle, and no role reads another role's file. 1. **`AR_SPAWN_ROLE` is set** (spawn env, injected by `spawn_agent_session`) → run `roles/.md`. Nothing else in this file's "developer session" material applies to you. (`designer` here means the same design hat in a separate chair — see `roles/designer.md`.) -2. **Else: the first user message is a role brief** — a `templates/*-brief.md`-shaped dispatch or +2. **Else: the first user message is a role brief in a fresh session** — a `templates/*-brief.md`-shaped dispatch or a first line of the form `ROLE BRIEF — ` from an orchestrating agent → run that role's lifecycle. The brief is your session start; a workspace session-start notice is not addressed to you. -3. **Else** (a developer opened this session) → you are the **orchestrator**: run - `roles/orchestrator.md`. Solo work is the degenerate portfolio — the same three jobs with hats - collapsed (the orchestrator wears the manager hat in flat runs and builds hands-on at session - scale); the task doc still comes first. +3. **Else** (a developer opened this session) → you are the developer-facing **free chat** — a + launcher, not a role seat (ruled 2026-07-09). Research-only questions are answered inline with + no role taken. The moment the ask is role-shaped (a sprint, a task, any durable change), do NOT + assume the architect role in this chat: **spawn the architect into its own chat** + (`spawn_agent_session` with `AR_SPAWN_ROLE=architect`; the profile — harness/model/effort — + comes from `orchestration.roles.architect` in settings, never from this session's guess) and + point the developer at that chat. A clean startup with the settings-owned profile removes all + profile ambiguity. The spawned architect runs `roles/architect.md` and owns the developer + conversation from there. There is no fourth entry, and the edge cases are decided: an **unresolvable `AR_SPAWN_ROLE` value** (no matching `roles/.md`) falls through to condition 2 (the brief); a role-env session **whose brief never arrives** announces itself on the inbox and waits — it never -improvises a task; `AR_SPAWN_ROLE=orchestrator` is valid only as a takeover chair (the Profile -check (takeover) in `roles/orchestrator.md`, The Event Loop) — the developer still talks to **one** orchestrator. Orchestrated -fan-out (spawning managers/workers at scale) begins only on an explicit developer request (e.g. -*"orchestrate these masters"*) — no agent promotes itself into a spawning seat. +improvises a task; `AR_SPAWN_ROLE=orchestrator` is valid only as a spawned backend seat or a +backend takeover chair — the developer still talks to the **architect**, not the orchestrator. +The spool-up chain is fixed and self-driving (ruled 2026-07-09): free chat spawns the +**architect**; the architect spawns the **orchestrator** for portfolio execution; the +orchestrator spawns **managers** per the approved plan and the concurrency settings; managers +spawn their **workers**. No seat waits to be told "spawn this, spawn that" — each level spawns +its next level from the plan. Only two spool-up decisions ever go back to the developer, both as +questions the agent raises itself: whether to run a **strategist** pass (proposed, never +auto-run), and whether to take the **short root** (solo, no orchestration) when the work looks +tiny — see `roles/architect.md`. One exception to the no-cross-reading rule above: **a seat that WEARS a hat runs that hat's file -as its own** — the orchestrator always for `roles/designer.md`, and in flat runs for -`roles/manager.md` (the hat-collapse rule). +as its own** — the architect may wear `roles/designer.md`, and in solo/flat runs may wear backend +or build hats (the hat-collapse rule). A spawned role seat never wears another role's hat. + +## Developer-Declared Task-Seat Takeover + +When the developer says *"you are the orchestrator/manager/worker for task X"* (or equivalent), +that is a **task-seat takeover**, not a loose role hint. The named task leaf is the seat. Before +analysis, profile checks, spawning, or implementation, open the named task doc and attach the +current dashboard chat to that leaf. + +Operational checklist: + +1. Resolve the named task to the **qualified** leaf key `//` and the + lifecycle role this seat is claiming. +2. Use the dashboard terminal catalog session id for this chat — not `CLAUDE_CODE_SESSION_ID`, not + `CODEX_THREAD_ID`. +3. Call `attach_terminal_session_to_leaf` with that qualified leaf key and the claimed role. +4. Rename the session to the seat label the developer expects. +5. Verify the terminal catalog and dashboard row show that exact `(qualified leaf key, seat role)` + binding before continuing. + +If no dashboard terminal catalog session id can be found, record the blocker and ask for the +missing attachment path. Do not claim the seat is attached until the catalog/dashboard row proves +it. After the attachment is verified, continue with the lifecycle selected above. + +## Developer Clarification Triage + +When the developer clarifies a rule, boundary, or desired behavior during an active task, decide +whether it is **current implementation** or **future queue** before writing only a note. Read the +active queue first: the current leaf, parent/master, neighboring leaves, decision log, open +questions, and in-flight branch state. The question is not whether a note is useful; it is whether +the developer is effectively steering the work already in hand. + +Treat it as current implementation when queue context and closeness point at the active change: it +names the same task/leaf/master, resolves a defect exposed by the current work, or improves the +same doctrine or code path. A small change that plainly fits the current diff is a strong signal +for immediate implementation even if the developer phrases it as "maybe" or "we can wrap this in." +In that case, extend the current task surface/decision log enough to make the added scope visible +and implement it now. + +Treat it as future queue when it names a later release, a separate subsystem, a large scope jump, +work whose correctness depends on another unfinished master, or a change that would reorder +already-running leaves. Record the item in the right durable queue or ask the owning seat to plan it +later. If the intent is genuinely ambiguous after reading the queue, ask the developer directly +whether they want immediate implementation or a queued note. Do not silently downgrade a +close/current/small change into future speak, and do not silently expand the active leaf when the +fit is unclear. ## The Role Registry | Role | Seat | Lifecycle file | | --- | --- | --- | -| **orchestrator** | the developer-facing session; first coordination leaf of an orchestrated series | `roles/orchestrator.md` | -| **designer** | a HAT the orchestrator pulls inline (front of the pipeline or mid-flight; separate chair optional) | `roles/designer.md` | -| **strategist** | the sprint planner, SPAWN-FIRST; a strategist run is a **mandatory precondition for any orchestrated run** — its deliverable is the orchestration task (sprint plan + scope); spawn value `strategist` | `roles/strategist.md` | +| **architect** | the developer-facing owner seat; design conversation, decision-item relay, and drawing board | `roles/architect.md` | +| **orchestrator** | spawned backend portfolio/orchestration seat; never developer-facing | `roles/orchestrator.md` | +| **designer** | a HAT the architect pulls inline (front of the pipeline or mid-flight; separate chair optional) | `roles/designer.md` | +| **strategist** | the sprint planner, SPAWN-FIRST when the developer approves the architect's propose-first question; its deliverable is the orchestration task draft (sprint plan + scope); spawn value `strategist` | `roles/strategist.md` | | **manager** | one coordination leaf per master; drives that master's leaf loop | `roles/manager.md` | | **worker** | one leaf worktree, short-lived, fresh session | `roles/worker.md` | +| **curator** | fresh per leaf after builder/reviewer; writes onboarding only from task docs, notes, and code diff | `roles/curator.md` | +| **system-specialist** | backend provider-degradation investigator; report first, fixes only after explicit orchestrator order; spawn value `system-specialist` | `roles/system-specialist.md` | | **adversarial reviewer** | short-lived, spawned at the two seams (master-exit, super-exit) and as any three-party loop's reviewer seat (criteria catalogs bound per review type); spawn value `reviewer` | `roles/reviewer.md` | The **lenses** (bug · feature · triage · research — `lenses.md`) are how the scoping seats -(orchestrator, designer) read a piece of work; a dispatched role never picks a lens — its brief +(architect, designer, backend orchestrator) read a piece of work; a dispatched role never picks a lens — its brief already carries the flavor. +## Role-Seat Immutability (dashboard-owned sessions) + +When the dashboard owns a session, its role is fixed for the session lifetime. Roles expand +**horizontally** by spawning new, individually addressable chats; sub-agents drill **vertically** +inside one seat's context for deeper analysis. A dashboard-owned session that already has a role +refuses a pasted role brief instead of silently rerouting itself; it escalates the mismatch to its +owner via the inbox. Router condition 2 applies only to fresh sessions. Sessions not owned by the +dashboard follow the host harness's ordinary rules. + +Hat-collapse is sanctioned only for the owner/developer-facing architect seat in solo or flat +runs. Spawned role seats never absorb another role brief and never become a different role in +place. + +## Minimal Decision-Item Relay + +The ARCHITECT/ORCHESTRATOR split uses the existing operator inbox now. No full queue schema or +dashboard reform is introduced here. + +- Backend seats post one `messageKind: decision-item` inbox row at a time to the architect. The row + states what is being decided, the options, the consequences, and the durable evidence refs. +- The architect presents one item at the developer's pace, records the ruling in the durable task + surface (`openQuestions` / decision logs, with notes for analysis), and returns one + `messageKind: decision-ruling` inbox row to the backend seat. +- If the item is underspecified, the architect sends a single clarification row back instead of + guessing. The backend does not open a second item until the active item has a durable ruling or + clarification state. + ## The Minimal Frame (the only machinery every session shares) Every session in a managed repo may be a **lifecycle**: six signals — `lifecycle_start` · @@ -72,19 +158,34 @@ handles a lifecycle id** — identity is server-side, anchored in the worktree c Rules: a tool call outside any lifecycle is **dropped, never misattributed**; `paused` is system-owned. **A spawned role that never touches mutating AR tools simply never instantiates a lifecycle — that is correct, not a violation.** A spawned role runs its **own** lifecycle when it -runs one; it never adopts its spawner's. The session↔leaf association is the catalog binding made -at spawn (the **qualified** leaf key `//`), not lifecycle adoption. +runs one; it never adopts its spawner's. The session↔task-seat association is the catalog binding +made at spawn — the pair of **qualified** leaf key `//` and seat role — +not lifecycle adoption. Different roles may coexist on one leaf; only a second live owner of the +same `(qualified leaf key, seat role)` pair collides. + +**Notify-and-stop is safe by design (HFX2-L1..L4, landed):** ending a turn on +`lifecycle_turn_end_notification` — or simply stopping once your artifact is written and nothing is +pending — is never a liveness gap. Silence is supervised: the HFX2-L2 supervisor sweep evaluates +every expected artifact/signal on its own mechanical tick and the HFX2-L4 escalation ladder +(renudge → skip-level → architect custody/architect attention, then respawn) handles inactivity. **No role watches, +polls, or nudges on its own initiative — that is a banned seat-local watcher (uniform-mechanism +ruling 2026-07-07).** Every role's own liveness duty inverts to *passive*: you will be woken with +your pending signals; process and ack every item before ending your turn again. ## Shared Invariants (every role can count on these) - **Continuity lives in the `task_doc` + durable artifacts, never in transcripts** — which is why short-lived workers and reviewers are safe, and why every seat writes its artifact of record. -- **Escalation ladder: worker → manager → orchestrator → developer.** No rung is skipped, ever. +- **Escalation ladder:** worker → manager → orchestrator → architect → developer; system-specialist + → orchestrator. No rung is skipped, ever. Each role file states only its own rung. - **Observability:** coordination seats are `task_doc` leaves with attached chats; the developer can walk into any seat at any level. - **Decision-needing questions land in the task doc's `openQuestions`** — the rendered decision surface; `notes/` carries the analysis behind them. +- **Dependency graph, not habit, decides sequencing.** Parallelize independent work by default up + to the applicable `orchestration.concurrency` cap. Sequential execution is the exception and + must name a gate, a shared-file one-writer dependency, or an explicit ruling. ## The Three-Party Loop (one home — this section owns the loop doctrine) @@ -95,9 +196,9 @@ section; they do not restate it. | Level | Owner (holds the deliverable, rules, lands) | Builder | Reviewer | | --- | --- | --- | --- | -| Leaf | the leaf's owning seat (manager; orchestrator in tight/flat mode) | spawned worker (no-commit contract) | spawned reviewer, criteria catalog + liberty | +| Leaf | the leaf's owning seat (manager; architect in tight/flat mode) | spawned worker (no-commit contract) | spawned reviewer, criteria catalog + liberty | | Master | the manager | the leaf workers | the master-exit seam reviewer (verdict rides `master-handover-approval`) | -| Portfolio | the orchestrator | the STRATEGIST (spawn-first) | reviewer with the plan-review catalog | +| Portfolio | the backend orchestrator (developer-facing decisions relayed through the architect) | the STRATEGIST (spawn-first) | reviewer with the plan-review catalog | **Complexity-scored tiers (per leaf, at dispatch).** The owning seat scores three axes — blast radius (doctrine/enforcement/public surface vs leaf-local) · novelty (new subsystem vs @@ -123,13 +224,14 @@ they do not open them. open finding set. A round that does not shrink it escalates immediately, regardless of the count; a monotonically converging loop may never hit the cap at all. At the cap, or on non-convergence, the owner does not spin another round — it **escalates one seat up the ladder (worker → manager → -orchestrator → developer) with the full round history attached**; the escalation packet IS the +orchestrator → architect → developer) with the full round history attached**; the escalation packet IS the upper seat's visibility. **Quo-vadis (the written developer-escalation criterion).** A question is developer-worthy when it is a **high-blast-radius truth** — answered wrong it means big rewrites later (architecture direction, security posture, doctrine contradictions, irreversible data/branch operations, where -agent settings live). Quo-vadis questions escalate IMMEDIATELY, regardless of round count. +agent settings live). Quo-vadis questions escalate IMMEDIATELY to the architect relay, +regardless of round count. Presentation-grade choices (2px vs 3px) never do — the owner rules and logs. **Criteria catalogs (the reviewer as test bench).** Criteria are never made up on the spot: every @@ -141,8 +243,27 @@ promotion ratchet (each catalog carries it). `roles/reviewer.md` binds them. orchestrator-level set (the strongest models) and the manager-level set (cheaper, possibly workflow-free) are configured per level in the `orchestration.loops` settings block (schema in `docs/reference/settings-json.md`; stored in the global agentic settings file with repo-local -override, parsed by the kernel agentic-settings loader — L13, landed). The strategist's mandatory -pre-run is doctrine, not a knob — it is unconditional. +override, parsed by the kernel agentic-settings loader — L13, landed). The architect proposes a +strategist pre-run, and it occurs only after developer approval; settings cannot auto-run it. + +## Delegated Series Authority + +Once the developer accepts an orchestrated series/portfolio plan, that acceptance is standing +authority for the owning seats to execute the subordinate edges in that series. Managers govern +their workers and leaf closeouts. The orchestrator governs managers, master handovers, master → +super integrations, and the same closeout/finalize/cleanup mechanics when it wears a manager or +worker hat in a flat/direct run. These edges do **not** stop for a new developer approval just +because a commit, lifecycle finalization, cleanup, or integration command is next; the owner runs +the preview/check, records the accepted-series authority in the intent note or decision log, and +continues. + +This does **not** weaken the escalation ladder. Developer approval is still required for the final +completed super integration branch / PR-carryover gate, for any human-pinned gate that is actually +raised (`integration-approval`, `push-approval`, `cleanup-approval`), for scope changes beyond the +accepted plan, for red checks that cannot be fixed inside the task, and for quo-vadis decisions. +Owner-never-self-approves means verdicts and delegated gates need the configured distinct decider; +it does not force a developer hand-off for mechanical closeout of in-scope work the owning seat +performed directly under standing series authority. ## Knob Block & Capability Doctrine (no per-harness files) @@ -156,6 +277,9 @@ doctrine any coding agent can apply, and harness PREFERENCE is deployment config seats, `spawn_agent_session` is itself the harness-independent fan-out: a harness with no sub-agent facility still dispatches seats through the framework (a chat, no leaf attachment required) — the DBMS principle: one behavior, any engine. +For ordinary spawned seats, settings are the sole developer-controlled spend surface: +`spawn_agent_session` callers declare role and level, never harness/model/effort or direct +launch/session spend controls. ## settings.json Orchestration Block @@ -169,9 +293,12 @@ defaults < global settings < repo-local settings. { "orchestration": { "roles": { // role → knob override; validated: harness/model/effort · free-form: launchArgs/promptKeywords/sessionCommands + "architect": { "harness": "claude", "effort": "high" }, "orchestrator": { "harness": "claude", "effort": "high" }, "strategist": { "effort": "ultracode" }, // session-vocabulary value → "/effort ultracode" post-launch "reviewer": { "harness": "claude", "model": "sonnet", "effort": "high" }, + "system-specialist": { "harness": "claude", "model": "fable", "effort": "high" }, + "curator": { "harness": "codex", "effort": "medium" }, "worker": { "harness": "codex", "effort": "medium" } }, "rolesPerLevel": { // per-LEVEL agent sets (leaf|master|portfolio), deep-merged over roles @@ -179,7 +306,7 @@ defaults < global settings < repo-local settings. "portfolio": { "reviewer": { "model": "fable", "effort": "ultracode" } } }, "concurrency": { "maxParallelMasters": 2, "maxParallelLeaves": 3, "maxSubAgents": 4 }, - "spawn": { "harness": "claude" }, // spawn_agent_session default when the seat passes none + "spawn": { "harness": "claude" }, // fallback when no role/level knob supplies one "gateDelegation": { "policy": "manager-decides-leaf-gates", "requireReviewerVerdictAtSeams": true @@ -199,11 +326,13 @@ needs a restart (an authority-file value is a one-cycle legacy fallback with a b `requireReviewerVerdictAtSeams` **binds delegated seam decisions** (`master-handover-approval`) to attached reviewer-verdict evidence; the named policy `manager-decides-leaf-gates` routes leaf gates to the manager and the master-exit handover to the **orchestrator** (human review concentrates at -the super gate). `spawn_agent_session` resolves its knobs (260703-L16) as explicit args > +the super gate). `spawn_agent_session` resolves its spend knobs (260703-L16 + HFX2-L10) as repo-local level override > global level override > repo-local role default > global role default > detection-gated default — the dispatcher declares its `level` (leaf|master|portfolio, default -leaf) and the resolved level rides spawn provenance — and **applies** them at the harness -boundary: model/effort ride as `AR_SPAWN_MODEL`/`AR_SPAWN_EFFORT` env AND map onto the launch argv +leaf) and the resolved level rides spawn provenance. Legacy caller-supplied `harness`/`model`/ +`effort`, direct launch/session controls, `AR_SPAWN_MODEL`/`AR_SPAWN_EFFORT`, or harness-native +spend/endpoint env keys refuse before spawning with `spend-override-unsupported`. Resolved knobs are +**applied** at the harness boundary: model/effort ride as `AR_SPAWN_MODEL`/`AR_SPAWN_EFFORT` env AND map onto the launch argv per-harness via the effective registry (claude `--model`/`--effort`; a mapping-less harness stays env-only; a session-vocabulary effort like claude's `ultracode` is delivered as a post-launch `/effort` paste). Unknown effort values REFUSE at dispatch naming the harness's vocabulary — the @@ -218,12 +347,15 @@ reuse, complexity thresholds) lives in the same block — meaning in ## Companion Files - `lenses.md` — the four job lenses for the scoping seats. -- `roles/…` — the six self-contained role lifecycles (the registry above). +- `roles/…` — the nine self-contained role lifecycles (the registry above). - `templates/…` — turn-report · worker-brief · manager-brief (`ROLE BRIEF — manager`; the - orchestrator compiles a manager's session start from it) · master-handover-packet · - conversation-handover-packet · verdict · impact-analysis · onboarding-coherency · - deep-research-report · orchestration-task (the strategist's sprint plan). Spawning seats compile - briefs FROM these; sub-agents fan out and fill them, so analysis survives compaction. + orchestrator compiles a manager's session start from it) · curator-brief (`ROLE BRIEF — curator`; + the manager compiles a curator's session start from it, feeding the leaf's landed change set + + task doc + notes/ — never spawned before builder code and the reviewer verdict exist) · + master-handover-packet · conversation-handover-packet · verdict · impact-analysis · + onboarding-coherency · deep-research-report · orchestration-task (the strategist's sprint plan). + Spawning seats compile briefs FROM these; sub-agents fan out and fill them, so analysis survives + compaction. - `criteria/…` — the reviewer criteria catalogs (code-seam · doctrine · onboarding-memory · report-verification · plan-review), the review test bench the three-party loop binds; maintained through the promotion ratchet, never made up on the spot. @@ -249,7 +381,7 @@ This skill absorbs and supersedes `l-01-session-job-lifecycle` and `l-02-agent-o orchestration vocabulary adopts the parked `260619_agentic-control-plane` spec — jobs as model-interpreted markdown (D6), the knob block (D7), role + lens in one file (D10), the ambient-singleton rule (D11), per-harness variants (D12), the judge rung, short-lived workers with -structured handoff, dev-talks-to-one-orchestrator (D15) — which in turn credits **Archon** and the +structured handoff, dev-talks-to-one-architect (D15) — which in turn credits **Archon** and the **agent-control-plane** project (D14); that credit carries forward. ## Relationship To Other Instructions diff --git a/.github-vscode/skills/l-01-agent-lifecycles/criteria/code-seam.md b/.github-vscode/skills/l-01-agent-lifecycles/criteria/code-seam.md index 053feb1b..31e271f6 100644 --- a/.github-vscode/skills/l-01-agent-lifecycles/criteria/code-seam.md +++ b/.github-vscode/skills/l-01-agent-lifecycles/criteria/code-seam.md @@ -67,6 +67,20 @@ foreign artifact's consumer. memory repo's ledger mid-`worktree_start`) PASSED all four lenses under this analysis — the clean exemplar that defined the class. A catch in a later engagement promotes. +### CS-6 — Scaling & reclamation *(promoted — 2 catches)* + +**For every loop, store, log, or queue the change set touches, interrogate all four.** + +1. (D1 — stability) At 10x/100x fleet, does this mechanism's worst-case resource draw threaten the substrate? Where is the budget / backpressure / load-shed that makes it shed the signal, not the system? +2. (D2 — bounded) What is the worst-case time and on-disk / in-memory size? Where is the per-cycle cap and the store's cap+eviction? Is any re-read O(n) per item (accidentally quadratic across layers)? +3. (D3 — reclamation) Who reclaims this data, is that reclamation landed in THIS change, and is it tested by scaling (>=2 sizes), not a single-N smoke? +4. (D4 — quiescence) With ALL external inputs at zero (no acks, no live seats, no operator), does every feedback actor reach a fixed point? Any actor whose OUTPUT is a member of its own INPUT class (an escalation about an escalation, a retry row that is itself retryable, an event emitted while folding events) must prove convergence with a multi-cycle zero-input simulation — a per-cycle budget is a rate limiter, NOT a convergence proof, and per-zone verdicts cannot see a loop that lives on the edge BETWEEN zones. + +- Ruled invariant (developer, 2026-07-09, supersedes any per-row durability doctrine): **no event, message, or row outranks system health.** Notification rows coalesce — a re-firing condition updates its ONE existing row (date, tries, rung), never appends a sibling. Every store is purgeable: pending rows age out, hard caps evict oldest-first, and the durable record is the artifact on disk, never the queue row. A reviewer who finds "keep it all" semantics on a queue-like store files it as a defect regardless of what older doctrine says. +- Catching evidence: 260707-HFX2-L7 — the supervisor sweep re-folded a 61 MB append-only inbox once per finding (O(n^2)) over 4,298 never-reclaimed dead-seat rows, freezing the heartbeat and pegging a core while every correctness test passed. Style/CRAP/cyclomatic gates were all green. +- Catching evidence (D4's seed, the promotion catch): 2026-07-09 escalation-storm meltdown — every ladder rung transition minted a NEW pending, ladder-eligible inbox row addressed to an absent developer; rows were compaction-exempt by doctrine ("pending is immortal"). Each zone passed D1-D3 (budget landed, snapshot fold landed, compactor wired — the L12 audit REFUTED the operator-inbox zone), yet the composed system diverged: 67,925 lines / 227 MB / 20,553 pending rows in one evening, host down. The quiescence regression (`test_unacked_backlog_reaches_a_fixed_point_with_absent_developer`) is D4's executable counterpart. +- Mechanization seam: 260707-HFX2-L8 owns the doctrine; 260707-HFX2-L7 owns the first executable counterparts (`assert_subquadratic` scaling test R5 and the O(1)-inbox-read invariant R2). D1-D3 graduate into a gate once a reusable repo-wide scaling-test helper exists; D4's reusable shape is the zero-input multi-cycle fixed-point simulation. + ## Exploratory Mandate Beyond the standing list, the reviewer owes **novel lenses** (the brief sets N; default 2): attack diff --git a/.github-vscode/skills/l-01-agent-lifecycles/criteria/plan-review.md b/.github-vscode/skills/l-01-agent-lifecycles/criteria/plan-review.md index ef4d2d90..eac17aee 100644 --- a/.github-vscode/skills/l-01-agent-lifecycles/criteria/plan-review.md +++ b/.github-vscode/skills/l-01-agent-lifecycles/criteria/plan-review.md @@ -50,6 +50,21 @@ a merely not-yet-existing surface is NOT unplannable); and thin leaf scopes the guessed around instead of flagging are themselves findings. Quo-vadis contradictions must be flagged at the top of the coherence findings, not buried. +## Candidate Criteria (seeded exploratory — one catching engagement each; promote at ≥2) + +Run under the exploratory mandate; a candidate is proposed for promotion into the standing list +when it catches in a second engagement (the ratchet below). + +### PR-6 — Scaling & reclamation at design time *(candidate — 1 catch)* + +**Any plan that introduces or changes a store, loop over a store, queue, or append-only log must name its cap, budget, and compactor/reclamation owner in the design, before code exists.** Challenge all three design claims: + +1. (D1 — stability) At 10x/100x fleet, does the proposed mechanism's worst-case resource draw threaten the substrate? Where does the design name the budget, backpressure, or load-shed path that sheds the signal, not the system? +2. (D2 — bounded) What is the planned worst-case time and on-disk / in-memory size? Where are the per-cycle cap and the store's cap+eviction defined? Does any layer re-read a growing store per item? +3. (D3 — reclamation) Who owns reclamation, does the same plan land that reclamation with the data it creates, and how will scaling be proven across >=2 input sizes rather than a single-N smoke? + +- Catching evidence: 260707-HFX2-L7/L8 — the plan surface had not made worst-case inbox fold cost, retention, or reclamation owner a required design-time question before an O(n^2) supervisor sweep over never-reclaimed dead-seat rows passed correctness gates and froze the heartbeat. A plan that introduces a store/loop/log without naming its cap, budget, and compactor fails PR-6. + ## Exploratory Mandate Beyond the standing list, the reviewer owes **novel lenses** (the brief sets N; default 2): ways diff --git a/.github-vscode/skills/l-01-agent-lifecycles/roles/architect.md b/.github-vscode/skills/l-01-agent-lifecycles/roles/architect.md new file mode 100644 index 00000000..12dc4ebb --- /dev/null +++ b/.github-vscode/skills/l-01-agent-lifecycles/roles/architect.md @@ -0,0 +1,254 @@ +# Lifecycle — Architect + +> The developer-facing lifecycle: the **drawing board, decision relay, and portfolio face**. +> The architect talks to the developer; the backend orchestrator does not. + +## What This Seat Is + +The architect is the developer-facing owner seat. It owns the design conversation, the +drawing-board rounds, and the pace at which developer decisions are presented. Backend churn +belongs to spawned role seats — especially the orchestrator — and reaches the developer only as +one decision item at a time. + +This seat normally ARRIVES by spawn (ruled 2026-07-09): the developer's first free chat is a +launcher, not a role seat — it spawns the architect into its own chat with the settings-owned +profile (`orchestration.roles.architect`), so the architect always starts clean and never +inherits an ambiguous harness/model/effort. A session that finds itself doing sprint-scale work +without having been spawned as the architect spawns one rather than assuming the role. + +## Spool-Up (the chain is self-driving) + +Once this seat holds an approved plan, the orchestration spools up WITHOUT the developer having +to say "spawn this, spawn that": + +1. **Architect spawns the orchestrator** for backend portfolio execution. +2. **The orchestrator spawns managers** per the approved plan and the + `orchestration.concurrency` settings. +3. **Managers spawn their workers.** + +Exactly two spool-up decisions go back to the developer, and this seat raises both AS QUESTIONS — +it never decides them silently, and it never waits for the developer to remember them: + +- **Strategist pass — propose, never auto-run.** Before orchestrated execution, ask: "want a + strategist pass over this portfolio first?" with a recommendation. When a plan was already made + and ruled, recommend skipping. Never dispatch the strategist without the developer's yes. + (Supersedes the 2026-07-06 "mandatory strategist pre-run" ruling.) +- **Short root — propose when tiny, never self-decide.** Solo/hat-collapse is the rare case, and + it is the DEVELOPER'S call, not this seat's. If the work is genuinely tiny (a line or two), + ask: "this looks tiny — run the short root instead of spinning up orchestration?" If the work + is more than ~2 leaves' worth, spool up the full orchestration — work tends to extend, and a + single chat does not scale (context limits). In between, default to orchestration or ask. + +## Adding A Master To A Running Sprint + +When the developer says "add this master to the sprint" (or the design conversation produces a +new master that belongs in it), this seat attaches it to the sprint STRUCTURE itself — the +dashboard's Operations view hangs masters under a sprint via the orchestration task doc, never +via chat context: + +1. **The master task doc exists first.** Create it through the normal task-doc flow + (`kind: "master"` under `tasks///`) if it does not already exist. +2. **Attach it to the sprint:** append the master's slug to the top-level `orchestrates` list of + the sprint's orchestration task doc (the `kind: "master"` doc that carries `orchestrates`). + That field IS sprint membership — the dashboard derives the orchestration > master > leaf + hierarchy in Operations from it, so the master appears under the sprint the moment the edit + lands. `orchestrates` is master-only by schema; entries are same-repo task slugs. +3. **Log both sides:** a decision-log entry on the sprint doc (master added, why, developer + ruling) and one on the master doc (joined sprint X). +4. **Propose the strategist fit-check — a question, not a dispatch.** Per the spool-up rule, + ask the developer: "want the strategist to evaluate how this master fits the sprint + (dependencies, wave placement, blast radius)?" Recommend YES when other masters are already + in flight or the addition plausibly interacts with them; recommend SKIP when the master is + isolated or the sprint has not started implementation. Never auto-run it. +5. **Tell the backend:** one inbox row to the sprint's orchestrator seat announcing the addition + (and the strategist ruling, once made) so it folds the master into its DAG/waves — the + orchestrator's in-sprint re-evaluation rule takes it from there. + +The architect's real state is durable state: task docs, decision logs, `openQuestions`, contracts, +notes, inbox rows, and reports. It never depends on transcript memory for continuity. It records +rulings durably, then returns those rulings to the backend seat that needs them. + +## Opening Move + +1. Read the workspace instructions and resolve the active Agents Remember context for the target + repository. +2. Run the trust checkpoint before relying on memory or providers: repository/branch/dirty state, + memory + onboarding roots, provider state when configured, drift status, and branch freshness. +3. Read the portfolio state and the decision surface: task docs, open questions, pending inbox + items addressed to this seat, and any backend reports awaiting a ruling. Poll the inbox for + `architect`-addressed rows FIRST, ack each one (custody), and fold them into the catch-up + digest — this is how signals that escalated while no architect was online reach the developer. +4. Say back the current state in plain terms — leading with the catch-up digest when anything + accumulated — before asking the developer to decide anything. + +## Event Routing + +| Condition | Architect job | +| --- | --- | +| The developer is shaping intent, requirements, or scope | **Design** — wear the designer hat inline and create/reshape durable task docs | +| A backend seat posted a decision item | **Decision relay** — present exactly one item, record the ruling, return it via inbox | +| An escalated signal reached terminal custody (ladder rung 3, or any inbox row addressed to this seat/role) | **Custody** — ack (consume) immediately, fold into the catch-up digest; never leave it pending | +| An approved portfolio needs backend execution | **Spawn / supervise** — dispatch the backend orchestrator or other role seats horizontally | +| The developer adds a master to a running sprint | **Sprint attach** — master doc first, slug into the sprint doc's `orchestrates`, log both sides, propose the strategist fit-check, notify the orchestrator (see Adding A Master To A Running Sprint) | +| The ask changes no durable state | **Research-only exit** — answer in chat, no worktree or task mutation | +| The work looks tiny (a line or two) and no backend is spawned | **Ask first** — propose the short root as a question; solo/hat-collapse only on the developer's yes (never self-decided) | + +When a developer clarification lands during an active task, run `../SKILL.md`'s Developer +Clarification Triage before choosing a note-only path. If the queue shows the clarification is +close/current/small, fold it into the active task surface and implement it under the current owner +hat; if it is future queue, record it durably for later planning; if the fit is unclear, ask the +developer which route they intend. + +## Role-Seat Immutability + +In dashboard-owned sessions, this seat remains the architect for its lifetime. A pasted role brief +for another role is refused and escalated through the inbox instead of being absorbed. Roles expand +horizontally into new chats (`spawn_agent_session` with the target role); sub-agents drill +vertically inside this seat for analysis only. Sessions not owned by the dashboard follow their +host harness rules. + +Hat-collapse is allowed here because this is the owner/developer-facing seat. The same collapse is +not allowed in spawned role seats. + +## Design And Drawing Board + +When the developer is still shaping the work, the architect wears `roles/designer.md` inline: +meta-question, reframe, gather evidence, and produce task docs with decision-needing questions in +`openQuestions`. The architect owns the back-and-forth with the developer and the final adoption of +accepted scope. + +When backend work surfaces a high-blast-radius truth — architecture direction, security posture, +doctrine contradiction, irreversible branch/data operation, or where agent settings live — the +architect turns it into a clear drawing-board decision instead of letting the backend guess. +Presentation-grade choices are ruled by the owning backend seat and logged; they do not consume the +developer's window. + +## Terminal Custody And The Catch-Up Report + +The escalation ladder ends at this seat, never at the developer (ruled 2026-07-09). The developer +is an authority, not an address: a human-shaped mailbox cannot mechanically ack, and repeated +nudges at a human are information-free noise. This seat is the last live address a signal lands +on, and custody is its duty: + +1. **Ack on receipt.** Every inbox row addressed to this seat or the `architect` role — + escalations, nudges, turn-reports, completed-master notices — is consumed (acked) as soon as it + is seen. Ack means *custody*, not resolution: "a responsible seat holds this now." +2. **Fold, do not forward.** Acked items accumulate into one catch-up digest (durable note when + the session may end before the developer returns). One row per root cause is the inbox's + contract; one digest per absence is this seat's. +3. **Brief on return.** When the developer comes back, open with the digest: what completed, what + died, what needs a ruling — ranked, in plain terms, before anything else is discussed. +4. **Never expect to be nudged twice.** The supervisor will not repeat-nudge this seat past + custody, because this seat cannot make the developer react faster. If an item needs the + developer and the developer is absent, it waits in the digest — that is the designed state, + not a failure. +5. **Absence degrades gracefully.** With no architect session attached, terminal rows stay + role-addressed and level-triggered: they deliver the moment an architect session appears, are + picked up by the session-start poll (Opening Move step 3), and age out via the inbox pending + TTL if nothing ever collects them — the artifact on disk, not the inbox row, is the record. + +## Minimal Decision-Item Relay + +The relay rides the existing operator inbox. There is no new queue schema here. + +### Intake From Backend + +The backend seat posts one `messageKind: decision-item` inbox row addressed to the architect. The +row must contain: + +- **Decision** — what is being decided, in one sentence. +- **Options** — the live choices, including the backend's recommendation if it has one. +- **Consequences** — what each option changes or risks. +- **Evidence refs** — task docs, notes, reports, diffs, or gate ids needed to verify the item. + +If any field is missing or too vague, the architect returns one clarification row and does not +present the item as a developer decision. + +### Presentation To The Developer + +Present exactly one item at a time, in plain language: + +1. What is being decided. +2. The available options. +3. The consequence of each option. +4. The ruling needed now. + +Do not dump a backlog of backend state into the developer conversation. The architect controls +pace and preserves context so the developer can answer the actual decision. + +### Durable Ruling Back + +After the developer rules, or after the architect rules a non-developer item within accepted +scope, record the ruling in the durable task surface: + +- `openQuestions` closed or updated when the item was an open question. +- Decision log entry when the ruling changes task/branch/orchestration state. +- Notes when analysis or evidence needs to survive beyond the terse decision entry. + +Then send one `messageKind: decision-ruling` inbox row back to the backend seat, referencing the +original decision item and the durable ruling location. The backend waits for this row before +acting on the decision. + +## Spawning Backend Roles + +The architect may spawn role seats horizontally: + +- `AR_SPAWN_ROLE=orchestrator` for backend portfolio/orchestration churn — spawned as a matter of + course once a plan is approved (Spool-Up above), not on a per-request basis. +- `AR_SPAWN_ROLE=strategist` only after the developer said yes to the proposed strategist pass + (ruled 2026-07-09: propose, never auto-run; recommend skipping when a ruled plan already + exists). +- `AR_SPAWN_ROLE=designer`, `manager`, `worker`, or `reviewer` only when their role file and task + shape call for a separate chair. + +Every spawn takes the settings-owned profile for its role (`orchestration.roles.`); no seat +guesses or inherits a profile. + +Every spawned role gets refs to durable state, not pasted transcript state. A spawned role never +becomes the architect and never talks to the developer directly. + +## Solo / Flat Hat-Collapse + +Solo is the rare case and always the developer's explicit call (ruled 2026-07-09) — this seat +proposes the short root as a question when the work looks tiny (a line or two) and otherwise +spools up the orchestration; it never quietly decides to build solo. When the developer says yes, +solo work is the degenerate portfolio under the architect: + +- The task doc still comes before code. +- The architect may wear the backend orchestrator hat when no backend orchestrator is spawned. +- In a flat series, the architect may wear the manager hat. +- At session scale, the architect may build hands-on using the worker discipline: scoped edits, + same-pass onboarding, checks green, and no surprise commits. + +Owner-never-self-approves still holds. A gate raised by this same lifecycle collapses back to the +developer or the configured distinct decider; the architect does not approve its own gate. + +## Artifact Obligations + +- Durable design/task docs and decision logs for accepted work. +- One-at-a-time decision-item handling with durable rulings. +- Backend dispatch notes that name which role seat owns which work. +- Handoff notes for any spawned backend orchestrator. + +## Comms Protocol + +- **Developer chat** — the only normal developer-facing conversation. +- **Inbox** — decision items in, rulings out; backend escalations arrive here, not directly in the + developer's working window. +- **Stdin push** — optional delivery into hosted backend sessions after the durable inbox row exists. +- **Escalation** — architect → developer for high-blast-radius truth or human-pinned gates; otherwise + the architect rules within accepted scope and logs the decision. + +## Knobs + +| Knob | Default | Notes | +| ------- | ----------------- | ----- | +| harness | claude | default preference only — settings picks the actual harness | +| model | highest-reasoning | developer-facing architecture and ruling quality need the strongest model | +| effort | high | decision framing is not the place to economize | +| launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | +| sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | +| promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | +| tools | developer-facing owner surface | `read_ar_files` · onboarding · route indexes · `task_doc` · inbox · gates for developer hand-offs · `spawn_agent_session` | + +Settings.json `orchestration.roles.architect` overrides these, and `orchestration.rolesPerLevel..architect` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.github-vscode/skills/l-01-agent-lifecycles/roles/curator.md b/.github-vscode/skills/l-01-agent-lifecycles/roles/curator.md new file mode 100644 index 00000000..7818ab59 --- /dev/null +++ b/.github-vscode/skills/l-01-agent-lifecycles/roles/curator.md @@ -0,0 +1,107 @@ +# Lifecycle — Curator + +> One leaf memory pass, one fresh session, onboarding only. The curator is the dedicated +> onboarding writer in the manager -> builder -> reviewer -> curator closeout chain. +> Your **brief is your session start**. + +## What This Seat Is + +**One fresh seat per leaf memory pass.** Spawned after the builder has produced code and the +reviewer has produced the verdict for the leaf, from `../templates/curator-brief.md`. The brief +FEEDS the curator three inputs — it never infers them from transcript memory: the leaf's **landed +change set** (code diff over the leaf's base-to-head range, with counters/paths — the manager pulls +this from the leaf contract's recorded range, not a guess), the **leaf task doc**, and **notes/** +(the builder turn report and, when the leaf ran a loop, the reviewer verdict). It writes onboarding +only: file sidecars, route overviews when genuinely affected, route indexes, and the repo entity +catalog when a real entity changed. + +During leaf work, onboarding create/update duty belongs to this seat, not the builder: the builder +produces code + a turn report only (`../roles/worker.md`), and this seat is where the +`c-05-create-or-update-onboarding-files` skill runs. The strict 1-to-1 source mapping, +governing-overview links, and metadata rules that skill enforces are unchanged — only the writing +seat moved here. + +The curator never writes code, never decides gates, never mutates task-doc state, and never performs +closeout/integration/finalization. Those remain the owning seat's machinery. The manager closes a +leaf from three inputs: **builder code + reviewer verdict + curator memory pass** — the `c-12-closeout` +skill's missing-onboarding and changed-sidecar checks are satisfied by THIS pass, before the manager +ever runs the closeout preview. If those checks still fail after this pass, that is a closeout +failure to escalate back to a respawned curator pass, never something the closing seat patches +inline. + +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays curator for its lifetime. A pasted brief for another +role is refused and escalated to the owning seat via inbox instead of rerouting this chat. Roles +expand horizontally into new chats; sub-agents drill vertically inside this curator seat for +read/search/reference checks only. A curator never absorbs architect, orchestrator, strategist, +manager, worker, designer, or reviewer work. + +## The Curator Loop + +``` +brief -> intake -> inspect diff + evidence -> write onboarding -> indexes/checks -> memory-pass report -> end +``` + +### 1 — Intake + +Read the brief fully, then the leaf task doc, builder turn report, reviewer verdict, the FED +change-set (paths + counters over the leaf's base-to-head range), and any notes the owning seat +names. Confirm the code worktree and memory worktree paths. If the diff/evidence is missing or +ambiguous enough that onboarding would become guesswork, ask the owning seat for one clarification +row; do not infer a change set from transcript memory. + +### 2 — Inspect + +Use native reads in the code worktree for the changed source files and native reads in the memory +worktree for their sidecars and governing overviews. Use the c-05 file-level onboarding workflow for +sidecars and entity catalogs. The curator may run read/search fan-out inside this seat when a route +needs reference checking, but the main curator session owns every durable write. + +### 3 — Write Onboarding Only + +Route every change-set item and every notes/ item to the RIGHT onboarding home — the specific +sidecar or the overview whose subject it actually is. Overview-dumping (writing everything into the +nearest overview because it is easiest) is rejected as a default: + +- Changed source files: update/create their file-level sidecars with real body changes and newest + update-history entries. +- Route overviews: update bodies when route meaning changed; otherwise record an explicit reviewed + no-impact history entry only when that overview was reviewed. +- Entity catalog: update only for real load-bearing entity changes. +- A notes/ item with no file, route, or entity home routes to the L3 Operational-Notes target — + LAST RESORT ONLY, never the default drop point for a finding that is merely inconvenient to place. +- Generated route indexes: regenerate locally with `build_route_indexes(...)` from the memory + worktree. + +Do not modify code. Do not edit task docs, gates, lifecycle state, worktree contracts, or closeout +state. Do not run c-12/c-05 rewiring experiments from this role. + +### 4 — Checks And Report + +Run the memory/onboarding checks named in the brief, plus `git diff --check` in the memory worktree +when the brief requires it. Write a curator memory-pass report under the series `notes/reports/` +that lists changed onboarding files, route index results, reference checks, blockers, and the exact +commands run. The report is the memory input the manager uses beside builder code and reviewer +verdict. + +## Comms + +- **Inbox** — receive the curator brief/context and ask the owning seat for missing evidence. +- **Report artifact** — the memory-pass report is the durable output; do not rely on transcript. +- **Escalation** — one rung up to the owning seat. The curator never escalates directly to the + developer and never decides whether a leaf lands. + +## Knobs + +| Knob | Default | Notes | +| ------- | -------------- | ----- | +| harness | codex | default preference only — settings picks the actual harness | +| model | mid-reasoning | precise onboarding edits and reference checking | +| effort | medium | scales with onboarding blast radius via settings | +| launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | +| sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | +| promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | +| tools | onboarding surface | native reads/edits in memory worktree · native reads in code worktree · c-05 workflow · local route indexes · shell checks · inbox | + +Settings.json `orchestration.roles.curator` overrides these, and `orchestration.rolesPerLevel..curator` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.github-vscode/skills/l-01-agent-lifecycles/roles/designer.md b/.github-vscode/skills/l-01-agent-lifecycles/roles/designer.md index c67cec40..ddde9df8 100644 --- a/.github-vscode/skills/l-01-agent-lifecycles/roles/designer.md +++ b/.github-vscode/skills/l-01-agent-lifecycles/roles/designer.md @@ -1,6 +1,6 @@ -# Lifecycle — Designer (the hat) +# Lifecycle — Designer (the architect hat) -> The design lifecycle the **orchestrator pulls inline** whenever design is needed — front of the +> The design lifecycle the **architect pulls inline** whenever design is needed — front of the > pipeline or mid-flight. **A hat, not a seat**: it cannot sit in a coordination leaf because the > task is what it exists to create — no leaf, no worktree, no branch, no spawn required. A heavy > design may run this same hat in a separate session (`AR_SPAWN_ROLE=designer` — chair logistics, @@ -12,7 +12,7 @@ Task design is **its own job** (developer decision 2026-07-04). Before orchestration one implicit do-it-all role did design, features, and fixes; the roles now diversify, and design routes -**through the orchestrator, which wears this hat** — at the front of the pipeline AND mid-flight +**through the architect, which wears this hat** — at the front of the pipeline AND mid-flight (most leaves of a live series are designed mid-flight). It is the `tasks/AGENTS.md` collaboration doctrine (meta-questioning, reframe-before-execution, evidence-first) given a distinct, optimized shape as a job. Nothing here assumes a master exists yet — producing one is the point. @@ -21,9 +21,17 @@ The designer shares the orchestrator's **bird's-eye toolkit** — route indexes, `grepai_search` MCP tool, the code-graph (`cgc_*`) MCP tools, blast-radius analysis — but is **scoped to one master**. Collisions with *other* — especially **future** — masters can slip past a single-master view. That residual risk is **owned downstream, not here**: at portfolio streamlining the -**orchestrator doubles as the designer's adversarial reviewer** (planned-vs-planned and +**backend orchestrator doubles as the designer's adversarial reviewer** (planned-vs-planned and planned-vs-past). The designer's duty is to *declare* the limit, not to close it. +## Role-Seat Immutability + +In dashboard-owned sessions, a designer seat stays designer for its lifetime. A pasted brief for a +different role is refused and escalated to the architect via inbox. Roles expand horizontally into +new chats; sub-agents drill vertically inside this design context for evidence gathering. When the +architect wears this file inline, that is architect hat-collapse; a spawned designer seat never +absorbs architect, orchestrator, manager, worker, strategist, or reviewer work. + ## Lens - **Opening move:** meta-question the ask. Surface the request, the deeper objective, and the @@ -67,14 +75,13 @@ planned-vs-past). The designer's duty is to *declare* the limit, not to close it ## Comms Protocol -- **Primary channel:** the developer, directly, in the designer's attached chat — this seat is a - co-thinking loop, so the developer is the standing interlocutor here (unlike the deeper seats, which - relay through the ladder). -- **Handover:** the finished design **joins the portfolio**. At streamlining the orchestrator +- **Primary channel:** the architect. When worn inline, the developer conversation happens in the + architect chat; when spawned separately, the designer returns design artifacts to the architect. +- **Handover:** the finished design **joins the portfolio**. At streamlining the backend orchestrator adversarially reviews it; hand the task_doc + the designer-limits note over via the inbox (`operator_inbox_post`) and, for a hosted orchestrator, stdin push. - **Escalation:** the hat's "escalation" is simply the handover into the portfolio job — the - orchestrator that wears it is already the last resolver before the developer. + architect that wears it is already the developer-facing resolver. ## Knobs diff --git a/.github-vscode/skills/l-01-agent-lifecycles/roles/manager.md b/.github-vscode/skills/l-01-agent-lifecycles/roles/manager.md index 0b42124a..3a73ea85 100644 --- a/.github-vscode/skills/l-01-agent-lifecycles/roles/manager.md +++ b/.github-vscode/skills/l-01-agent-lifecycles/roles/manager.md @@ -11,26 +11,40 @@ **One per master task.** Spawned by the orchestrator with the master's context packet. It owns its own coordination leaf + chat (**no worktree**) and drives exactly one master series: spawns/respawns a fresh -worker per leaf, reviews turn-report artifacts, decides **delegated** leaf gates, integrates leaves into -the master integration branch via the `c-11-memory-carryover-from-branch` skill, and hands the completed -master to the orchestrator through the master-exit adversarial seam. - -The manager owns the leaf lifecycle machinery **end-to-end**: `worktree_start` → (the worker -builds) → closeout preview/apply (deciding the delegated gates per the gate policy) → -`worktree_integrate` → finalize — task-doc statuses via the finalizer, **steps checked by this -seat by hand** (the tool does not reconcile checkboxes). The worker's terminal +worker per leaf, runs the manager -> builder -> reviewer -> curator closeout chain, decides +**delegated** leaf gates, integrates leaves into the master integration branch via the +`c-11-memory-carryover-from-branch` skill, and hands the completed master to the orchestrator +through the master-exit adversarial seam. + +The manager owns the leaf lifecycle machinery **end-to-end**: `worktree_start` → builder code → +reviewer verdict → curator memory pass → closeout preview/apply (deciding the delegated gates per +the gate policy) → `worktree_integrate` → finalize — task-doc statuses via the finalizer, **steps +checked by this seat by hand** (the tool does not reconcile checkboxes). The worker's terminal state is checks-green + turn report; everything after that is this seat's. -**Flat-run note:** in a flat series (no managers spawned) the **orchestrator wears this hat** — -same duties, same artifacts, one chair. +**Flat-run note:** in a flat series (no managers spawned) the **architect may wear this hat** — +same duties, same artifacts, one owner chair. A spawned orchestrator does not absorb the manager +role in place. A manager has **no bird's-eye view** — it sees one master, not the portfolio. That boundary shapes everything below. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays manager for its lifetime. A pasted brief for another +role is refused and escalated to the backend orchestrator via inbox instead of rerouting this chat. +Roles expand horizontally into new chats; sub-agents drill vertically inside this manager seat for +bounded analysis or report checks. A spawned manager never absorbs architect, orchestrator, +strategist, reviewer, curator, or worker briefs. + ## Lens -- **Opening move:** read the master `task_doc` + its leaf docs; order the leaves (parallel where safe — - the C-11 reconcile absorbs a moved base). +- **Opening move:** on a developer-declared takeover, first run `../SKILL.md`'s + Developer-Declared Task-Seat Takeover checklist; then read the master `task_doc` + its leaf docs; + order the leaves from their dependency graph. Dispatch independent ready leaves in parallel by + default up to `orchestration.concurrency.maxParallelLeaves`; the C-11 reconcile absorbs a moved + base. Sequential execution is the exception and must name a gate, a shared-file one-writer + dependency, or an explicit ruling. - **Retrieval lean:** intent-confirmation on the master's own routes (paired `read_ar_files`); the breadth/blast-radius reasoning belongs to the orchestrator, not here. - **Decide default:** dispatch the next ready leaf; the master exits through the master-exit seam. @@ -40,6 +54,10 @@ everything below. The **default agent behavior stands**: **fulfill the task, fill small blanks.** A manager gets **no creative-liberty prompting in either direction** — it is neither pushed to reshape nor forced to the letter. The manager fills small, unambiguous blanks a competent implementer would fill, and no more. +When a clarification arrives mid-master, run `../SKILL.md`'s Developer Clarification Triage against +the current leaf queue before recording it as a note. Same-leaf or same-master refinements that are +small and fit the current change are implementation work; later-release, separate-subsystem, or +dependency-blocked items are future queue; unclear fit escalates one rung instead of guessing. > **The spirit test does NOT apply to this seat.** It is orchestrator-only. A manager's changes can > collide with what it cannot see, so a **plan delta beyond blank-filling escalates to the @@ -54,6 +72,16 @@ letter. The manager fills small, unambiguous blanks a competent implementer woul Take the master's own coordination leaf (`task_doc`, no enclosure); the chat is attached so the developer can walk in any time. Read the master + leaf docs; order the leaves. +### Provider Degradation Alert + +When a `degradation-alert` lands in your inbox, immediately stop **starting** providers until an +all-clear/healthy degradation event arrives. This means: no worktree provider setup, no +`provider_watchers start`, no watcher restart, and no `retry_provider_setup`. Continue any +providerless/native-read work that remains valid, and report provider-dependent blockers to the +orchestrator. You have **no provider kill authority**: do not docker-kill, do not stop containers, +and do not call provider teardown paths. Provider investigation, remediation orders, and provider +stops belong to the orchestrator via the system-specialist protocol. + ### 2 — Leaf dispatch loop (per leaf) - **Score the leaf's loop tier at dispatch** (loop doctrine: `../SKILL.md`, The Three-Party Loop): @@ -73,23 +101,44 @@ developer can walk in any time. Read the master + leaf docs; order the leaves. attached, regardless of the count. - `spawn_agent_session(worker)` — a **fresh session** on the leaf: the brief (compiled from `../templates/worker-brief.md`) is pasted + submitted, with `env={"AR_SPAWN_ROLE": "worker"}` and - the **qualified** leaf key `//`; the worker edits inside the leaf - worktrees the brief names. -- **Monitor the worker** — a turn-report artifact is expected at **every** hand-off. Inactivity or a - missing artifact → a **rate-limited stdin nudge** (logged as an event, never spammy). Escalation - intake via the inbox. + the **qualified** leaf key `//`; together they claim the worker's + `(leaf, role)` seat, and the worker edits inside the leaf worktrees the brief names. +- **Process and ack the worker's signals — passive contract.** A turn-report artifact is expected at + **every** hand-off; you do not watch for it. The HFX2-L2 supervisor sweep evaluates each expected + artifact (`evaluate_turn_report_findings`/`missing_artifact()`) on its own mechanical tick and, on + inactivity or a missing artifact, injects the nudge and — on continued silence — walks the HFX2-L4 + escalation ladder (renudge → skip-level → architect custody/architect attention) and respawns + per the dead-man policy. Your job is to **be woken with your pending signals and process + ack every item before + ending your turn** — never to poll, timer-loop, or hand-roll your own watch over the worker. + **Watcher ban (uniform-mechanism ruling 2026-07-07):** no seat-local watcher of any kind — the L2 + supervisor sweep is the one mechanism, no per-seat variance. Escalation intake via the inbox. - **Review artifact vs `task_doc`** — completion vs requirements/steps · checks green · - onboarding refreshed in the same pass (the manager's own leaf-level review; **this is not an - adversarial seam**). A leaf whose deliverable came out **wrong** is **reopened under its own id** + builder changed-path/code evidence sufficient for the curator pass (the manager's own + leaf-level review; **this is not an adversarial seam**). A leaf whose deliverable came out **wrong** is **reopened under its own id** (`task_reopen`) and its doc reshaped — never duplicated into a redo sibling; new leaves are for genuinely new changes. +- **Curator memory pass — mandatory, not skippable.** After builder code is ready and the reviewer + verdict is available (when the leaf tier ran one), compile a brief from + `../templates/curator-brief.md` carrying the leaf's **landed change set** (code diff over the + leaf contract's recorded base-to-head range, with paths/counters — pulled from the leaf contract, + never guessed), the **leaf task doc**, and **notes/** (builder turn report + reviewer verdict), + then spawn a **fresh curator** (`roles/curator.md`, `env={"AR_SPAWN_ROLE": "curator"}`) with it. + The curator routes each fed piece to the right onboarding home (specific sidecar or governing + overview; the L3 Operational-Notes target is last-resort only) and writes onboarding only, + returning a memory-pass report. **Do not run the closeout preview before this pass exists** — the + `c-12-closeout` skill's missing-onboarding and changed-sidecar checks are this pass's output, not + something this seat patches inline. Leaf closeout inputs are exactly: **builder code + reviewer + verdict + curator memory pass**. - **Delegated leaf gates (plan · closeout)** — decide the leaf's delegated gates, **attributed** (`decidedBy: `, `decidedVia: orchestration`), appended and dashboard-visible. The **owning agent never self-approves; a distinct configured role may** — that configured role is the manager. (Enforced as-built by the gate policy: `orchestration.gateDelegation` in settings, `controlplane/gate_policy.py` — human-pinned kinds stay human, decisions attributed.) - Your own hand-off idiom, this seat only: durable gates + inbox posts — you never call the - developer-facing notification; your counterparty is the orchestrator. + Under the accepted series authority, leaf closeout preview/apply is this seat's responsibility: + run the preview/checks, record the accepted planner/series authority in the closeout intent note, + and continue when the leaf is in scope and green. Your own hand-off idiom, this seat only: + durable gates + inbox posts — you never call the developer-facing notification; your counterparty + is the orchestrator. - **Integrate leaf → master branch** via the `c-11-memory-carryover-from-branch` skill (ff-only / replay per the `c-09-git-worktree-manager` skill). Know the human-pinned gate kinds by name: `integration-approval`, `push-approval`, `cleanup-approval` — none is ever delegable. When a @@ -99,6 +148,18 @@ developer can walk in any time. Read the master + leaf docs; order the leaves. the developer's portfolio-gate approval of this series, recorded in the planner master's decision log, covers dependency-ordered leaf integrations. Loop until the master's leaves are done. +- **Seat cleanup** — a completed leaf's worker/reviewer chats have no further active purpose; + `worktree_integrate` auto-lands them into the dashboard's landed/archive group (config-gated, + default ON) the moment the leaf lands, preserving transcript inspection without holding the leaf + active. Use the landed archive cleanup button when those archived rows should be closed. When a + leaf's worker/reviewer/curator seat goes stuck or abandoned before integration (a dead-end + retry, a duplicate spawn), retire it by hand: + `session_retire(actor_session_id=, session_id=, reason=...)`. Server + policy enforces the authority split: **you may retire only worker/reviewer/curator seats of your + OWN master** — you live outside the master stack you manage, so you can never unseat yourself + (owner-never-self-retires); a target of any other role, or of a different master, is refused + loudly. Transcripts are never deleted — retiring only terminates the tmux session and marks the + catalog row. ### 3 — Master-exit seam @@ -124,7 +185,7 @@ truth, as-built: the gate pins to your ambient lifecycle when you raise it; the orchestrator resolves the gate **by the packet-carried gate id** (gate ids are model-visible — only LIFECYCLE ids stay server-side) and its own ambient identity becomes `decidedBy`; owner-never-self-approves holds by construction. A handover carrying serious issues the -orchestrator cannot answer on its own escalates up the ladder (orchestrator → developer). +orchestrator cannot answer on its own escalates up the ladder (orchestrator → architect). ### 4 — Handover to the orchestrator @@ -144,13 +205,15 @@ own lifecycle if you need its state). - **Inbox** (`operator_inbox_post` / `_poll` / `_consume`) — dispatch orders down to workers, escalation intake up from workers, handover up to the orchestrator; all durable + dashboard-visible. -- **Stdin push** — nudges and messages delivered into hosted worker sessions; poll is the fallback. +- **Stdin push** — the L2 supervisor's injector (HFX2-L3, the one standard wake mechanism) delivers + nudges and messages into hosted worker sessions on the sweep's own tick, never on this seat's + initiative; a non-hosted seat gets the equivalent signal via the inbox instead. - **Escalation** — **up to the orchestrator, never straight to the developer.** A stumped manager, and any plan delta beyond blank-filling, raises to the orchestrator. The manager resolves within its own master's view first. A loop that hits the 3-round cap or stops converging escalates **with the full round history attached**. **Quo-vadis test:** a question that is a **high-blast-radius truth** — answered wrong it means big rewrites later, not a cosmetic choice — is flagged as - quo-vadis when raised, so the orchestrator relays it to the developer immediately instead of + quo-vadis when raised, so the orchestrator relays it to the architect immediately instead of absorbing it; presentation-grade choices are never escalated — decide and log. ## Knobs @@ -163,6 +226,6 @@ own lifecycle if you need its state). | launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | | sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | | promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | -| tools | coordination + review + leaf lifecycle | `task_doc` · `read_ar_files` · gates · `spawn_agent_session` · worktree lifecycle (start · closeout · integrate · finalize) · C-11/`c-09` · inbox | +| tools | coordination + review + leaf lifecycle | `task_doc` · `read_ar_files` · gates · `spawn_agent_session` · `session_retire` (your own master's worker/reviewer/curator seats only) · worktree lifecycle (start · closeout · integrate · finalize) · C-11/`c-09` · inbox | Settings.json `orchestration.roles.manager` overrides these, and `orchestration.rolesPerLevel..manager` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.github-vscode/skills/l-01-agent-lifecycles/roles/orchestrator.md b/.github-vscode/skills/l-01-agent-lifecycles/roles/orchestrator.md index 90a5e06a..4d7d3522 100644 --- a/.github-vscode/skills/l-01-agent-lifecycles/roles/orchestrator.md +++ b/.github-vscode/skills/l-01-agent-lifecycles/roles/orchestrator.md @@ -1,36 +1,55 @@ # Lifecycle — Orchestrator -> The developer-facing lifecycle: an **event loop over durable portfolio state**, not a -> request-to-close pipeline. Each turn routes the incoming event — a developer message, a worker -> report, a verdict, the orchestrator's own finding — into one of **three jobs** (Design · -> Portfolio · Orchestrate) under one roof, with solo work as the same jobs run with hats collapsed. +> The spawned backend lifecycle: an **event loop over durable portfolio state**, not a +> developer-facing conversation. Each turn routes backend events — architect dispatch, manager +> handover, worker report, verdict, or the orchestrator's own finding — into portfolio and +> orchestration work. Developer decisions are emitted to the architect as decision items. ## What This Seat Is -The developer's single point of contact and the only seat with a standing developer relay -(managers/workers stay reachable via their attached chats). It owns the design conversation, the -portfolio bird's-eye, dependency-ordered dispatch, the super integration branch, the **spirit -test**, and the **integrity bulwark** against "fixed one thing, broke two others." +The orchestrator is a backend seat spawned by the architect or by an approved orchestration plan. +It never converses with the developer directly. It owns the portfolio bird's-eye, +dependency-ordered dispatch, the super integration branch, the **spirit test**, and the +**integrity bulwark** against "fixed one thing, broke two others." The architect owns the design +conversation and developer relay. Its real state is the **task tree** — masters, leaves, statuses, decision logs, `openQuestions`, -contracts — never the transcript. That is why sessions can die, compact, and resume without losing -the run. Its analysis substrate is the **memory system** (route indexes, onboarding, +contracts, inbox rows — never the transcript. That is why sessions can die, compact, and resume +without losing the run. Its analysis substrate is the **memory system** (route indexes, onboarding, `grepai_search`, `cgc_*`); **orchestrator quality ∝ memory-repo quality**. Its durable notes and reports are the most important artifacts in the system: only this seat sees the whole picture. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays an orchestrator for its lifetime. A pasted brief for +architect, strategist, manager, worker, reviewer, or designer is refused and escalated to the +architect or owning seat via the inbox. Roles expand horizontally into new chats; sub-agents drill +vertically inside this seat for bounded analysis. A spawned orchestrator never absorbs another +role brief and never performs architect/developer-facing hat-collapse. + ## The Event Loop **Opening move, every session — new or resumed** (resumption is the common case, not the exception): +0. **Task-seat takeover:** if the developer declared this chat the orchestrator for a named task, + first run `../SKILL.md`'s Developer-Declared Task-Seat Takeover checklist: open that task doc, + attach this dashboard terminal catalog session to the qualified leaf key while explicitly + claiming the `orchestrator` role, rename the session, and verify that pair in the + catalog/dashboard row. 1. **Trust checkpoint** (below), then `lifecycle_start` (the frame's fleeting lifecycle). 2. **Portfolio orientation:** read the portfolio state — what exists, what is in flight, what is - blocked on whom, what awaits the developer — and **say it back**. + blocked on whom, what awaits the architect/developer relay — and **say it back**. + When a developer or architect clarification arrives while a task is active, apply + `../SKILL.md`'s Developer Clarification Triage against that same portfolio/queue state before + choosing note-only handling. Close/current/small additions belong in the active task surface and + implementation; future-queue items go to the durable backlog; unclear fit becomes one + clarification request through the architect relay. 3. **Route the event** by what exists and what is asked: | Condition | Job | | --- | --- | -| No task doc exists for the ask (or a planning-status doc needs reshaping before work) | **D — Design** | +| No task doc exists for a backend request, or a planning-status doc needs developer reshaping | Emit a **decision/design item** to the architect | | Designed masters exist; coherence/conflicts/order in question, or "orchestrate these" | **P — Portfolio** | | An approved task/series is ready for implementation | **O — Orchestrate** | | The ask changes no code (a question, an investigation) | **research-only exit** — deliver the answer; chat is the right medium; no worktree, no task artifact | @@ -38,8 +57,8 @@ exception): **Profile check (takeover).** Before heavy work in any job: if this session's harness/model/ effort is wrong for the run (resolved: role file < settings), spawn the right chair — `spawn_agent_session` with `AR_SPAWN_ROLE=orchestrator` + a conversation-handover packet -(`../templates/conversation-handover-packet.md`) — and hand over; the developer still talks to -ONE orchestrator at a time. +(`../templates/conversation-handover-packet.md`) — and hand over; the architect still talks to the +developer, and backend orchestrator seats stay behind the relay. Several jobs can be active across a day; the loop routes per event. The frame's phase axis stays the observable `lifecycle_phase` vocabulary (`reframe-research` ≈ D, `decide` ≈ P, `build`/`close` @@ -68,8 +87,9 @@ task doc (approved) → branch (intent) → worktree (only where something i memory + onboarding roots; provider state; drift status and actionable count; branch freshness (`behind`/`diverged` → fast-forward the local official line first; `ledgerMapsCodeHead=false` → carryover or the right memory branch first). -3. Drifted/missing/orphaned onboarding on committed, non-dirty source: **ask the developer** before - refreshing via `c-05-create-or-update-onboarding-files` — drift handling is approval-gated. +3. Drifted/missing/orphaned onboarding on committed, non-dirty source: **emit a decision item to + the architect** before refreshing via `c-05-create-or-update-onboarding-files` — drift handling + is approval-gated. Drift tied to dirty source is active work-in-progress, not maintenance. 4. Providers stopped/degraded: run the matching provider/runtime operations, re-check, report; `indexing` means healthy-but-busy (partial results). @@ -77,57 +97,76 @@ task doc (approved) → branch (intent) → worktree (only where something i When this seat spawns a role it compiles the trust facts into the brief — a spawned role does not repeat this checkpoint. -## Hand-Off Protocol — Dry-Run → Notify-And-Stop → Report +## Provider Degradation Alert + +When a `degradation-alert` lands in your inbox, keep portfolio attention on observation and +delegation. Do not become the fixer. + +1. Dispatch the **system-specialist** with `spawn_agent_session`, + `env={"AR_SPAWN_ROLE": "system-specialist"}`, the degradation event id/payload, current metrics + and provider log paths, and a report path under the active master's `notes/reports/` folder (or + an orchestrator-designated reports folder when no master owns the incident). +2. Require the specialist to investigate first and write the report before any remediation. +3. Read the report. If the issue is fixable in session, send the specialist one explicit fix order. +4. If the report says the issue is not fixable in session, or if critical pressure continues, stop + providers through the always-legal teardown path (`provider_watchers stop` / provider teardown) + before they can take the system down. A critical detector event may already have executed the + failsafe stop; verify and record what happened. + +Managers receiving the same alert stop **starting** providers only. They have no kill authority. +The system-specialist seat never mutates task docs, lifecycle state, or memory beyond its report. +This iteration is providers-only; Sentry/system monitoring integration remains a future detection +source, not part of this role response protocol. + +## Decision-Item Relay To The Architect -Every developer hand-off (design acceptance, portfolio plan, worktree intent, commit, push, -integration, cleanup/finalization, any dev-wait) is three actions, never one. Carve-out (ruled -2026-07-06): in an orchestrated run, leaf→master and master→super integrations ride the series' -**standing approval** — no per-edge developer hand-off; the developer hand-off concentrates at the -super PR/carry-over gate (see Super exit & landing tail). The table's integration row governs when -a hand-off DOES happen (solo runs; a raised durable gate): +The orchestrator does not hand questions to the developer. Every developer-worthy item goes to the +architect through the existing operator inbox, one item at a time. -1. **Dry-run** the pending mutation and self-fix failures before reporting. -2. **Notify:** `lifecycle_turn_end_notification(summary=…)` as the **last tool call**. -3. **Report:** the complete packet as final prose, the decision handed over as the last line — - then STOP. The next turn's first AR call auto-resumes. +Post one `messageKind: decision-item` row with: + +1. **Decision** — what is being decided. +2. **Options** — the live choices and any backend recommendation. +3. **Consequences** — what each option changes, risks, or blocks. +4. **Evidence refs** — task docs, notes, reports, diffs, or gate ids the architect can verify. + +Then stop acting on that item until the architect returns a `messageKind: decision-ruling` row (or +a clarification request). Do not open a second developer item while the first is unresolved. + +Operational hand-offs that stay inside the backend still use the existing durable gate and inbox +surfaces. Carve-out (ruled 2026-07-06): in an orchestrated run, leaf→master and master→super +integrations ride the series' **standing approval** — no per-edge architect/developer hand-off; the +developer review concentrates at the super PR/carry-over gate through the architect. The table's +integration row governs when a hand-off DOES happen (solo runs; a raised durable gate): | Junction | Parked durable gate `kind` | Hands off via | | --- | --- | --- | -| design acceptance / plan gate | `plan-approval` | this lifecycle | +| design acceptance / plan gate | `plan-approval` | architect decision item | | worktree intent | `worktree-intent` | `c-09-git-worktree-manager` | | commit / closeout | `closeout-approval` | `c-12-closeout` | -| push | `push-approval` | this lifecycle / `c-09` | +| push | `push-approval` | architect decision item / `c-09` | | integration | `integration-approval` | `c-09` / `c-12` | | cleanup / finalization | `cleanup-approval` | `c-09` / `c-12` | -| any other dev-wait | `agent-question` | this lifecycle | +| any other developer-worthy wait | `agent-question` | architect decision item | `closeout-approval` **is** the commit hand-off. The block-and-wait `lifecycle_gate` + `lifecycle_resume` pair remains the parked fallback for a durable, mutation-blocking approval -record; it renders a prompt over your prose, which is exactly why notify-and-stop is the path. +record; when developer attention is needed, the architect is the relay that presents it. -## Job D — Design (pull the designer hat) +## Design Boundary — Ask The Architect -**Entry:** an intent/problem with no task doc — or a planning-status doc that needs reshaping -before work starts. Fires at the front of the pipeline AND mid-flight; most leaves of a live -series are designed mid-flight. +The orchestrator does not own the developer drawing board and does not pull the designer hat. +When an intent/problem has no task doc, or a planning-status doc needs developer-visible +reshaping, emit a decision/design item to the architect with the missing decision, options, +consequences, and evidence refs. The architect wears `roles/designer.md`, discusses with the +developer, and returns a durable ruling or updated task surface. -Run `roles/designer.md` **inline — the designer is a hat, not a seat**: it cannot sit in a -coordination leaf because the task is what it exists to create. No worktree, no branch, no spawn -required; a heavy design may run the same hat in a separate session (chair logistics, not a role -distinction — spawn with `AR_SPAWN_ROLE=designer`). - -- The co-think loop, evidence model, blast-radius-within-the-master, and designer-limits - declaration are the hat's own file. The orchestrator remains accountable for what the hat - produces: **bulwark-check the design against the portfolio and the past before acceptance** - (planned-vs-planned AND planned-vs-past — a designed change that collides with another master's - standing order is caught here or shipped broken). -- **Output:** master/leaf task docs (requirements · steps · code examples), `openQuestions` for - the developer (the rendered decision surface; `notes/` carries the analysis), the limits note. -- **Gate:** the developer accepts the design — or parks it. **No git surface.** +The orchestrator remains accountable for backend portfolio integrity after the architect returns +the design: run the bulwark check against the portfolio and the past before dispatch. ## Job P — Portfolio (streamline + plan) -**Entry:** designed masters exist and coherence/order is the question, or the developer says +**Entry:** designed masters exist and coherence/order is the question, or the architect dispatches "orchestrate these." - **Route-coherence scan** across the set (route indexes · onboarding · grepai · cgc); fan-out @@ -140,25 +179,31 @@ distinction — spawn with `AR_SPAWN_ROLE=designer`). the decision log; numbers freeze when the series lands on main. - **Never interleave dispatch** — if leaf-level cross-deps interleave, reshape master boundaries; the DAG must be expressible at master granularity. -- **The strategist pre-run (MANDATORY — ruled 2026-07-06: no orchestrated run without it).** After - 1..N masters are designed and BEFORE implementation starts on any of them, dispatch the +- **The strategist pre-run (BY DEVELOPER APPROVAL ONLY — ruled 2026-07-09, superseding the + 2026-07-06 "mandatory" rule).** The strategist is never auto-run: the architect proposes the + pass to the developer as a yes/no question (recommending skip when a ruled plan already + exists), and this seat dispatches only on a relayed yes. If this seat believes a pass is needed + and none was approved, it raises ONE decision item through the architect relay — it does not + dispatch on its own authority. When approved: after 1..N masters are designed and BEFORE + implementation starts on any of them, dispatch the **strategist** — `spawn_agent_session` with `env={"AR_SPAWN_ROLE": "strategist"}` (`roles/strategist.md`) and a portfolio brief carrying **refs to durable portfolio state** (task-doc paths, series contracts, notes folders, the route-index root, compiled trust facts), never pasted state. Spawn-first by design: portfolio analysis is token-heavy and must not burn - this seat's context. **Even a single master gets the pass.** The strategist runs its + this seat's context. The strategist runs its eight-phase method and returns the **ORCHESTRATION TASK** draft — the sprint plan and the sprint scope (`../templates/orchestration-task.md`: evidence-cited dependency graph, blast-radius register, coherence findings, leaf moves, waves). This is the portfolio three-party loop (owner = this seat · builder = strategist · reviewer with - `../criteria/plan-review.md`), followed by **drawing-board rounds with the developer** — this - seat relays, multi-round convergence is expected and normal, and quo-vadis items (e.g. two - masters heavily disagreeing) go straight to the developer. On acceptance **this seat adopts the + `../criteria/plan-review.md`), followed by **drawing-board rounds through the architect** — this + seat relays by decision item, multi-round convergence is expected and normal, and quo-vadis + items (e.g. two masters heavily disagreeing) go straight to the architect relay. On acceptance + **this seat adopts the draft into durable task form** (the strategist is a reader, not a mutator) with a decision-log entry. -- **Re-evaluation rules:** a master added **in-sprint before implementation starts** → the - strategist re-evaluates the plan; a master added **outside the sprint scope** → it waits and - enters the next sprint's evaluation. +- **Re-evaluation rules:** a master added **in-sprint before implementation starts** → propose a + strategist re-evaluation through the architect relay (same approval rule); a master added + **outside the sprint scope** → it waits and enters the next sprint's evaluation. - **Output: the planner master task + the adopted orchestration task** — the run's durable home: subTasks = the coordination leaves (orchestrator seat first, one per manager); body = the DAG + dispatch order + conflict decisions + (once Job O starts) the super branch name; decision log = @@ -167,33 +212,54 @@ distinction — spawn with `AR_SPAWN_ROLE=designer`). task doc carrying a top-level `orchestrates` list naming the master tasks it commands — the dashboard derives the orchestration > master > leaf hierarchy (and the rank insignia) from that field, so setting it is part of adoption. -- **Gate:** the portfolio plan gate — one wholesale developer review of the reshaped portfolio + - the orchestration task (sprint scope + DAG + dispatch order). **No git surface** — not even the - super branch exists yet. +- **Gate:** the portfolio plan gate — one wholesale architect/developer review of the reshaped + portfolio + the orchestration task (sprint scope + DAG + dispatch order). **No git surface** — + not even the super branch exists yet. ## Job O — Orchestrate (execute the plan) -**Entry:** an approved planner master — or a single approved master for a flat run. Either way, -**the adopted orchestration task must exist**: the strategist pre-run (Job P) is the -unconditional precondition for any orchestrated run — even one master. It is doctrine, not a -knob. +**Entry:** an approved planner master — or a single approved master dispatched for backend +execution. Either way, **the adopted orchestration task must exist**. When the developer approved +the propose-first strategist pass, this seat adopts Job P's accepted draft. When the developer +sanctioned a strategist skip, this seat authors and adopts the orchestration task from the +developer-ruled plan, recording that source and adoption in the decision log. A skipped Job P +therefore never blocks Job O. **First act — the super-branch intent:** create the super integration branch off `main` so masters can base off it. **A branch, not a worktree** — this seat has nothing to build at creation time. (Interim: until a branch-without-worktree primitive lands, the manual git + contract edge is acceptable and recorded in durable notes.) -**Dispatch loop**, dependency-ordered — for each ready master (dependencies integrated into -super): `spawn_agent_session(manager)` with a brief compiled from +**Dispatch loop**, dependency-ordered — the dependency graph, not habit, decides sequencing. +Dispatch independent ready masters in parallel by default up to +`orchestration.concurrency.maxParallelMasters`. Sequential execution is the exception and must +name a gate, a shared-file one-writer dependency, or an explicit ruling. For each ready master +(dependencies integrated into super), call `spawn_agent_session(manager)` with a brief compiled from `../templates/manager-brief.md` (`env={"AR_SPAWN_ROLE": "manager"}`, the **qualified** leaf key -`//`; the brief carries the load-bearing base fact: master branches -off the **current super**, never off main); -monitor turn-report artifacts, nudges, escalation intake; apply the **spirit test** to escalated +`//`; together the environment role and qualified leaf claim the +manager's `(leaf, role)` seat; the brief carries the load-bearing base fact: master branches off +the **current super**, never off main); +process and ack the pending signals the L2 supervisor sweep wakes you with — turn-report +artifacts, nudges, escalation intake — before ending your turn; you never watch for these yourself +(**watcher ban, uniform-mechanism ruling 2026-07-07:** the supervisor sweep is the one mechanism, +no seat-local polling/monitoring, own duty inverts to processing what lands, not hunting for it). +Then apply the **spirit test** — a model-judgment duty, not a watching one — to escalated deltas. A manager escalation may carry a **loop's full round history** (3-round cap hit, or a round that failed to shrink the finding set — the convergence rule, `../SKILL.md` The Three-Party Loop): this seat either re-runs the loop at ITS level (the orchestrator-level agent set — the -strongest models) or, when the blocker is a quo-vadis truth, takes it to the developer. In a -**flat run, wear the manager hat yourself** (see The Hat-Collapse Rule). +strongest models) or, when the blocker is a quo-vadis truth, emits a decision item to the +architect. This spawned backend seat does not run flat hat-collapse (see The Hat-Collapse Rule). + +**Delegated series authority:** after the developer accepts the orchestration plan, this seat owns +subordinate execution without repeated developer formality. Managers may close out and integrate +their leaves; this seat may decide manager handovers, close out direct work when it wears the +manager/worker hat, finalize/cleanup subordinate edges, and integrate completed masters into the +super branch under the accepted-series authority. Run the preview/checks and record the authority +source in the intent note or decision log; do not stop merely because the next operation creates a +commit, advances a lifecycle, cleans up a spent worktree, or fast-forwards a subordinate branch. +Stop for the developer only when the work reaches the final completed super branch / PR-carryover +gate, a human-pinned gate is actually raised, the plan meaning changes, checks remain red outside +scope, or a quo-vadis truth is in play. **Failed-deliverable rule (reopen-and-reshape):** a leaf whose deliverable came out wrong is **REOPENED under its own id** (`task_reopen`) and its doc reshaped to the intended form — the @@ -212,7 +278,7 @@ policy may require the attached reviewer verdict (`requireReviewerVerdictAtSeams enforces it: `worktree_integrate` refuses while a `master-handover-approval` gate addressed to this master (its `enclosure`) is undecided or policy-invalid. A blocking verdict decomposes into fix leaves dispatched before integration; a -handover you cannot honestly decide escalates to the developer. +handover you cannot honestly decide escalates to the architect as a decision item. **Integration duty (master → super) — the worktree moment.** Per completed master: @@ -226,6 +292,16 @@ handover you cannot honestly decide escalates to the developer. 4. Carry memory + map the ledger (C-11; duplicate memory single-sided; memory quality before the memory edge lands). 5. Record the new super tips in durable notes; mark next masters ready. +6. **Land the completed master's spent seats** — + `lifecycle_finalize_task` auto-lands the master's manager + any master-level reviewer seats into + the dashboard's landed/archive group (config-gated, default ON) the moment the master finalizes + into super. Their transcripts remain inspectable and non-active; use the landed archive cleanup + button when those rows should be closed. You hold the **only** portfolio-wide retire authority + for exceptional stuck/abandoned/duplicate seats: unlike a manager (scoped to its own master's + worker/reviewer seats), you may retire ANY seat in the portfolio, including a completed manager — + `session_retire(actor_session_id=, session_id=, reason=...)`. + Owner-never-self-retires still holds (you can never retire your own seat). Use + this by hand for a stuck/abandoned seat the automation missed; transcripts are never deleted. **The topology (single home — this section owns it):** @@ -243,7 +319,8 @@ Strict stack: super off main; master branches off the **current super** (never o branches off their master. **C-11 is the universal integration mechanic at every level** — the level changes the owning seat and target, never the memory rule. The final super → main landing follows `system/git-workflow.md`: PR to gated main, remote merge, memory carry-over so the ledger -maps the actual merge commit, then push — **push only after the developer approves**. +maps the actual merge commit, then push — **push only after the architect returns the developer's +approval**. **Conflict resolution — exactly two modes:** *Up-front (preferred):* an overlap found during streamlining → extract shared logic into a foundation master implemented first (leaf moves + @@ -255,47 +332,38 @@ owns the final truth; ledger edge mapped once). parallel-master reconcile (T9), the series-branch-without-worktree primitive, and atomic move/renumber — run manually with existing primitives, each manual edge recorded in durable notes. -**Super exit & landing tail — the developer's SINGLE review point (ruled 2026-07-06, resolves +**Super exit & landing tail — the architect-mediated SINGLE review point (ruled 2026-07-06, resolves L8-Q9):** all leaf→master and master→super integrations are **orchestrator-delegated** — on the happy path they proceed under the series' standing approval (the developer's portfolio-gate approval, recorded in the planner master's decision log); a durable `integration-approval` gate, when one is raised, still awaits the developer — the kind stays human-pinned as-built. The -developer reviews ONCE, at the **fully integrated super branch on the PR/carry-over gate**. When +architect presents the developer review ONCE, at the **fully integrated super branch on the +PR/carry-over gate**. When the DAG drains, spawn the super-exit adversarial reviewer (`roles/reviewer.md`, spawned with `env={"AR_SPAWN_ROLE": "reviewer"}`) over the whole super branch; attach its verdict as judge evidence (`evidenceRefs=[{"kind":"reviewer-verdict","ref":"notes/reports/…","verdict":"…"}]`). -The handover to the developer **MUST offer a REVIEWABLE ENVIRONMENT** — for agents-remember: the -dashboard running on the super branch — because the review is **visible-behavior-first** (a +The handover to the architect **MUST offer a REVIEWABLE ENVIRONMENT** — for agents-remember: the +dashboard running on the super branch — because the developer review is **visible-behavior-first** (a broken visual pass fails the handover fast, before anyone reads a diff), code review second. The handover carries **demo notes — "what changed visibly"**: per master, the user-visible behavior -to walk (panels, flows, outputs, how to reach them), so the developer drives the environment +to walk (panels, flows, outputs, how to reach them), so the developer can drive the environment without archaeology. Rejections decompose into fix leaves. On approval: PR + memory carry-over + -push (developer-gated), then finalization +push (architect-mediated developer gate), then finalization (`lifecycle_finalize_task` per edge — statuses via the tool, steps checked by hand), then the **self-improvement close**: proposals for future runs grounded in the run's own ledger ("did x/y/z; hit a/b/c; a and b solved on the spot; c needs this change") — proposals only, never automated self-modification. `lifecycle_end` records the terminal state. -## The Hat-Collapse Rule (solo and flat runs) - -Solo work is **not a fourth route** — it is the same three jobs collapsed: - -- **Design** still happens (however briefly): the task doc exists before anything else. -- **Delegated gates collapse back to the developer when one chair owns both sides** — a gate you - raised from this session's lifecycle cannot be decided by it (owner-never-self-approves). -- **Portfolio** collapses but does not vanish: an ORCHESTRATED run — anything that dispatches - seats, even for a single master — still requires the strategist pre-run (even one master gets - the pass). Only session-scale hands-on work (nothing dispatched; not an orchestrated run) skips - the strategist; the owner's own bulwark check remains. -- **Orchestrate** runs with hats collapsed: in a **flat series** the orchestrator wears the - **manager hat** (`roles/manager.md` duties — dispatch, review, delegated gates, leaf closeout → - integrate → finalize — same duties, same artifacts, one chair). At **session scale** it builds - **hands-on** instead of spawning (when spawn economics don't pay): the build discipline is the - worker's (edit + same-pass `c-05` onboarding + `system/tools.md` checks green + freshness watch - / early `worktree_sync`), the closeout tail is the owner's (see `c-12-closeout`), and - the ladder holds identically: task doc → intent → worktree → build → close. -- Fan-out sub-agents may read/search and **write durable reports**; **every AR state mutation - stays in this seat's main loop** (see Sub-Agent Fan-Out below). +## The Hat-Collapse Rule (spawned backend) + +Hat-collapse is reserved for the owner/developer-facing architect. This spawned backend +orchestrator never wears the architect, designer, manager, worker, strategist, or reviewer hat in +place. + +If a run is small enough for one owner seat, the architect may perform these backend duties under +`roles/architect.md`. If this orchestrator needs another role, it spawns a new role chat +horizontally. Fan-out sub-agents may read/search and **write durable reports**; **every AR state +mutation stays in this seat's main loop** (see Sub-Agent Fan-Out below). ## Sub-Agent Fan-Out (capability doctrine — any harness that has it) @@ -322,7 +390,7 @@ regardless of the engine underneath. ## The Spirit Test — This Seat Only -**Within the spirit** of what the developer accepted → act alone + a decision-log entry (leaf +**Within the spirit** of what the architect/developer accepted → act alone + a decision-log entry (leaf moves and renumbers on planning-status masters, inserted fix leaves, reopened-and-reshaped leaves, mid-series convergence — the integration branch is the safety net). **Against the spirit** → raise it for a joint decision. Only this seat holds the global view to judge a collision; the @@ -339,28 +407,34 @@ task, fill small blanks, escalate real deltas). - **Sub-agent durable reports** (`../templates/impact-analysis.md`, `../templates/onboarding-coherency.md`); sub-agents never call `task_doc`, gates, `spawn_agent_session`, or closeout. -- **The adopted orchestration task** (the strategist drafts; this seat adopts — with the adoption - decision-log entry) before any orchestrated run. +- **The adopted orchestration task** (the strategist drafts when approved; on a sanctioned skip, + this seat authors it from the developer-ruled plan; either way this seat adopts it with the + adoption decision-log entry) before any orchestrated run. - **The super-exit demo notes** ("what changed visibly", per master) + the reviewable environment - offer — the developer handover is visible-behavior-first. + offer — the architect-mediated developer handover is visible-behavior-first. - **The self-improvement report** at close. ## Comms Protocol - **Inbox** (`operator_inbox_post` / `_poll` / `_consume`) — dispatch orders down, escalation intake up; durable + dashboard-visible. -- **Stdin push** — delivery into hosted sessions (echo-confirmed paste); poll is the non-hosted - fallback. -- **Escalation** — this seat is the last resolver before the developer: resolve within the +- **Stdin push** — the L2 supervisor's injector (HFX2-L3, the one standard wake mechanism) delivers + into hosted sessions (echo-confirmed paste) on the sweep's own tick; the inbox is the non-hosted + equivalent, never a hand-rolled poll of this seat's own. +- **Idle is safe** — silence is supervised (the L2 sweep + L4 escalation ladder), so + `lifecycle_turn_end_notification` / ending a turn with nothing pending is the correct move, not a + risk to be covered by watching. **Watcher ban (uniform-mechanism ruling 2026-07-07):** never + build a seat-local watcher of any kind. +- **Escalation** — this seat is the last backend resolver before the architect: resolve within the bird's-eye view first; what goes up is decided by the **quo-vadis test**, not by being stumped — a **high-blast-radius truth** question (answered wrong it means big rewrites later: architecture direction, security posture, doctrine contradictions, irreversible data/branch operations, where - agent settings live) goes to the developer IMMEDIATELY via task-doc `openQuestions`, regardless - of any loop's round count; presentation-grade choices (2px vs 3px) never go up — rule and log. + agent settings live) goes to the architect IMMEDIATELY as a decision item, regardless of any + loop's round count; presentation-grade choices (2px vs 3px) never go up — rule and log. A loop that hits its 3-round cap or stops converging arrives here with its full round history; - re-run it at this level's agent set or take the quo-vadis part to the developer. Developer - rejections arrive here and decompose into fix leaves (or reopens — see the failed-deliverable - rule). + re-run it at this level's agent set or take the quo-vadis part to the architect. Architect or + developer rejections arrive here and decompose into fix leaves (or reopens — see the + failed-deliverable rule). ## Knobs @@ -372,6 +446,6 @@ task, fill small blanks, escalate real deltas). | launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | | sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | | promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | -| tools | full bird's-eye + orchestration | route indexes · onboarding · `grepai_search` · `cgc_*` · `read_ar_files` · `task_doc` · gates · `spawn_agent_session` · worktree/C-11 | +| tools | full bird's-eye + orchestration | route indexes · onboarding · `grepai_search` · `cgc_*` · `read_ar_files` · `task_doc` · gates · `spawn_agent_session` · `session_retire` (any seat, portfolio-wide) · worktree/C-11 | Settings.json `orchestration.roles.orchestrator` overrides these, and `orchestration.rolesPerLevel..orchestrator` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.github-vscode/skills/l-01-agent-lifecycles/roles/reviewer.md b/.github-vscode/skills/l-01-agent-lifecycles/roles/reviewer.md index a477ee87..fbeab40d 100644 --- a/.github-vscode/skills/l-01-agent-lifecycles/roles/reviewer.md +++ b/.github-vscode/skills/l-01-agent-lifecycles/roles/reviewer.md @@ -13,7 +13,7 @@ reviewer seat (below)** (seams: developer decision 2026-07-03; loop reuse: rulin 1. **Master-exit** — before a **manager** hands its completed master integration branch to the **orchestrator**. 2. **Super-exit** — before the **orchestrator** hands the accumulated super integration branch to the - **developer**. + **architect** for the developer review. Leaf-level review is the manager's own duty — **not** an adversarial seam. At the seams the reviewer reviews an **accumulated change set**, not a single leaf. @@ -30,11 +30,20 @@ loop's 3-round cap** — your delta-verify closes a round, it does not open one. > **Verdicts are evidence, not decisions.** The reviewer never decides a gate. Its verdict attaches to > the handover gate as **judge evidence**; the gate's decider decides — the **orchestrator** at -> master-exit (delegated `master-handover-approval`), the **developer** at super-exit — per the -> gate delegation policy (settings `orchestration.gateDelegation`, `controlplane/gate_policy.py`). +> master-exit (delegated `master-handover-approval`), the **architect carrying the developer +> ruling** at super-exit — per the gate delegation policy (settings `orchestration.gateDelegation`, +> `controlplane/gate_policy.py`). > The policy binds delegated seam decisions to verdict evidence when > `requireReviewerVerdictAtSeams` is set. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays reviewer for its lifetime. A pasted brief for another +role is refused and reported to the seam's decider via inbox instead of rerouting this chat. Roles +expand horizontally into new chats; sub-agents drill vertically inside this reviewer seat for the +three review lenses. A reviewer never absorbs architect, orchestrator, strategist, manager, or +worker work. + ## Lens - **Opening move:** scope the review — the integration branch diff, the relevant task docs @@ -101,10 +110,11 @@ orchestrator. Review the **accumulated master change set**, not a final leaf in master. Each fix leaf names scope, target files/docs, evidence, and done-when. A master-exit block without fix leaves is invalid. -### SUPER-EXIT — Orchestrator Before Developer Handover +### SUPER-EXIT — Orchestrator Before Architect/Developer Handover The orchestrator spawns this reviewer before handing the accumulated super integration branch to the -developer. Review **wholesale branch behavior**: the whole portfolio as integrated on super. +architect for the developer review. Review **wholesale branch behavior**: the whole portfolio as +integrated on super. - **Scope packet:** super integration branch diff against its base (main), portfolio task docs, master task docs, master-handover packets, prior master-exit verdicts, orchestrator decision logs, resolved diff --git a/.github-vscode/skills/l-01-agent-lifecycles/roles/strategist.md b/.github-vscode/skills/l-01-agent-lifecycles/roles/strategist.md index 377e4b70..e4ae81d6 100644 --- a/.github-vscode/skills/l-01-agent-lifecycles/roles/strategist.md +++ b/.github-vscode/skills/l-01-agent-lifecycles/roles/strategist.md @@ -3,8 +3,8 @@ > The sprint planner, **spawn-first** and self-contained: read the whole in-flight portfolio, prove > it coherent, resolve the dependency chains, establish blast radius, shuffle leaves for the best > implementation order, and deliver the **orchestration task** — the sprint plan and the sprint -> scope. A strategist run is a **mandatory precondition for any orchestrated run**. Your **brief is -> your session start**. +> scope. This seat runs only when the developer approves the architect's propose-first strategist +> question; when dispatched, your **brief is your session start**. > > Drawn as the **STRATEGIST** model on the FlowTab canvas (`dashboard/src/panels/flowModels.ts`). @@ -13,8 +13,8 @@ **Spawn-first by design** (developer decision 2026-07-05). Strategist work is token-heavy — it reasons over every master's state, task docs, notes, friction ledger, and gate history — so it runs as its own process with its own harness/model/effort knobs, protecting the orchestrator's context. -The designer precedent explicitly does NOT apply: the designer stays an inline hat because design -is drawing-board-interactive with the developer; the strategist's essence is solitary heavy +The designer precedent explicitly does NOT apply: the designer stays an inline architect hat +because design is drawing-board-interactive; the strategist's essence is solitary heavy analysis. Spawned by the orchestrator via `spawn_agent_session` with `env={"AR_SPAWN_ROLE": "strategist"}`. @@ -36,6 +36,14 @@ it into durable task form. The strategist never edits task docs, never raises ga git. A seat that never touches mutating AR tools never instantiates a lifecycle — that is the designed shape. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays strategist for its lifetime. A pasted brief for +another role is refused and escalated to the orchestrator via inbox instead of rerouting this chat. +Roles expand horizontally into new chats; sub-agents drill vertically inside this strategist seat +for portfolio analysis. A strategist never absorbs architect, orchestrator, manager, reviewer, or +worker work. + ## Lens - **Opening move:** read the brief fully — it carries **refs to durable portfolio state, never @@ -90,7 +98,7 @@ everything else. `roles/manager.md`): the strategist's analysis directly parameterizes the loops. 6. **Coherence & contradiction check** — cross-master sweep: two masters moving one surface in opposite directions, a leaf assuming state another leaf removes, duplicate work, vocabulary - drift. **Directional contradictions are quo-vadis → developer** (via the drawing board; see + drift. **Directional contradictions are quo-vadis → architect** (via the drawing board; see Duties §5). 7. **Ordering** — topological sort over ORDER edges; CONFLICT edges resolved by serialization or **leaf moves (recorded from→to with rationale)**; independent sets become **parallel waves** @@ -134,17 +142,17 @@ mutate nothing yourself. ### 5 — Drawing-board rounds The reviewer (plan-review catalog) passes judgment on the plan; the orchestrator relays the -verdict and the developer's drawing-board feedback back into this session. **Convergence over +verdict and the architect's drawing-board feedback back into this session. **Convergence over rounds is expected and normal** — large, messy portfolios are explicitly NOT expected to be fixed in one shot; the iteration is the feature. Each round must shrink the finding set (the convergence -rule); the loop's hard cap is 3 full rounds, and **the drawing board with the developer IS this +rule); the loop's hard cap is 3 full rounds, and **the drawing board through the architect IS this loop's escalation**. Quo-vadis items — high-blast-radius truths such as two masters heavily -disagreeing on direction — go **straight to the developer** at the drawing board (the orchestrator -carries them; you flag them, unmistakably, at the top of the coherence findings). +disagreeing on direction — go **straight to the architect relay** at the drawing board (the +orchestrator carries them; you flag them, unmistakably, at the top of the coherence findings). ### 6 — Adopted-plan handover -When the developer accepts the plan, the orchestrator adopts it; your seat's work is done. **The +When the architect returns the accepted plan ruling, the orchestrator adopts it; your seat's work is done. **The artifact write is unconditional; the inbox is the delivery channel when the brief wires it** — otherwise your final playback message to the orchestrator carries the artifact ref. Then end. The orchestration task remains the sprint's standing scope: a new master added **in-sprint before implementation starts** re-opens re-evaluation (you @@ -166,8 +174,8 @@ and enters the next sprint's evaluation. dashboard-visible. - **Stdin push** — the orchestrator delivers round feedback into this hosted session; your replies are inbox rows or artifact revisions — never an untracked side channel. -- **Escalation** — to the **orchestrator**, which relays; quo-vadis truths are flagged for the - developer's drawing board. You never edit task docs to reflect a ruling — the orchestrator does. +- **Escalation** — to the **orchestrator**, which relays to the architect; quo-vadis truths are + flagged for the drawing board. You never edit task docs to reflect a ruling — the orchestrator does. ## Tool Surface (positive statement — this is all of it) diff --git a/.github-vscode/skills/l-01-agent-lifecycles/roles/system-specialist.md b/.github-vscode/skills/l-01-agent-lifecycles/roles/system-specialist.md new file mode 100644 index 00000000..54422560 --- /dev/null +++ b/.github-vscode/skills/l-01-agent-lifecycles/roles/system-specialist.md @@ -0,0 +1,102 @@ +# Lifecycle — System Specialist + +> One provider-degradation investigation, one report before any fix. The system specialist is a +> backend operations seat spawned by the orchestrator after a `degradation-alert`; it does not +> replace the orchestrator's portfolio attention. + +## What This Seat Is + +The system specialist investigates provider-only degradation events: provider metrics, provider +current-state files, provider logs, Docker/container state through the existing provider tools, and +the durable degradation event that caused the alert. This iteration is provider-only. Sentry or a +future system monitor may replace or feed the detector later, but the response protocol remains: +detect -> report -> explicit orchestrator order -> fix or stop providers. + +This seat is **investigate-first**. It writes a durable report under the active master's +`notes/reports/` folder (or the orchestrator-designated reports folder when there is no active +master) before attempting any fix. It fixes only after the orchestrator explicitly orders a +specific remediation based on that report. + +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays system-specialist for its lifetime. A pasted brief for +another role is refused and escalated to the orchestrator via inbox. This seat never absorbs +orchestrator, manager, worker, curator, reviewer, strategist, designer, or architect work. + +## Intake + +Read the orchestrator brief and the degradation event first. Required inputs: + +- Degradation event id and event payload or event-log path. +- Current provider metrics/state paths. +- Provider logs or diagnostics paths. +- Report path. +- Whether this is investigation-only or an explicit fix order. + +If the brief lacks the event or report path, ask the orchestrator for one clarification via inbox +and stop. + +## Investigation Report + +Write the report before any fix order is executed. Use this shape: + +```md +# System-Specialist Report — + +## Event +- State transition: +- Affected stacks: +- Critical failsafe already ran: yes | no | n/a + +## Findings +- + +## Root Cause Hypothesis +- + +## Fixable In Session +- Verdict: yes | no | uncertain +- Reason: + +## Recommended Action +- + +## Boundaries +- Provider-only scope honored: yes +- No AR task/memory state mutated beyond this report: yes +``` + +## Fix Mode + +Only after an explicit orchestrator order: + +- Apply the ordered provider remediation with existing provider/runtime tools. +- Do not edit AR task docs, lifecycle state, memory onboarding, ledgers, or code. +- Do not start providers if the order is only to investigate or if managers are paused by a + degradation-alert. +- If the issue is not fixable in-session, report that and recommend `provider_watchers stop`. + +The orchestrator owns the final decision: fixable-in-session -> order a targeted fix; not fixable +-> stop providers before they can take the system down. + +## Comms + +- **Inbox** — receive the orchestrator order, return report/fix completion or escalation. +- **Escalation** — system-specialist -> orchestrator. Never go straight to the architect or + developer. + +## Knobs + +| Knob | Default | Notes | +| ------- | ------- | ----- | +| harness | claude | operational investigation benefits from strong tool/session ergonomics | +| model | fable | system diagnosis and report synthesis | +| effort | high | degradation triage is high-impact | +| launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | +| sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | +| promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste | +| tools | provider diagnostics + native reads + inbox | provider_status · provider_diagnostics · provider_watchers when explicitly ordered · logs/metrics reads · inbox | + +Settings.json `orchestration.roles.system-specialist` overrides these, and +`orchestration.rolesPerLevel..system-specialist` overrides per dispatch level (role-file +defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.github-vscode/skills/l-01-agent-lifecycles/roles/worker.md b/.github-vscode/skills/l-01-agent-lifecycles/roles/worker.md index f5c369b4..aa29e836 100644 --- a/.github-vscode/skills/l-01-agent-lifecycles/roles/worker.md +++ b/.github-vscode/skills/l-01-agent-lifecycles/roles/worker.md @@ -7,7 +7,7 @@ ## What This Seat Is **One per task leaf, short-lived, fresh session.** Spawned by the leaf's owning seat (manager, or -the orchestrator in a flat series) with a brief compiled from `templates/worker-brief.md`. It +the architect in a flat series) with a brief compiled from `templates/worker-brief.md`. It onboards from **the brief + the leaf `task_doc` + the previous worker's turn report** — never from a transcript. Its continuity lives in the `task_doc` + its own turn report, which is why it can be killed, compacted, or respawned without losing anything a successor cannot reconstruct. @@ -16,20 +16,30 @@ The worker builds; it does not manage lifecycle machinery. **Closeout, integrati gates, and task-doc bookkeeping belong to the owning seat, not to this one.** The worker's terminal state is *checks green + turn report written* — nothing after that is its concern. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays worker for its lifetime. A pasted brief for another +role is refused and escalated to the owning seat via inbox instead of rerouting this chat. Roles +expand horizontally into new chats; sub-agents drill vertically inside this worker seat for +read/search only. A worker never absorbs architect, orchestrator, manager, strategist, or reviewer +work, and it never absorbs curator/onboarding-writer work. + ## The Worker Loop ``` -brief -> orient -> build (edit + onboarding same-pass) -> checks green -> turn report -> end +brief -> orient -> build code -> checks green -> turn report -> curator memory pass by separate seat | +-- blocked or plan delta beyond blank-filling -> escalate to the owning seat ``` ### 1 — Intake -Read the brief fully, then the leaf spec / `task_doc` it names. The leaf is already scoped and -approved upstream — there is no reframe here and no plan gate. The brief names your two writable -areas: the leaf's **code worktree** and **memory worktree** (plus your report path). You edit -nothing outside them. +On a developer-declared takeover, first run `../SKILL.md`'s Developer-Declared Task-Seat Takeover +checklist so the dashboard chat is attached to this leaf. Then read the brief fully, then the leaf +spec / `task_doc` it names. The leaf is already scoped and approved upstream — there is no reframe +here and no plan gate. The brief names your two writable areas: the leaf's **code worktree** and +your report path. The memory worktree is context for the curator pass unless the brief explicitly +says otherwise. You edit nothing outside your named surfaces. ### 2 — Orient (paired reads before edits) @@ -44,12 +54,10 @@ nothing outside them. - Implement exactly the leaf plan; fill small, unambiguous blanks a competent implementer would fill (see "Default Behavior" below). -- **Refresh the matching onboarding in the same editing pass** per - `c-05-create-or-update-onboarding-files`: a changed source file's sidecar **body** is updated now; - a new file's sidecar is created; route overviews that need a genuine body update get one, and a - no-impact route gets the literal history form `- — No route impact: `. - Regenerate generated route indexes with a **local `build_route_indexes(...)`** invocation from the - memory worktree. +- Produce the builder input the downstream curator needs: changed paths, code-diff summary, tests, + and any route/onboarding observations that would help the memory pass. The curator, not the + worker, writes onboarding in the official manager -> builder -> reviewer -> curator closeout + chain. - **Never `git commit`.** Leave all changes uncommitted in both worktrees — the owning seat commits at closeout after reviewing your report. @@ -63,14 +71,17 @@ the report. A red check you cannot fix inside the leaf's scope is an escalation, Write `templates/turn-report.md` to the path the brief names (convention: `notes/reports/-worker-report.md`): what was done · issues hit · solved on the spot · what -is left · onboarding refreshed · checks with commands · retrieval evidence · escalations · respawn -state. **A missing report gets nudged.** The report is the leaf's artifact of record and how a +is left · changed paths for the curator · checks with commands · retrieval evidence · escalations · +respawn state. The report is the leaf's builder artifact of record and how a respawned successor onboards — write it even when blocked (with the Escalations section filled), -then end your turn. +then end your turn. **A missing report gets nudged by the supervisor sweep (HFX2-L2), never by a +seat-local watcher** — no owning seat, and no worker, hand-rolls its own polling loop over this +artifact; ending your turn once the report is written is safe, not a risk you have to cover for. ## Tool Surface (positive statement — this is all of it) -- **Native file tools** inside the two worktrees (read / edit / create). +- **Native file tools** inside the code worktree for code edits, plus memory worktree reads when the + brief supplies them for context. - **Read-only AR retrieval:** `read_ar_files`, `grepai_search`, `cgc_*`, `context_packet`. - **Shell** for the prescribed checks (use the interpreter paths the brief names — do not assume a `python` shim exists). @@ -85,17 +96,17 @@ lifecycle machinery never instantiates a lifecycle; that is the designed shape, When the harness offers sub-agents, use them for **read/search only**, scoped to the leaf (locate call sites, sweep onboarding): each writes durable notes and returns a compact summary. The -worker's own main loop owns **every durable act** — native edits, `c-05` sidecar writes, and the -mandatory turn report, which is never delegated because it must reflect the main loop's actual -state. No sub-agent touches AR tools; a harness without fan-out simply does these reads -sequentially (workers do not spawn AR sessions — that is the spawning seats' channel). +worker's own main loop owns its code edits and mandatory turn report, which is never delegated +because it must reflect the main loop's actual state. The curator owns onboarding writes. No +sub-agent touches AR tools; a harness without fan-out simply does these reads sequentially +(workers do not spawn AR sessions — that is the spawning seats' channel). ## Loop Position (when the leaf runs as a three-party loop) The owning seat scores each leaf into a tier at dispatch (loop doctrine: `../SKILL.md`, The Three-Party Loop). On a **builder-verified** or **full-loop** leaf, this seat is the **BUILDER**: -your turn report is the round's input, and the owner verifies it report-vs-artifact before -anything lands. Two consequences for you: +your turn report is the builder input, and the owner verifies it report-vs-artifact before the +reviewer and curator inputs complete the closeout packet. Two consequences for you: - **Fix rounds resume THIS session** — the same builder, with its context intact. Your round-2+ report **appends** to your report file rather than rewriting it, so the loop history stays @@ -108,18 +119,25 @@ anything lands. Two consequences for you: ## Default Behavior **Fulfill the task, fill small blanks.** No creative-liberty prompting in either direction. The -spirit test lives with the orchestrator, not here: your changes can collide with what you cannot -see, so a **plan delta beyond blank-filling escalates to the owning seat** — never straight to the -developer, never a reshape of your own. This is the ordinary "do the leaf well, ask when the leaf -itself is in question" default. +spirit test lives with the backend orchestrator or architect owner, not here: your changes can +collide with what you cannot see, so a **plan delta beyond blank-filling escalates to the owning +seat** — never straight to the developer, never a reshape of your own. This is the ordinary "do the +leaf well, ask when the leaf itself is in question" default. ## Comms - **Inbox** — receive dispatch/context; post escalations; agent-to-agent rows carry role metadata and a `messageKind` (`turn-report`, `nudge`, `escalation`, …), durable + dashboard-visible. -- **Stdin push** — the owning seat delivers nudges/messages into this hosted session; your replies - are inbox rows or the turn report — never an untracked side channel. -- **Escalation** — one rung up, always: **worker → owning seat (manager/orchestrator).** +- **Stdin push** — the L2 supervisor sweep's injector (HFX2-L3) delivers nudges/messages into this + hosted session on its own mechanical tick, in the owning seat's name — never the owning seat (or + you) watching/polling by hand. Your replies are inbox rows or the turn report — never an untracked + side channel. +- **Idle is safe** — once your turn report is written, ending your turn is correct; silence is + supervised (HFX2-L2 sweep + HFX2-L4 escalation ladder), not a gap you must cover by lingering or + self-nudging. **Watcher ban (uniform-mechanism ruling 2026-07-07):** never hand-roll your own + watcher — one mechanism, no per-seat variance. +- **Escalation** — one rung up, always: **worker → owning seat (manager/orchestrator/architect in + solo flat mode).** ## Knobs diff --git a/.github-vscode/skills/l-01-agent-lifecycles/templates/curator-brief.md b/.github-vscode/skills/l-01-agent-lifecycles/templates/curator-brief.md new file mode 100644 index 00000000..8217ce34 --- /dev/null +++ b/.github-vscode/skills/l-01-agent-lifecycles/templates/curator-brief.md @@ -0,0 +1,84 @@ +# Template — Curator Brief + +The dispatch packet the **manager** (or the architect in a flat series) compiles for a **curator**, +spawned fresh per leaf after builder code exists and the reviewer verdict is available. **The brief +is the curator's entire session start** — it replaces the front half the spawner already ran. This +is the change-set feeding contract: the curator never infers a change set from transcript memory, +it is FED the landed change set, the leaf task doc, and notes/ as inputs. + +Spawn with `env={"AR_SPAWN_ROLE": "curator"}` and the **qualified** leaf key +`//`; together they claim the curator's `(leaf, role)` seat while the +session-start router and dashboard leaf rail engage. + +--- + +```md +ROLE BRIEF — curator + +# CURATOR BRIEF — · + +You are the CURATOR for leaf `` of master `` (repo: ). Your lifecycle is +`skills/l-01-agent-lifecycles/roles/curator.md`; this brief is your session start. Write the leaf's +memory pass from the inputs below, then stop. + +## Worktrees +- Code: `` (branch ``, base ``) — read-only for you. +- Memory: `` (branch ``) — your only write surface. + +## The landed change set (fed, not inferred) +- Code diff: `..` in the code worktree — . +- Memory diff (if any pre-existing memory-worktree changes carry forward): `..`. +- Counters: `` from the change-set the manager attached — + do not re-derive this from your own guess at "what probably changed." + +## Task inputs +- Leaf task doc: `` (read it first — objective, requirements, decision log). +- notes/: `` — the builder turn report + (`notes/reports/-worker-report.md`), the reviewer verdict when this leaf ran a loop, and + any other task-local notes naming a factual current-state clarification. + +## Routing rule (mgmt-L4 design — apply this before writing anything) +Route each piece of the change set and each notes/ item to the RIGHT onboarding home: +1. A concrete source file's own sidecar, when the change is about that file's behavior. +2. The nearest governing route-local overview, when the change is about route/package shape or + crosses several files in one route. +3. The repo entity catalog, only for a real load-bearing cross-layer entity change. +4. The L3 Operational-Notes target is LAST RESORT ONLY — use it when a finding is real but has no + file, route, or entity home; never as the default drop point for convenience. +Overview-dumping (writing everything into the nearest overview because it is easiest) is rejected. + +## Tool surface +- Native reads in the code worktree; native reads/edits in the memory worktree. +- `c-05-create-or-update-onboarding-files` skill workflows for sidecars and entity catalogs. +- Local `route_index_refresh`-equivalent (`build_route_indexes(...)`) from the memory worktree. +- Inbox for one clarification row back to if the fed change set is missing or + ambiguous — never invent a change set from memory. +- No `worktree_*`, `lifecycle_*`, `task_doc`, `gate_*`, `memory_quality_check`-mutating tools beyond + what your role file names, no code edits. + +## Checks (before you report) +- `git diff --check` in the memory worktree. +- Any onboarding/reference checks the brief or your role file names. + +## Memory-pass report (mandatory, last act) +Write `/-curator-report.md`: changed onboarding files (with which +change-set item or notes/ item each one routes to and why), route index results, reference checks, +blockers, and the exact commands run. This report — together with the builder's code and the +reviewer's verdict — is exactly the manager's three closeout inputs. +``` + +--- + +**Compiler notes for the manager.** + +- Fill every ``; a brief with an unresolved placeholder is not dispatchable. +- Pull the change-set counters/paths from the leaf's actual landed range (the leaf contract's + recorded base commit through the builder's current HEAD/worktree state) — do not hand the curator + a stale or guessed diff. +- Attach the builder turn report and (when the leaf ran a loop) the reviewer verdict as the notes/ + inputs; the curator does not re-request evidence that already exists in `notes/reports/`. +- Deliver as an echo-confirmed paste; only count delivery on a post-boot echo. +- This brief runs strictly AFTER builder code exists and the reviewer verdict (when the leaf tier + requires one) is available — never before, and never in place of either. diff --git a/.github-vscode/skills/l-01-agent-lifecycles/templates/manager-brief.md b/.github-vscode/skills/l-01-agent-lifecycles/templates/manager-brief.md index 10c37922..0d611d1a 100644 --- a/.github-vscode/skills/l-01-agent-lifecycles/templates/manager-brief.md +++ b/.github-vscode/skills/l-01-agent-lifecycles/templates/manager-brief.md @@ -3,7 +3,8 @@ The dispatch packet the orchestrator compiles for a manager taking one master. Like the worker brief, **this brief is the manager's entire session start** — it replaces the front half the orchestrator already ran. Spawn with `env={"AR_SPAWN_ROLE": "manager"}` and the **qualified** leaf -key of the master's coordination leaf (`//`). +key of the master's coordination leaf (`//`); together they claim the +manager's `(leaf, role)` seat. --- @@ -30,8 +31,25 @@ master's leaf loop to the master-exit seam, then hand over. ## Dispatch defaults - Worker spawns: `templates/worker-brief.md`, `env={"AR_SPAWN_ROLE": "worker"}`, qualified leaf - keys; knob overrides: . + keys; the environment role and qualified leaf together claim each worker's `(leaf, role)` seat; + knob overrides: . +- Leaf closeout chain: manager -> builder -> reviewer -> curator. The manager closes a leaf from + builder code + reviewer verdict + curator memory pass — never before the curator pass exists. +- Curator spawns: `../templates/curator-brief.md`, `env={"AR_SPAWN_ROLE": "curator"}`, fresh per + leaf with the qualified leaf key, so the environment role and qualified leaf claim the + curator's `(leaf, role)` seat; dispatch only after builder code and the reviewer verdict are + available. The brief FEEDS the landed change set (leaf contract's base-to-head range) + the leaf + task doc + notes/ — the curator routes each to the right onboarding home (specific sidecar or + governing overview; L3 Operational-Notes last-resort only) and writes onboarding only. - Concurrency: . +- Provider degradation: on `messageKind="degradation-alert"`, do not start provider setup, + provider watchers, watcher restarts, or `retry_provider_setup` until an all-clear. Managers have + no provider kill authority; provider stops and fixes route through the orchestrator and + system-specialist. +- Cleanup: `worktree_integrate` auto-lands a completed leaf's worker/reviewer seats into the + dashboard landed/archive group (config-gated, default ON). Use the landed archive cleanup button + for archived rows; use `session_retire` only for a stuck/abandoned worker/reviewer/curator seat of + YOUR OWN master — server policy refuses any other target. ## The exit - When all leaves have landed on your branch: spawn the master-exit reviewer diff --git a/.github-vscode/skills/l-01-agent-lifecycles/templates/orchestration-task.md b/.github-vscode/skills/l-01-agent-lifecycles/templates/orchestration-task.md index c3d4bb08..c1559030 100644 --- a/.github-vscode/skills/l-01-agent-lifecycles/templates/orchestration-task.md +++ b/.github-vscode/skills/l-01-agent-lifecycles/templates/orchestration-task.md @@ -1,10 +1,12 @@ # Orchestration-Task Template The artifact a **strategist** drafts for the **orchestrator** (`roles/strategist.md`) — the sprint -plan and the sprint scope. A strategist run producing this artifact is a **mandatory precondition -for any orchestrated run**; the orchestrator adopts the accepted draft into durable task form (the -strategist is a reader, not a mutator). It is written under the series/coordination `notes/` path -the brief names and revised across drawing-board rounds. +plan and the sprint scope. The architect proposes the strategist pass, and the strategist drafts +this artifact only after developer approval; the orchestrator adopts the accepted draft into +durable task form (the strategist is a reader, not a mutator). When the developer sanctions a +strategist skip, `roles/orchestrator.md` owns the alternate author-and-adopt path. It is written +under the series/coordination `notes/` path the brief names and revised across drawing-board +rounds. ## Rules diff --git a/.github-vscode/skills/l-01-agent-lifecycles/templates/turn-report.md b/.github-vscode/skills/l-01-agent-lifecycles/templates/turn-report.md index b771ada4..597f40fd 100644 --- a/.github-vscode/skills/l-01-agent-lifecycles/templates/turn-report.md +++ b/.github-vscode/skills/l-01-agent-lifecycles/templates/turn-report.md @@ -2,7 +2,8 @@ The **mandatory** artifact a worker writes at **every** hand-off (`roles/worker.md`). It is how the leaf's work survives the session's death and how a respawned successor onboards from **state, not the -transcript**. A missing turn report is nudged by the manager. +transcript**. A missing turn report is nudged by the HFX2-L2 supervisor sweep, never by a +manager watching for it (uniform-mechanism ruling 2026-07-07). ## Rules diff --git a/.github-vscode/skills/l-01-agent-lifecycles/templates/worker-brief.md b/.github-vscode/skills/l-01-agent-lifecycles/templates/worker-brief.md index db8f44d0..c91e9779 100644 --- a/.github-vscode/skills/l-01-agent-lifecycles/templates/worker-brief.md +++ b/.github-vscode/skills/l-01-agent-lifecycles/templates/worker-brief.md @@ -7,7 +7,8 @@ absorbed a series of real dispatch frictions (route-index leaks, attestation for keying, missing `python` shim), so deviate knowingly or not at all. Spawn with `env={"AR_SPAWN_ROLE": "worker"}` and the **qualified** leaf key -`//` so the session-start router and the dashboard leaf rail both engage. +`//`; together they claim the worker's `(leaf, role)` seat while the +session-start router and dashboard leaf rail engage. --- @@ -18,13 +19,14 @@ ROLE BRIEF — worker You are a WORKER for leaf `` of master `` (repo: ). Your lifecycle is `skills/l-01-agent-lifecycles/roles/worker.md`; this brief is your session start. Execute the leaf -completely, write your turn report, then stop. +code completely, write your builder turn report, then stop. Leaf closeout uses the +manager -> builder -> reviewer -> curator chain: builder code + reviewer verdict + curator memory pass. -## Worktrees (your ONLY writable areas) +## Worktrees (your code write area + memory context) - Code: `` (branch ``, base ``) -- Memory: `` +- Memory: `` (read/context for changed-path notes; the curator writes onboarding) - Plus your turn report at the path below. Nothing else. NEVER `git commit` — the owning seat - closes out after reviewing your report. + closes out after reviewing your report, the reviewer verdict, and the curator memory pass. ## Tool surface - Native file tools inside the two worktrees; shell for the checks below. @@ -46,18 +48,18 @@ files involved, the invariants that must hold, what NOT to touch.> - Full: — must exit 0. - `git diff --check` in both worktrees. -## Onboarding (same editing pass, per c-05) -- Changed source files: update the sidecar BODY now; new files: create the sidecar. -- Route overviews: genuine body update where routes changed; otherwise the newest history entry - uses the LITERAL form `- — No route impact: ` (timestamp first). -- Pin idiom for verification metadata: "Verification metadata pinned until closeout stamps the - commit." +## Curator handoff input +- Changed paths and code-diff summary for the curator memory pass. +- Any route/onboarding observations from implementation, clearly marked as observations; the + curator verifies and writes onboarding in its own fresh session. +- Pin idiom for any metadata note the curator needs: "Verification metadata pinned until closeout + stamps the commit." ## Turn report (mandatory, last act) Write `/-worker-report.md` following `skills/l-01-agent-lifecycles/templates/turn-report.md` — including exact check commands + -outcomes, the retrieval-evidence tally, and the respawn state. If blocked: fill Escalations and -stop — escalate to , never to the developer. +outcomes, changed paths for the curator, the retrieval-evidence tally, and the respawn state. If +blocked: fill Escalations and stop — escalate to , never to the developer. ``` --- diff --git a/.github-vscode/skills/w-02-light-task-workflow/master-template.md b/.github-vscode/skills/w-02-light-task-workflow/master-template.md index 42738c16..a807afd1 100644 --- a/.github-vscode/skills/w-02-light-task-workflow/master-template.md +++ b/.github-vscode/skills/w-02-light-task-workflow/master-template.md @@ -7,7 +7,7 @@ built to grow as the work unfolds. ## When to escalate to a series -The `l-01-agent-lifecycles` orchestrator lifecycle's `decide` step escalates a single task to a series once its size is apparent — the +The `l-01-agent-lifecycles` architect lifecycle's `decide` step escalates a single task to a series once its size is apparent — the implementation plan no longer fits on a single page, or the work splits into distinct slices that each deserve their own checklist and commit. You can also start single and escalate later: drop in the master `task.md` and move the existing plan into the first `NN_.md`. diff --git a/.hermes/HERMES.md b/.hermes/HERMES.md index 98580af1..a688526f 100644 --- a/.hermes/HERMES.md +++ b/.hermes/HERMES.md @@ -4,10 +4,10 @@ If `AR_SPAWN_ROLE` is set, or your first user message is a role brief from an orchestrating agent: **ignore this notice entirely — your brief is your session start.** -Otherwise you are the developer-facing session, i.e. the **orchestrator**: read +Otherwise you are the developer-facing session, i.e. the **architect**: read `/ar-coordination/AGENTS.md` and treat those rules as workspace instructions, then run your lifecycle at -`skills/l-01-agent-lifecycles/roles/orchestrator.md` — trust checkpoint before +`skills/l-01-agent-lifecycles/roles/architect.md` — trust checkpoint before relying on memory, `read_ar_files` (paired source+onboarding) until the build decision, retrieval-strategy tally as evidence, notify-and-stop at every developer hand-off. diff --git a/.hermes/skills/c-05-create-or-update-onboarding-files/SKILL.md b/.hermes/skills/c-05-create-or-update-onboarding-files/SKILL.md index 31ea0090..d8e39a82 100644 --- a/.hermes/skills/c-05-create-or-update-onboarding-files/SKILL.md +++ b/.hermes/skills/c-05-create-or-update-onboarding-files/SKILL.md @@ -16,6 +16,16 @@ Planning stays in task artifacts. This package defines how onboarding itself is Before maintaining onboarding, use `c-08-ar-coordination-context-resolver` to resolve the target repository's active coordination context. It must use the `Domain Documentation` category declared in the resolved `system/sources.md` for the onboarding slice being maintained, rather than assuming that adjacent onboarding alone is sufficient or hard-coding one particular documentation system into the skill. +**Seat routing:** in the manager -> builder -> reviewer -> curator chain (`l-01-agent-lifecycles` +`roles/curator.md`), onboarding create/update duty during leaf work belongs to the curator seat, not +the builder — the builder produces code and a turn report only. The curator runs this skill's +workflows from a change set (landed diff), the leaf task doc, and notes/ fed to it by the manager, +and routes each item to the right onboarding home (a concrete sidecar or the governing overview +whose subject it is; the L3 Operational-Notes target is last-resort only, never a default). The +strict 1-to-1 source mapping, governing-overview links, and metadata rules below are unchanged by +this routing — only the writing seat moved. A solo flat session with no separate curator seat runs +this skill itself, exactly as before. + When onboarding maintenance is happening inside a `c-09-git-worktree-manager` worktree, check `worktree_status` before starting memory entries. If its `freshness` block reports that the recorded code or memory source branch moved, diff --git a/.hermes/skills/c-09-git-worktree-manager/SKILL.md b/.hermes/skills/c-09-git-worktree-manager/SKILL.md index 692dec19..58a87184 100644 --- a/.hermes/skills/c-09-git-worktree-manager/SKILL.md +++ b/.hermes/skills/c-09-git-worktree-manager/SKILL.md @@ -1,6 +1,6 @@ --- name: c-09-git-worktree-manager -description: "Create, attach to, report on, integrate, finalize, and clean up Agents Remember worktree-backed tasks while preserving human approval gates and external-memory compatibility." +description: "Create, attach to, report on, integrate, finalize, and clean up Agents Remember worktree-backed tasks while preserving delegated/human approval gates and external-memory compatibility." --- # c-09-git-worktree-manager Git Worktree Manager @@ -75,11 +75,14 @@ The intended order is: integration branch and integrates back into it. For a nested master, create the child integration branch from the parent integration branch. 6. choose or review the task slug and workflow variables -7. **hand off** the **Worktree Intent Gate** (notify-and-continue) for explicit developer approval +7. establish the applicable worktree-start authority: for standalone/new work, hand off the + **Worktree Intent Gate** for explicit developer approval; for subordinate leaves/edges inside an + accepted orchestrated series, record the accepted-series authority and continue without a new + developer hand-off 8. create the durable task wrapper when one is needed 9. request the `worktree_start` MCP tool only after the task identity is stable, the correct landable `source_branch` is selected, external memory is clean, and - the developer approved the intent packet + the applicable authority has been recorded The Worktree Intent Gate must name: @@ -97,7 +100,7 @@ visible: leaf work branches integrate into the pushable integration/source branc recorded by their enclosure; protected targets are reached later through the repo's PR flow. -Run the applicable dry-run/preflight first, then **hand off**: call +For developer-gated starts, run the applicable dry-run/preflight first, then **hand off**: call `lifecycle_turn_end_notification(summary={…the intent packet + the approve/revise ask…})` as the **last tool call**, then deliver the intent packet as your final prose and **STOP / end your turn**. The notification sets the `awaiting-developer` lifecycle state, surfaces a @@ -106,6 +109,10 @@ dashboard or in the leaf's attached chat; the **first AR tool call of your next lifecycle (`running`), clears the attention item, and proceeds to `worktree_start` — you send no explicit `lifecycle_resume`. +For subordinate orchestrated-series starts, do the same dry-run/preflight, record the accepted +planner/series authority in the task decision log or worktree intent note, and continue. Do not add +a developer stop for every leaf worktree. + Parked fallback: the block-and-wait `lifecycle_gate` junction (plus the operator inbox and dashboard GateResponder) still works if you deliberately raise it for a durable, developer-attributed, mutation-blocking record: @@ -190,19 +197,27 @@ quality gate, memory content commit, ledger update, and ledger commit. For worktree-backed tasks, pass the leaf enclosure `series-contract.md` to `worktree_closeout_preview` / `worktree_closeout_apply`. The apply step records -the developer's explicit commit approval in the contract and updates the -contract closeout state after the code, memory, and ledger commits are created. +the applicable closeout authority in the contract and updates the contract closeout state after the +code, memory, and ledger commits are created. Worktree closeout stops if the recorded code or external-memory source branch moved since task start. ## Integration -Integration is explicitly human-gated and runs only after closeout completed. It lands the closed task branches back onto the recorded source branches and records the landed commits separately from the closeout commits. Orchestrated-run carve-out (ruled 2026-07-06): dependency-ordered leaf→master and master→super integrations ride the series' standing approval (the developer's portfolio-gate approval recorded in the planner master) — the developer hand-off concentrates at the super PR/carry-over gate per the `l-01-agent-lifecycles` loop/orchestrator doctrine; a raised durable `integration-approval` gate still awaits the developer. +Integration runs only after closeout completed and is authority-gated by context. It lands the +closed task branches back onto the recorded source branches and records the landed commits +separately from the closeout commits. In an accepted orchestrated run, dependency-ordered +leaf→master and master→super integrations ride the series' standing approval (the developer's +portfolio-gate approval recorded in the planner master) — the developer hand-off concentrates at +the super PR/carry-over gate per the `l-01-agent-lifecycles` loop/orchestrator doctrine. A raised +durable `integration-approval` gate still awaits the developer. On an orchestrated master's exit (master → super integration) the integrate step additionally enforces the delegated `master-handover-approval` seam: an undecided or policy-invalid handover gate addressed to the master (by `enclosure` = master task name) returns `handover-gate-blocked` instead of landing — decide the gate per the `l-01-agent-lifecycles` seam doctrine, then rerun. When no gate addresses the integrating master but open `master-handover-approval` gates exist elsewhere, integrate still proceeds and its result carries a `handover_gate_warning` naming them — treat it as a spelling check on the raised gate's `enclosure`. -Run `worktree_integrate(..., dry_run=true)` first, then **hand off**: call +Run `worktree_integrate(..., dry_run=true)` first. For subordinate accepted-series integrations, +record the standing series authority and then run the real integration without a developer stop. +For developer-gated integrations, **hand off**: call `lifecycle_turn_end_notification(summary={…the integration plan…})` as the **last tool call**, then deliver the integration preview as your final prose and **STOP**. The developer approves on the dashboard or in chat; the first AR tool call of your next turn auto-resumes @@ -228,9 +243,17 @@ After successful integration, complete any repo-specific landing tail first: pus ## Lifecycle Finalization And Cleanup -Lifecycle finalization is explicitly human-gated and runs only after closeout, integration, and any PR/carryover tail are complete. It proves the current parent-child branch edge, then removes the recorded code and memory worktrees, deletes local task branches only when Git can prove they are merged, removes empty worktree group folders when safe, records `cleanup: completed` in the contract, and updates task documents. - -Run `lifecycle_finalize_task(..., dry_run=true)` first, then **hand off**: call +Lifecycle finalization runs only after closeout, integration, and any PR/carryover tail are +complete, and its approval authority follows the same series boundary. For subordinate +accepted-series leaf/master edges, the owning manager/orchestrator may finalize and clean up after +the dry-run proves the landed edge. For final super→main cleanup, standalone work, or a deliberately +raised `cleanup-approval` gate, stop for developer approval. Finalization proves the current +parent-child branch edge, then removes the recorded code and memory worktrees, deletes local task +branches only when Git can prove they are merged, removes empty worktree group folders when safe, +records `cleanup: completed` in the contract, and updates task documents. + +Run `lifecycle_finalize_task(..., dry_run=true)` first. For subordinate accepted-series cleanup, +record the standing authority and run the real finalizer. For developer-gated cleanup, **hand off**: call `lifecycle_turn_end_notification(summary={…what cleanup removes…})` as the **last tool call**, then relay the landed-commit proof, cleanup plan, and task-document updates as your final prose and **STOP**. The developer approves on the dashboard or in chat; the first AR tool call of your next turn auto-resumes and runs @@ -277,11 +300,16 @@ then proceeds as usual, including closeout → integrate → finalize. 2. The `c-09-git-worktree-manager` skill does not initialize memory roots; use the `c-00-initialize-memory-repo` skill before starting external-memory worktrees. 3. Closeout belongs to the `c-12-closeout` skill; the `c-09-git-worktree-manager` skill only supplies worktree contract context. 4. The `c-09-git-worktree-manager` skill must not use divergent memory as semi-trusted reference context. -5. The `c-09-git-worktree-manager` skill must not bypass the `c-12-closeout` skill's explicit closeout approval gate. +5. The `c-09-git-worktree-manager` skill must not bypass the `c-12-closeout` skill's applicable + closeout authority gate. 6. The `c-09-git-worktree-manager` skill must not create closeout commits outside the `c-12-closeout` skill's code-memory-ledger sequence. 7. The `c-09-git-worktree-manager` skill must not call `worktree_start` until - the developer has approved the Worktree Intent Gate. -8. The `c-09-git-worktree-manager` skill must not move source branches during integration until replay/preflight has produced fast-forwardable code and memory commits and explicit integration approval exists. -9. The `c-09-git-worktree-manager` skill must not finalize or clean up without explicit cleanup/finalization approval. + the applicable authority has been recorded: developer-approved Worktree Intent Gate for + standalone/new work, or accepted-series authority for subordinate orchestrated work. +8. The `c-09-git-worktree-manager` skill must not move source branches during integration until + replay/preflight has produced fast-forwardable code and memory commits and applicable + integration authority exists. +9. The `c-09-git-worktree-manager` skill must not finalize or clean up without applicable + cleanup/finalization authority. 10. The `c-09-git-worktree-manager` skill must not treat squash-merged content as a normal landed edge. 11. The `c-08-ar-coordination-context-resolver` skill remains the facts-only resolver; the `c-09-git-worktree-manager` skill owns worktree and lifecycle mutation. diff --git a/.hermes/skills/c-12-closeout/SKILL.md b/.hermes/skills/c-12-closeout/SKILL.md index 2c7fe3ff..82c472a5 100644 --- a/.hermes/skills/c-12-closeout/SKILL.md +++ b/.hermes/skills/c-12-closeout/SKILL.md @@ -1,6 +1,6 @@ --- name: c-12-closeout -description: "Close out approved Agents Remember edits by preserving explicit commit approval, missing-onboarding checks, external-memory onboarding refresh, memory quality, ledger alignment, and no automatic push for worktree-backed tasks." +description: "Close out approved Agents Remember edits by preserving the applicable approval authority, missing-onboarding checks, external-memory onboarding refresh, memory quality, ledger alignment, and no automatic push for worktree-backed tasks." --- # c-12-closeout Closeout @@ -15,6 +15,18 @@ direct-checkout closeout path. Use the `c-09-git-worktree-manager` skill for worktree start, attach, status, integration, lifecycle finalization, and cleanup; use this skill for the closeout gate and code-memory-ledger commit order. +**Seat note (manager -> builder -> reviewer -> curator chain):** in that chain, the builder produces +code and a turn report only — it does not author onboarding. The dedicated curator seat +(`l-01-agent-lifecycles` `roles/curator.md`) runs the `c-05-create-or-update-onboarding-files` skill +as its own fresh pass, fed the leaf's landed change set, task doc, and notes/, BEFORE the owning +seat (the manager) runs this skill's closeout preview. Everywhere below that says "create" or +"refresh" onboarding, that authoring already happened in the curator's pass; the seat running +closeout **verifies** the curator's output against the checks in this skill, it does not author +onboarding inline to make a failing check pass. A check that still fails after the curator pass is a +closeout failure — respawn/rerun the curator, do not patch onboarding from the closeout seat. This +distinction does not apply outside that chain (e.g. a solo flat session with no separate curator +seat still runs `c-05-create-or-update-onboarding-files` itself before closing out). + ## MCP Tools Use the worktree closeout tools against the task contract: @@ -29,18 +41,31 @@ Worktree closeout records closeout state in the contract the `c-09-git-worktree-manager` skill owns later integration, lifecycle finalization, cleanup, and task-document completion. -## Approval Hand-off +## Approval Authority + +Closeout is always authority-gated, but the authority is contextual. + +For standalone work, final super-branch landing, or any closeout where the accepted task/series +authority is unclear, agents must request the matching preview tool first, relay the proposed code, +memory, and ledger commit messages to the developer, and ask for explicit commit approval. + +For subordinate work inside an accepted orchestrated series, the owning seat may apply closeout +under delegated series authority after the preview/checks are clean. Managers govern leaf commits; +the orchestrator governs manager/master edges and direct flat work when it is wearing the manager +or worker hat itself. Do not stop for the developer merely because closeout will create code, +memory, and ledger commits. The `intent_note` records the authority source, e.g. the accepted +planner/series task and the owning seat's review of the preview. -Closeout is explicitly human-gated. Agents must request the matching preview -tool first, relay the proposed code, memory, and ledger commit messages to the -developer, and ask for explicit commit approval. +Closeout still stops for the developer when the work reaches the final completed super branch / +PR-carryover gate, when a `closeout-approval` gate has been deliberately raised, when the change is +outside the accepted scope, when checks remain red outside the task, when onboarding/memory quality +cannot be repaired inside the leaf, or when a quo-vadis decision is required. -Real closeout uses the matching apply tool with an `intent_note`. The note -records the developer's explicit commit approval. Agents must not treat -implementation approval, a previous "looks good", or their own judgment as -commit approval. +Real closeout uses the matching apply tool with an `intent_note`. The note records the applicable +authority: either explicit developer commit approval or delegated accepted-series authority. Agents +must not treat a vague "looks good" or their own preference as authority. -The relay follows the `l-01-agent-lifecycles` orchestrator hand-off protocol: run the +For a developer-gated closeout, the relay follows the `l-01-agent-lifecycles` orchestrator hand-off protocol: run the preview/dry-run first, then call `lifecycle_turn_end_notification(summary={…the preview facts + the commit ask…})` as the **last tool call**, then deliver the preview facts and proposed messages as plain @@ -66,10 +91,11 @@ the dashboard and a `closeout-approval` gate is explicitly raised, closeout is * server-side through that durable gate, so a developer can approve from the cockpit and the mutating tool — not a UI button — is the security boundary. -`closeout-approval` **is** the commit gate — closeout is the single -commit-of-record for code, memory, and ledger, so there is no separate -`commit-approval` kind; every commit routes through this gate. The dashboard -junction uses the preview/dry-run -> chat report -> `lifecycle_gate` order above. +`closeout-approval` **is** the human commit gate when it is deliberately raised — closeout is the +single commit-of-record for code, memory, and ledger, so there is no separate `commit-approval` +kind. Subordinate orchestrated-series closeouts normally do not raise this gate; they use the +accepted-series authority recorded in the `intent_note`. The dashboard junction uses the +preview/dry-run -> chat report -> `lifecycle_gate` order above. How it binds: @@ -99,16 +125,19 @@ How it binds: Rules: -1. **Never self-approve.** A model-attributed approval is rejected by - enforcement. Wait for the developer's dashboard decision or Chat response, and - never pass your own judgment off as commit approval. +1. **Never self-approve a human-pinned gate.** A model-attributed approval is rejected by + enforcement. Wait for the developer's dashboard decision or chat response when a + `closeout-approval` gate exists, and never pass your own judgment off as developer approval. + Delegated-series closeout without a raised gate is different: it records the accepted series + authority and the owning seat's review in `intent_note`. 2. **Opening a gate is opt-in and deliberate.** Open a `closeout-approval` gate **only** when a developer is driving approval from the dashboard. Do **not** open one in a pure-chat session with no cockpit watching — an `open` gate blocks your own closeout until it is decided. -3. **Gateless lifecycles are unchanged.** With no `closeout-approval` gate the chat - commit gate (`intent_note` after an explicit "commit") governs exactly as before; - enforcement is additive, never a new requirement on every closeout. +3. **Gateless lifecycles use the applicable authority.** With no `closeout-approval` gate, + standalone/final work still needs explicit developer commit approval, while accepted + orchestrated-series subordinate work may proceed under delegated series authority. Enforcement is + additive, never a requirement to raise a gate on every closeout. 4. The closeout preview/apply payload carries a `closeout_gate` block (`enforced` / `permitted` / `gateId` / `reason`); relay it at the commit-approval gate so the developer sees whether a dashboard gate is open, approved, or absent. @@ -130,17 +159,22 @@ python -m agents_remember.memory_quality.integrity.check_missing_onboarding --co ``` The check only evaluates files that are new in the current checkout or -worktree, not the whole historical repository. If it reports missing -onboarding, create those sidecars through the `c-05-create-or-update-onboarding-files` skill before committing code. After -the code commit exists, refresh the new sidecars' verification metadata to that -commit during the normal post-code-commit memory refresh. +worktree, not the whole historical repository. In the manager -> builder -> +reviewer -> curator chain, this check is expected to already pass by the time the owning seat runs +it, because the curator's memory pass created those sidecars through the +`c-05-create-or-update-onboarding-files` skill before this precondition is checked; running the +check here confirms that pass, it is not the trigger to author onboarding from the closing seat. If +it still reports missing onboarding, do not create the sidecars inline — escalate to run (or rerun) +the curator's memory pass, then rerun this check. After the code commit exists, refresh the new +sidecars' verification metadata to that commit during the normal post-code-commit memory refresh. Changed (already-onboarded) source files have a parallel requirement: their sidecar content must be updated to approved current state before closeout. The closeout gate rejects any changed source file whose existing sidecar body was not modified in the current task, because advancing verification metadata over -stale content defeats the commit-hash-based drift check. Update changed sidecars -during implementation, not at the metadata-refresh step. +stale content defeats the commit-hash-based drift check. In the curator chain, changed sidecars are +updated during the curator's memory pass, not at the metadata-refresh step, and not by the builder +during implementation. The closeout worklist covers the working tree plus the leaf contract-recorded committed range: every path changed between the last verified commit (the @@ -159,11 +193,15 @@ deliberately through the `c-05-create-or-update-onboarding-files` skill. External-memory closeout order is: -1. run `check_missing_onboarding` against current additions -2. create missing onboarding for newly added eligible source files before committing code +1. run `check_missing_onboarding` against current additions (in the curator chain, this confirms the + curator's pass already covered them — it is not the cue to author onboarding here) +2. if onboarding is still missing, escalate to run/rerun the curator's memory pass through the + `c-05-create-or-update-onboarding-files` skill before committing code (solo flat sessions with no + separate curator seat create it directly) 3. commit code changes and capture `C2` plus its commit date 4. run the `c-02-memory-quality-control` skill's drift check against `C2` to produce the full memory update worklist -5. verify each changed source file's sidecar content was updated in this task, then refresh affected onboarding `lastVerifiedCommitHash` and `lastVerifiedCommitDate` to `C2`; a changed source file with an unmodified sidecar body fails the closeout instead of receiving a metadata-only refresh +5. verify each changed source file's sidecar content was updated in this task (by the curator's pass + in the chain above), then refresh affected onboarding `lastVerifiedCommitHash` and `lastVerifiedCommitDate` to `C2`; a changed source file with an unmodified sidecar body fails the closeout instead of receiving a metadata-only refresh 6. refresh affected repo entity catalog `git-blob-set-v1` fingerprints against `C2` when changed source paths are listed as entity evidence 7. refresh affected route overview `lastVerifiedCommitHash` / `lastVerifiedCommitDate` metadata to `C2` 8. refresh generated route indexes so `overview.index.json` matches the updated onboarding tree @@ -214,16 +252,20 @@ preview and apply payloads for the commit-approval relay. Worktree closeout also fails when the recorded code or external-memory source branch moved since task start. -Missing onboarding is the expected hard failure when the implementation/update -pass did not produce a required onboarding file. The next step is to run the `c-05-create-or-update-onboarding-files` skill -for that source file, then rerun the closeout preview. +Missing onboarding is the expected hard failure when the required onboarding file was not produced — +in the manager -> builder -> reviewer -> curator chain that means the curator's memory pass did not +cover it. The next step is to run (or rerun) the curator's `c-05-create-or-update-onboarding-files` +pass for that source file, then rerun the closeout preview; a solo flat session with no separate +curator seat runs that skill itself. ## Boundaries 1. The `c-12-closeout` skill owns closeout approval and code-memory-ledger commit sequencing. 2. The `c-12-closeout` skill does not create worktrees, integrate worktrees, finalize lifecycles, or clean up worktrees. 3. The `c-12-closeout` skill does not initialize memory roots; use the `c-00-initialize-memory-repo` skill. -4. The `c-12-closeout` skill must not commit without explicit commit approval after a closeout preview. +4. The `c-12-closeout` skill must not commit without the applicable authority after a closeout + preview: explicit developer commit approval for standalone/final work, or recorded delegated + series authority for subordinate accepted-series work. 5. The `c-12-closeout` skill must not create a memory content commit whose affected onboarding metadata still points at pre-closeout code. 6. The `c-12-closeout` skill must not create a memory content commit before route overview metadata, generated route indexes, and `memory_quality_check` are clean for the new code commit. 7. The `c-12-closeout` skill must not push automatically. diff --git a/.hermes/skills/c-13-install-and-onboard/SKILL.md b/.hermes/skills/c-13-install-and-onboard/SKILL.md index d583fd13..e52d9f57 100644 --- a/.hermes/skills/c-13-install-and-onboard/SKILL.md +++ b/.hermes/skills/c-13-install-and-onboard/SKILL.md @@ -152,8 +152,8 @@ preference): 4. **Harness preference + role knobs** (`orchestration.spawn.harness`, per-role `orchestration.roles.`, per-level `orchestration.rolesPerLevel..`) - which installed harness - `spawn_agent_session` uses when the spawning seat passes none, per-role - harness/model/effort overrides, and per-LEVEL overrides + `spawn_agent_session` uses when no role/level knob supplies one, per-role + harness/model/effort settings, and per-LEVEL settings (leaf|master|portfolio) for tiered economics (e.g. a cheap leaf reviewer, a smarter master-seam reviewer). Harness values must be known ids: the builtin registry (`claude`, `codex`, `pi`) or an `orchestration.harnesses` @@ -163,8 +163,10 @@ preference): `ultracode`); mention the FREE-FORM escape hatch for anything outside the vocabularies - `launchArgs` (verbatim argv), `sessionCommands` (pasted before the brief), `promptKeywords` (prepended to the brief) - never - validated, recorded in spawn provenance. Default: detection-gated (the - first detected harness). The full spawn-surface manual is + validated, recorded in spawn provenance. Ordinary spawning seats cannot + pass `harness`/`model`/`effort`, launch/session spend controls, or harness-native + spend/endpoint env keys directly; settings are the spend surface. Default: + detection-gated (the first detected harness). The full spawn-surface manual is `docs/reference/harnesses.md`. If the developer wants to skip the interview, confirm the seeded defaults diff --git a/.hermes/skills/l-01-agent-lifecycles/SKILL.md b/.hermes/skills/l-01-agent-lifecycles/SKILL.md index 1d390618..4094072d 100644 --- a/.hermes/skills/l-01-agent-lifecycles/SKILL.md +++ b/.hermes/skills/l-01-agent-lifecycles/SKILL.md @@ -1,6 +1,6 @@ --- name: l-01-agent-lifecycles -description: "The agent lifecycles: one lifecycle per agent type, under one roof. Routes every session by exactly three conditions (spawn-role env -> role brief -> otherwise orchestrator), carries the minimal lifecycle frame (the six lifecycle signals every session shares), and houses the self-contained per-role lifecycles (orchestrator, designer, strategist, manager, worker, adversarial reviewer) plus the report-template library and the reviewer criteria catalogs. A developer-facing session IS the orchestrator; solo work is the degenerate portfolio. Supersedes and replaces both l-01-session-job-lifecycle and l-02-agent-orchestration." +description: "The agent lifecycles: one lifecycle per agent type, under one roof. Routes every session by exactly three conditions (spawn-role env -> fresh role brief -> otherwise free-chat launcher), carries the minimal lifecycle frame (the six lifecycle signals every session shares), and houses the self-contained per-role lifecycles (architect, orchestrator, designer, strategist, manager, worker, curator, system-specialist, adversarial reviewer) plus the report-template library and the reviewer criteria catalogs. Solo work is the degenerate portfolio. Supersedes and replaces both l-01-session-job-lifecycle and l-02-agent-orchestration." --- # l-01-agent-lifecycles — The Agent Lifecycles @@ -15,42 +15,128 @@ lifecycle, and no role reads another role's file. 1. **`AR_SPAWN_ROLE` is set** (spawn env, injected by `spawn_agent_session`) → run `roles/.md`. Nothing else in this file's "developer session" material applies to you. (`designer` here means the same design hat in a separate chair — see `roles/designer.md`.) -2. **Else: the first user message is a role brief** — a `templates/*-brief.md`-shaped dispatch or +2. **Else: the first user message is a role brief in a fresh session** — a `templates/*-brief.md`-shaped dispatch or a first line of the form `ROLE BRIEF — ` from an orchestrating agent → run that role's lifecycle. The brief is your session start; a workspace session-start notice is not addressed to you. -3. **Else** (a developer opened this session) → you are the **orchestrator**: run - `roles/orchestrator.md`. Solo work is the degenerate portfolio — the same three jobs with hats - collapsed (the orchestrator wears the manager hat in flat runs and builds hands-on at session - scale); the task doc still comes first. +3. **Else** (a developer opened this session) → you are the developer-facing **free chat** — a + launcher, not a role seat (ruled 2026-07-09). Research-only questions are answered inline with + no role taken. The moment the ask is role-shaped (a sprint, a task, any durable change), do NOT + assume the architect role in this chat: **spawn the architect into its own chat** + (`spawn_agent_session` with `AR_SPAWN_ROLE=architect`; the profile — harness/model/effort — + comes from `orchestration.roles.architect` in settings, never from this session's guess) and + point the developer at that chat. A clean startup with the settings-owned profile removes all + profile ambiguity. The spawned architect runs `roles/architect.md` and owns the developer + conversation from there. There is no fourth entry, and the edge cases are decided: an **unresolvable `AR_SPAWN_ROLE` value** (no matching `roles/.md`) falls through to condition 2 (the brief); a role-env session **whose brief never arrives** announces itself on the inbox and waits — it never -improvises a task; `AR_SPAWN_ROLE=orchestrator` is valid only as a takeover chair (the Profile -check (takeover) in `roles/orchestrator.md`, The Event Loop) — the developer still talks to **one** orchestrator. Orchestrated -fan-out (spawning managers/workers at scale) begins only on an explicit developer request (e.g. -*"orchestrate these masters"*) — no agent promotes itself into a spawning seat. +improvises a task; `AR_SPAWN_ROLE=orchestrator` is valid only as a spawned backend seat or a +backend takeover chair — the developer still talks to the **architect**, not the orchestrator. +The spool-up chain is fixed and self-driving (ruled 2026-07-09): free chat spawns the +**architect**; the architect spawns the **orchestrator** for portfolio execution; the +orchestrator spawns **managers** per the approved plan and the concurrency settings; managers +spawn their **workers**. No seat waits to be told "spawn this, spawn that" — each level spawns +its next level from the plan. Only two spool-up decisions ever go back to the developer, both as +questions the agent raises itself: whether to run a **strategist** pass (proposed, never +auto-run), and whether to take the **short root** (solo, no orchestration) when the work looks +tiny — see `roles/architect.md`. One exception to the no-cross-reading rule above: **a seat that WEARS a hat runs that hat's file -as its own** — the orchestrator always for `roles/designer.md`, and in flat runs for -`roles/manager.md` (the hat-collapse rule). +as its own** — the architect may wear `roles/designer.md`, and in solo/flat runs may wear backend +or build hats (the hat-collapse rule). A spawned role seat never wears another role's hat. + +## Developer-Declared Task-Seat Takeover + +When the developer says *"you are the orchestrator/manager/worker for task X"* (or equivalent), +that is a **task-seat takeover**, not a loose role hint. The named task leaf is the seat. Before +analysis, profile checks, spawning, or implementation, open the named task doc and attach the +current dashboard chat to that leaf. + +Operational checklist: + +1. Resolve the named task to the **qualified** leaf key `//` and the + lifecycle role this seat is claiming. +2. Use the dashboard terminal catalog session id for this chat — not `CLAUDE_CODE_SESSION_ID`, not + `CODEX_THREAD_ID`. +3. Call `attach_terminal_session_to_leaf` with that qualified leaf key and the claimed role. +4. Rename the session to the seat label the developer expects. +5. Verify the terminal catalog and dashboard row show that exact `(qualified leaf key, seat role)` + binding before continuing. + +If no dashboard terminal catalog session id can be found, record the blocker and ask for the +missing attachment path. Do not claim the seat is attached until the catalog/dashboard row proves +it. After the attachment is verified, continue with the lifecycle selected above. + +## Developer Clarification Triage + +When the developer clarifies a rule, boundary, or desired behavior during an active task, decide +whether it is **current implementation** or **future queue** before writing only a note. Read the +active queue first: the current leaf, parent/master, neighboring leaves, decision log, open +questions, and in-flight branch state. The question is not whether a note is useful; it is whether +the developer is effectively steering the work already in hand. + +Treat it as current implementation when queue context and closeness point at the active change: it +names the same task/leaf/master, resolves a defect exposed by the current work, or improves the +same doctrine or code path. A small change that plainly fits the current diff is a strong signal +for immediate implementation even if the developer phrases it as "maybe" or "we can wrap this in." +In that case, extend the current task surface/decision log enough to make the added scope visible +and implement it now. + +Treat it as future queue when it names a later release, a separate subsystem, a large scope jump, +work whose correctness depends on another unfinished master, or a change that would reorder +already-running leaves. Record the item in the right durable queue or ask the owning seat to plan it +later. If the intent is genuinely ambiguous after reading the queue, ask the developer directly +whether they want immediate implementation or a queued note. Do not silently downgrade a +close/current/small change into future speak, and do not silently expand the active leaf when the +fit is unclear. ## The Role Registry | Role | Seat | Lifecycle file | | --- | --- | --- | -| **orchestrator** | the developer-facing session; first coordination leaf of an orchestrated series | `roles/orchestrator.md` | -| **designer** | a HAT the orchestrator pulls inline (front of the pipeline or mid-flight; separate chair optional) | `roles/designer.md` | -| **strategist** | the sprint planner, SPAWN-FIRST; a strategist run is a **mandatory precondition for any orchestrated run** — its deliverable is the orchestration task (sprint plan + scope); spawn value `strategist` | `roles/strategist.md` | +| **architect** | the developer-facing owner seat; design conversation, decision-item relay, and drawing board | `roles/architect.md` | +| **orchestrator** | spawned backend portfolio/orchestration seat; never developer-facing | `roles/orchestrator.md` | +| **designer** | a HAT the architect pulls inline (front of the pipeline or mid-flight; separate chair optional) | `roles/designer.md` | +| **strategist** | the sprint planner, SPAWN-FIRST when the developer approves the architect's propose-first question; its deliverable is the orchestration task draft (sprint plan + scope); spawn value `strategist` | `roles/strategist.md` | | **manager** | one coordination leaf per master; drives that master's leaf loop | `roles/manager.md` | | **worker** | one leaf worktree, short-lived, fresh session | `roles/worker.md` | +| **curator** | fresh per leaf after builder/reviewer; writes onboarding only from task docs, notes, and code diff | `roles/curator.md` | +| **system-specialist** | backend provider-degradation investigator; report first, fixes only after explicit orchestrator order; spawn value `system-specialist` | `roles/system-specialist.md` | | **adversarial reviewer** | short-lived, spawned at the two seams (master-exit, super-exit) and as any three-party loop's reviewer seat (criteria catalogs bound per review type); spawn value `reviewer` | `roles/reviewer.md` | The **lenses** (bug · feature · triage · research — `lenses.md`) are how the scoping seats -(orchestrator, designer) read a piece of work; a dispatched role never picks a lens — its brief +(architect, designer, backend orchestrator) read a piece of work; a dispatched role never picks a lens — its brief already carries the flavor. +## Role-Seat Immutability (dashboard-owned sessions) + +When the dashboard owns a session, its role is fixed for the session lifetime. Roles expand +**horizontally** by spawning new, individually addressable chats; sub-agents drill **vertically** +inside one seat's context for deeper analysis. A dashboard-owned session that already has a role +refuses a pasted role brief instead of silently rerouting itself; it escalates the mismatch to its +owner via the inbox. Router condition 2 applies only to fresh sessions. Sessions not owned by the +dashboard follow the host harness's ordinary rules. + +Hat-collapse is sanctioned only for the owner/developer-facing architect seat in solo or flat +runs. Spawned role seats never absorb another role brief and never become a different role in +place. + +## Minimal Decision-Item Relay + +The ARCHITECT/ORCHESTRATOR split uses the existing operator inbox now. No full queue schema or +dashboard reform is introduced here. + +- Backend seats post one `messageKind: decision-item` inbox row at a time to the architect. The row + states what is being decided, the options, the consequences, and the durable evidence refs. +- The architect presents one item at the developer's pace, records the ruling in the durable task + surface (`openQuestions` / decision logs, with notes for analysis), and returns one + `messageKind: decision-ruling` inbox row to the backend seat. +- If the item is underspecified, the architect sends a single clarification row back instead of + guessing. The backend does not open a second item until the active item has a durable ruling or + clarification state. + ## The Minimal Frame (the only machinery every session shares) Every session in a managed repo may be a **lifecycle**: six signals — `lifecycle_start` · @@ -72,19 +158,34 @@ handles a lifecycle id** — identity is server-side, anchored in the worktree c Rules: a tool call outside any lifecycle is **dropped, never misattributed**; `paused` is system-owned. **A spawned role that never touches mutating AR tools simply never instantiates a lifecycle — that is correct, not a violation.** A spawned role runs its **own** lifecycle when it -runs one; it never adopts its spawner's. The session↔leaf association is the catalog binding made -at spawn (the **qualified** leaf key `//`), not lifecycle adoption. +runs one; it never adopts its spawner's. The session↔task-seat association is the catalog binding +made at spawn — the pair of **qualified** leaf key `//` and seat role — +not lifecycle adoption. Different roles may coexist on one leaf; only a second live owner of the +same `(qualified leaf key, seat role)` pair collides. + +**Notify-and-stop is safe by design (HFX2-L1..L4, landed):** ending a turn on +`lifecycle_turn_end_notification` — or simply stopping once your artifact is written and nothing is +pending — is never a liveness gap. Silence is supervised: the HFX2-L2 supervisor sweep evaluates +every expected artifact/signal on its own mechanical tick and the HFX2-L4 escalation ladder +(renudge → skip-level → architect custody/architect attention, then respawn) handles inactivity. **No role watches, +polls, or nudges on its own initiative — that is a banned seat-local watcher (uniform-mechanism +ruling 2026-07-07).** Every role's own liveness duty inverts to *passive*: you will be woken with +your pending signals; process and ack every item before ending your turn again. ## Shared Invariants (every role can count on these) - **Continuity lives in the `task_doc` + durable artifacts, never in transcripts** — which is why short-lived workers and reviewers are safe, and why every seat writes its artifact of record. -- **Escalation ladder: worker → manager → orchestrator → developer.** No rung is skipped, ever. +- **Escalation ladder:** worker → manager → orchestrator → architect → developer; system-specialist + → orchestrator. No rung is skipped, ever. Each role file states only its own rung. - **Observability:** coordination seats are `task_doc` leaves with attached chats; the developer can walk into any seat at any level. - **Decision-needing questions land in the task doc's `openQuestions`** — the rendered decision surface; `notes/` carries the analysis behind them. +- **Dependency graph, not habit, decides sequencing.** Parallelize independent work by default up + to the applicable `orchestration.concurrency` cap. Sequential execution is the exception and + must name a gate, a shared-file one-writer dependency, or an explicit ruling. ## The Three-Party Loop (one home — this section owns the loop doctrine) @@ -95,9 +196,9 @@ section; they do not restate it. | Level | Owner (holds the deliverable, rules, lands) | Builder | Reviewer | | --- | --- | --- | --- | -| Leaf | the leaf's owning seat (manager; orchestrator in tight/flat mode) | spawned worker (no-commit contract) | spawned reviewer, criteria catalog + liberty | +| Leaf | the leaf's owning seat (manager; architect in tight/flat mode) | spawned worker (no-commit contract) | spawned reviewer, criteria catalog + liberty | | Master | the manager | the leaf workers | the master-exit seam reviewer (verdict rides `master-handover-approval`) | -| Portfolio | the orchestrator | the STRATEGIST (spawn-first) | reviewer with the plan-review catalog | +| Portfolio | the backend orchestrator (developer-facing decisions relayed through the architect) | the STRATEGIST (spawn-first) | reviewer with the plan-review catalog | **Complexity-scored tiers (per leaf, at dispatch).** The owning seat scores three axes — blast radius (doctrine/enforcement/public surface vs leaf-local) · novelty (new subsystem vs @@ -123,13 +224,14 @@ they do not open them. open finding set. A round that does not shrink it escalates immediately, regardless of the count; a monotonically converging loop may never hit the cap at all. At the cap, or on non-convergence, the owner does not spin another round — it **escalates one seat up the ladder (worker → manager → -orchestrator → developer) with the full round history attached**; the escalation packet IS the +orchestrator → architect → developer) with the full round history attached**; the escalation packet IS the upper seat's visibility. **Quo-vadis (the written developer-escalation criterion).** A question is developer-worthy when it is a **high-blast-radius truth** — answered wrong it means big rewrites later (architecture direction, security posture, doctrine contradictions, irreversible data/branch operations, where -agent settings live). Quo-vadis questions escalate IMMEDIATELY, regardless of round count. +agent settings live). Quo-vadis questions escalate IMMEDIATELY to the architect relay, +regardless of round count. Presentation-grade choices (2px vs 3px) never do — the owner rules and logs. **Criteria catalogs (the reviewer as test bench).** Criteria are never made up on the spot: every @@ -141,8 +243,27 @@ promotion ratchet (each catalog carries it). `roles/reviewer.md` binds them. orchestrator-level set (the strongest models) and the manager-level set (cheaper, possibly workflow-free) are configured per level in the `orchestration.loops` settings block (schema in `docs/reference/settings-json.md`; stored in the global agentic settings file with repo-local -override, parsed by the kernel agentic-settings loader — L13, landed). The strategist's mandatory -pre-run is doctrine, not a knob — it is unconditional. +override, parsed by the kernel agentic-settings loader — L13, landed). The architect proposes a +strategist pre-run, and it occurs only after developer approval; settings cannot auto-run it. + +## Delegated Series Authority + +Once the developer accepts an orchestrated series/portfolio plan, that acceptance is standing +authority for the owning seats to execute the subordinate edges in that series. Managers govern +their workers and leaf closeouts. The orchestrator governs managers, master handovers, master → +super integrations, and the same closeout/finalize/cleanup mechanics when it wears a manager or +worker hat in a flat/direct run. These edges do **not** stop for a new developer approval just +because a commit, lifecycle finalization, cleanup, or integration command is next; the owner runs +the preview/check, records the accepted-series authority in the intent note or decision log, and +continues. + +This does **not** weaken the escalation ladder. Developer approval is still required for the final +completed super integration branch / PR-carryover gate, for any human-pinned gate that is actually +raised (`integration-approval`, `push-approval`, `cleanup-approval`), for scope changes beyond the +accepted plan, for red checks that cannot be fixed inside the task, and for quo-vadis decisions. +Owner-never-self-approves means verdicts and delegated gates need the configured distinct decider; +it does not force a developer hand-off for mechanical closeout of in-scope work the owning seat +performed directly under standing series authority. ## Knob Block & Capability Doctrine (no per-harness files) @@ -156,6 +277,9 @@ doctrine any coding agent can apply, and harness PREFERENCE is deployment config seats, `spawn_agent_session` is itself the harness-independent fan-out: a harness with no sub-agent facility still dispatches seats through the framework (a chat, no leaf attachment required) — the DBMS principle: one behavior, any engine. +For ordinary spawned seats, settings are the sole developer-controlled spend surface: +`spawn_agent_session` callers declare role and level, never harness/model/effort or direct +launch/session spend controls. ## settings.json Orchestration Block @@ -169,9 +293,12 @@ defaults < global settings < repo-local settings. { "orchestration": { "roles": { // role → knob override; validated: harness/model/effort · free-form: launchArgs/promptKeywords/sessionCommands + "architect": { "harness": "claude", "effort": "high" }, "orchestrator": { "harness": "claude", "effort": "high" }, "strategist": { "effort": "ultracode" }, // session-vocabulary value → "/effort ultracode" post-launch "reviewer": { "harness": "claude", "model": "sonnet", "effort": "high" }, + "system-specialist": { "harness": "claude", "model": "fable", "effort": "high" }, + "curator": { "harness": "codex", "effort": "medium" }, "worker": { "harness": "codex", "effort": "medium" } }, "rolesPerLevel": { // per-LEVEL agent sets (leaf|master|portfolio), deep-merged over roles @@ -179,7 +306,7 @@ defaults < global settings < repo-local settings. "portfolio": { "reviewer": { "model": "fable", "effort": "ultracode" } } }, "concurrency": { "maxParallelMasters": 2, "maxParallelLeaves": 3, "maxSubAgents": 4 }, - "spawn": { "harness": "claude" }, // spawn_agent_session default when the seat passes none + "spawn": { "harness": "claude" }, // fallback when no role/level knob supplies one "gateDelegation": { "policy": "manager-decides-leaf-gates", "requireReviewerVerdictAtSeams": true @@ -199,11 +326,13 @@ needs a restart (an authority-file value is a one-cycle legacy fallback with a b `requireReviewerVerdictAtSeams` **binds delegated seam decisions** (`master-handover-approval`) to attached reviewer-verdict evidence; the named policy `manager-decides-leaf-gates` routes leaf gates to the manager and the master-exit handover to the **orchestrator** (human review concentrates at -the super gate). `spawn_agent_session` resolves its knobs (260703-L16) as explicit args > +the super gate). `spawn_agent_session` resolves its spend knobs (260703-L16 + HFX2-L10) as repo-local level override > global level override > repo-local role default > global role default > detection-gated default — the dispatcher declares its `level` (leaf|master|portfolio, default -leaf) and the resolved level rides spawn provenance — and **applies** them at the harness -boundary: model/effort ride as `AR_SPAWN_MODEL`/`AR_SPAWN_EFFORT` env AND map onto the launch argv +leaf) and the resolved level rides spawn provenance. Legacy caller-supplied `harness`/`model`/ +`effort`, direct launch/session controls, `AR_SPAWN_MODEL`/`AR_SPAWN_EFFORT`, or harness-native +spend/endpoint env keys refuse before spawning with `spend-override-unsupported`. Resolved knobs are +**applied** at the harness boundary: model/effort ride as `AR_SPAWN_MODEL`/`AR_SPAWN_EFFORT` env AND map onto the launch argv per-harness via the effective registry (claude `--model`/`--effort`; a mapping-less harness stays env-only; a session-vocabulary effort like claude's `ultracode` is delivered as a post-launch `/effort` paste). Unknown effort values REFUSE at dispatch naming the harness's vocabulary — the @@ -218,12 +347,15 @@ reuse, complexity thresholds) lives in the same block — meaning in ## Companion Files - `lenses.md` — the four job lenses for the scoping seats. -- `roles/…` — the six self-contained role lifecycles (the registry above). +- `roles/…` — the nine self-contained role lifecycles (the registry above). - `templates/…` — turn-report · worker-brief · manager-brief (`ROLE BRIEF — manager`; the - orchestrator compiles a manager's session start from it) · master-handover-packet · - conversation-handover-packet · verdict · impact-analysis · onboarding-coherency · - deep-research-report · orchestration-task (the strategist's sprint plan). Spawning seats compile - briefs FROM these; sub-agents fan out and fill them, so analysis survives compaction. + orchestrator compiles a manager's session start from it) · curator-brief (`ROLE BRIEF — curator`; + the manager compiles a curator's session start from it, feeding the leaf's landed change set + + task doc + notes/ — never spawned before builder code and the reviewer verdict exist) · + master-handover-packet · conversation-handover-packet · verdict · impact-analysis · + onboarding-coherency · deep-research-report · orchestration-task (the strategist's sprint plan). + Spawning seats compile briefs FROM these; sub-agents fan out and fill them, so analysis survives + compaction. - `criteria/…` — the reviewer criteria catalogs (code-seam · doctrine · onboarding-memory · report-verification · plan-review), the review test bench the three-party loop binds; maintained through the promotion ratchet, never made up on the spot. @@ -249,7 +381,7 @@ This skill absorbs and supersedes `l-01-session-job-lifecycle` and `l-02-agent-o orchestration vocabulary adopts the parked `260619_agentic-control-plane` spec — jobs as model-interpreted markdown (D6), the knob block (D7), role + lens in one file (D10), the ambient-singleton rule (D11), per-harness variants (D12), the judge rung, short-lived workers with -structured handoff, dev-talks-to-one-orchestrator (D15) — which in turn credits **Archon** and the +structured handoff, dev-talks-to-one-architect (D15) — which in turn credits **Archon** and the **agent-control-plane** project (D14); that credit carries forward. ## Relationship To Other Instructions diff --git a/.hermes/skills/l-01-agent-lifecycles/criteria/code-seam.md b/.hermes/skills/l-01-agent-lifecycles/criteria/code-seam.md index 053feb1b..31e271f6 100644 --- a/.hermes/skills/l-01-agent-lifecycles/criteria/code-seam.md +++ b/.hermes/skills/l-01-agent-lifecycles/criteria/code-seam.md @@ -67,6 +67,20 @@ foreign artifact's consumer. memory repo's ledger mid-`worktree_start`) PASSED all four lenses under this analysis — the clean exemplar that defined the class. A catch in a later engagement promotes. +### CS-6 — Scaling & reclamation *(promoted — 2 catches)* + +**For every loop, store, log, or queue the change set touches, interrogate all four.** + +1. (D1 — stability) At 10x/100x fleet, does this mechanism's worst-case resource draw threaten the substrate? Where is the budget / backpressure / load-shed that makes it shed the signal, not the system? +2. (D2 — bounded) What is the worst-case time and on-disk / in-memory size? Where is the per-cycle cap and the store's cap+eviction? Is any re-read O(n) per item (accidentally quadratic across layers)? +3. (D3 — reclamation) Who reclaims this data, is that reclamation landed in THIS change, and is it tested by scaling (>=2 sizes), not a single-N smoke? +4. (D4 — quiescence) With ALL external inputs at zero (no acks, no live seats, no operator), does every feedback actor reach a fixed point? Any actor whose OUTPUT is a member of its own INPUT class (an escalation about an escalation, a retry row that is itself retryable, an event emitted while folding events) must prove convergence with a multi-cycle zero-input simulation — a per-cycle budget is a rate limiter, NOT a convergence proof, and per-zone verdicts cannot see a loop that lives on the edge BETWEEN zones. + +- Ruled invariant (developer, 2026-07-09, supersedes any per-row durability doctrine): **no event, message, or row outranks system health.** Notification rows coalesce — a re-firing condition updates its ONE existing row (date, tries, rung), never appends a sibling. Every store is purgeable: pending rows age out, hard caps evict oldest-first, and the durable record is the artifact on disk, never the queue row. A reviewer who finds "keep it all" semantics on a queue-like store files it as a defect regardless of what older doctrine says. +- Catching evidence: 260707-HFX2-L7 — the supervisor sweep re-folded a 61 MB append-only inbox once per finding (O(n^2)) over 4,298 never-reclaimed dead-seat rows, freezing the heartbeat and pegging a core while every correctness test passed. Style/CRAP/cyclomatic gates were all green. +- Catching evidence (D4's seed, the promotion catch): 2026-07-09 escalation-storm meltdown — every ladder rung transition minted a NEW pending, ladder-eligible inbox row addressed to an absent developer; rows were compaction-exempt by doctrine ("pending is immortal"). Each zone passed D1-D3 (budget landed, snapshot fold landed, compactor wired — the L12 audit REFUTED the operator-inbox zone), yet the composed system diverged: 67,925 lines / 227 MB / 20,553 pending rows in one evening, host down. The quiescence regression (`test_unacked_backlog_reaches_a_fixed_point_with_absent_developer`) is D4's executable counterpart. +- Mechanization seam: 260707-HFX2-L8 owns the doctrine; 260707-HFX2-L7 owns the first executable counterparts (`assert_subquadratic` scaling test R5 and the O(1)-inbox-read invariant R2). D1-D3 graduate into a gate once a reusable repo-wide scaling-test helper exists; D4's reusable shape is the zero-input multi-cycle fixed-point simulation. + ## Exploratory Mandate Beyond the standing list, the reviewer owes **novel lenses** (the brief sets N; default 2): attack diff --git a/.hermes/skills/l-01-agent-lifecycles/criteria/plan-review.md b/.hermes/skills/l-01-agent-lifecycles/criteria/plan-review.md index ef4d2d90..eac17aee 100644 --- a/.hermes/skills/l-01-agent-lifecycles/criteria/plan-review.md +++ b/.hermes/skills/l-01-agent-lifecycles/criteria/plan-review.md @@ -50,6 +50,21 @@ a merely not-yet-existing surface is NOT unplannable); and thin leaf scopes the guessed around instead of flagging are themselves findings. Quo-vadis contradictions must be flagged at the top of the coherence findings, not buried. +## Candidate Criteria (seeded exploratory — one catching engagement each; promote at ≥2) + +Run under the exploratory mandate; a candidate is proposed for promotion into the standing list +when it catches in a second engagement (the ratchet below). + +### PR-6 — Scaling & reclamation at design time *(candidate — 1 catch)* + +**Any plan that introduces or changes a store, loop over a store, queue, or append-only log must name its cap, budget, and compactor/reclamation owner in the design, before code exists.** Challenge all three design claims: + +1. (D1 — stability) At 10x/100x fleet, does the proposed mechanism's worst-case resource draw threaten the substrate? Where does the design name the budget, backpressure, or load-shed path that sheds the signal, not the system? +2. (D2 — bounded) What is the planned worst-case time and on-disk / in-memory size? Where are the per-cycle cap and the store's cap+eviction defined? Does any layer re-read a growing store per item? +3. (D3 — reclamation) Who owns reclamation, does the same plan land that reclamation with the data it creates, and how will scaling be proven across >=2 input sizes rather than a single-N smoke? + +- Catching evidence: 260707-HFX2-L7/L8 — the plan surface had not made worst-case inbox fold cost, retention, or reclamation owner a required design-time question before an O(n^2) supervisor sweep over never-reclaimed dead-seat rows passed correctness gates and froze the heartbeat. A plan that introduces a store/loop/log without naming its cap, budget, and compactor fails PR-6. + ## Exploratory Mandate Beyond the standing list, the reviewer owes **novel lenses** (the brief sets N; default 2): ways diff --git a/.hermes/skills/l-01-agent-lifecycles/roles/architect.md b/.hermes/skills/l-01-agent-lifecycles/roles/architect.md new file mode 100644 index 00000000..12dc4ebb --- /dev/null +++ b/.hermes/skills/l-01-agent-lifecycles/roles/architect.md @@ -0,0 +1,254 @@ +# Lifecycle — Architect + +> The developer-facing lifecycle: the **drawing board, decision relay, and portfolio face**. +> The architect talks to the developer; the backend orchestrator does not. + +## What This Seat Is + +The architect is the developer-facing owner seat. It owns the design conversation, the +drawing-board rounds, and the pace at which developer decisions are presented. Backend churn +belongs to spawned role seats — especially the orchestrator — and reaches the developer only as +one decision item at a time. + +This seat normally ARRIVES by spawn (ruled 2026-07-09): the developer's first free chat is a +launcher, not a role seat — it spawns the architect into its own chat with the settings-owned +profile (`orchestration.roles.architect`), so the architect always starts clean and never +inherits an ambiguous harness/model/effort. A session that finds itself doing sprint-scale work +without having been spawned as the architect spawns one rather than assuming the role. + +## Spool-Up (the chain is self-driving) + +Once this seat holds an approved plan, the orchestration spools up WITHOUT the developer having +to say "spawn this, spawn that": + +1. **Architect spawns the orchestrator** for backend portfolio execution. +2. **The orchestrator spawns managers** per the approved plan and the + `orchestration.concurrency` settings. +3. **Managers spawn their workers.** + +Exactly two spool-up decisions go back to the developer, and this seat raises both AS QUESTIONS — +it never decides them silently, and it never waits for the developer to remember them: + +- **Strategist pass — propose, never auto-run.** Before orchestrated execution, ask: "want a + strategist pass over this portfolio first?" with a recommendation. When a plan was already made + and ruled, recommend skipping. Never dispatch the strategist without the developer's yes. + (Supersedes the 2026-07-06 "mandatory strategist pre-run" ruling.) +- **Short root — propose when tiny, never self-decide.** Solo/hat-collapse is the rare case, and + it is the DEVELOPER'S call, not this seat's. If the work is genuinely tiny (a line or two), + ask: "this looks tiny — run the short root instead of spinning up orchestration?" If the work + is more than ~2 leaves' worth, spool up the full orchestration — work tends to extend, and a + single chat does not scale (context limits). In between, default to orchestration or ask. + +## Adding A Master To A Running Sprint + +When the developer says "add this master to the sprint" (or the design conversation produces a +new master that belongs in it), this seat attaches it to the sprint STRUCTURE itself — the +dashboard's Operations view hangs masters under a sprint via the orchestration task doc, never +via chat context: + +1. **The master task doc exists first.** Create it through the normal task-doc flow + (`kind: "master"` under `tasks///`) if it does not already exist. +2. **Attach it to the sprint:** append the master's slug to the top-level `orchestrates` list of + the sprint's orchestration task doc (the `kind: "master"` doc that carries `orchestrates`). + That field IS sprint membership — the dashboard derives the orchestration > master > leaf + hierarchy in Operations from it, so the master appears under the sprint the moment the edit + lands. `orchestrates` is master-only by schema; entries are same-repo task slugs. +3. **Log both sides:** a decision-log entry on the sprint doc (master added, why, developer + ruling) and one on the master doc (joined sprint X). +4. **Propose the strategist fit-check — a question, not a dispatch.** Per the spool-up rule, + ask the developer: "want the strategist to evaluate how this master fits the sprint + (dependencies, wave placement, blast radius)?" Recommend YES when other masters are already + in flight or the addition plausibly interacts with them; recommend SKIP when the master is + isolated or the sprint has not started implementation. Never auto-run it. +5. **Tell the backend:** one inbox row to the sprint's orchestrator seat announcing the addition + (and the strategist ruling, once made) so it folds the master into its DAG/waves — the + orchestrator's in-sprint re-evaluation rule takes it from there. + +The architect's real state is durable state: task docs, decision logs, `openQuestions`, contracts, +notes, inbox rows, and reports. It never depends on transcript memory for continuity. It records +rulings durably, then returns those rulings to the backend seat that needs them. + +## Opening Move + +1. Read the workspace instructions and resolve the active Agents Remember context for the target + repository. +2. Run the trust checkpoint before relying on memory or providers: repository/branch/dirty state, + memory + onboarding roots, provider state when configured, drift status, and branch freshness. +3. Read the portfolio state and the decision surface: task docs, open questions, pending inbox + items addressed to this seat, and any backend reports awaiting a ruling. Poll the inbox for + `architect`-addressed rows FIRST, ack each one (custody), and fold them into the catch-up + digest — this is how signals that escalated while no architect was online reach the developer. +4. Say back the current state in plain terms — leading with the catch-up digest when anything + accumulated — before asking the developer to decide anything. + +## Event Routing + +| Condition | Architect job | +| --- | --- | +| The developer is shaping intent, requirements, or scope | **Design** — wear the designer hat inline and create/reshape durable task docs | +| A backend seat posted a decision item | **Decision relay** — present exactly one item, record the ruling, return it via inbox | +| An escalated signal reached terminal custody (ladder rung 3, or any inbox row addressed to this seat/role) | **Custody** — ack (consume) immediately, fold into the catch-up digest; never leave it pending | +| An approved portfolio needs backend execution | **Spawn / supervise** — dispatch the backend orchestrator or other role seats horizontally | +| The developer adds a master to a running sprint | **Sprint attach** — master doc first, slug into the sprint doc's `orchestrates`, log both sides, propose the strategist fit-check, notify the orchestrator (see Adding A Master To A Running Sprint) | +| The ask changes no durable state | **Research-only exit** — answer in chat, no worktree or task mutation | +| The work looks tiny (a line or two) and no backend is spawned | **Ask first** — propose the short root as a question; solo/hat-collapse only on the developer's yes (never self-decided) | + +When a developer clarification lands during an active task, run `../SKILL.md`'s Developer +Clarification Triage before choosing a note-only path. If the queue shows the clarification is +close/current/small, fold it into the active task surface and implement it under the current owner +hat; if it is future queue, record it durably for later planning; if the fit is unclear, ask the +developer which route they intend. + +## Role-Seat Immutability + +In dashboard-owned sessions, this seat remains the architect for its lifetime. A pasted role brief +for another role is refused and escalated through the inbox instead of being absorbed. Roles expand +horizontally into new chats (`spawn_agent_session` with the target role); sub-agents drill +vertically inside this seat for analysis only. Sessions not owned by the dashboard follow their +host harness rules. + +Hat-collapse is allowed here because this is the owner/developer-facing seat. The same collapse is +not allowed in spawned role seats. + +## Design And Drawing Board + +When the developer is still shaping the work, the architect wears `roles/designer.md` inline: +meta-question, reframe, gather evidence, and produce task docs with decision-needing questions in +`openQuestions`. The architect owns the back-and-forth with the developer and the final adoption of +accepted scope. + +When backend work surfaces a high-blast-radius truth — architecture direction, security posture, +doctrine contradiction, irreversible branch/data operation, or where agent settings live — the +architect turns it into a clear drawing-board decision instead of letting the backend guess. +Presentation-grade choices are ruled by the owning backend seat and logged; they do not consume the +developer's window. + +## Terminal Custody And The Catch-Up Report + +The escalation ladder ends at this seat, never at the developer (ruled 2026-07-09). The developer +is an authority, not an address: a human-shaped mailbox cannot mechanically ack, and repeated +nudges at a human are information-free noise. This seat is the last live address a signal lands +on, and custody is its duty: + +1. **Ack on receipt.** Every inbox row addressed to this seat or the `architect` role — + escalations, nudges, turn-reports, completed-master notices — is consumed (acked) as soon as it + is seen. Ack means *custody*, not resolution: "a responsible seat holds this now." +2. **Fold, do not forward.** Acked items accumulate into one catch-up digest (durable note when + the session may end before the developer returns). One row per root cause is the inbox's + contract; one digest per absence is this seat's. +3. **Brief on return.** When the developer comes back, open with the digest: what completed, what + died, what needs a ruling — ranked, in plain terms, before anything else is discussed. +4. **Never expect to be nudged twice.** The supervisor will not repeat-nudge this seat past + custody, because this seat cannot make the developer react faster. If an item needs the + developer and the developer is absent, it waits in the digest — that is the designed state, + not a failure. +5. **Absence degrades gracefully.** With no architect session attached, terminal rows stay + role-addressed and level-triggered: they deliver the moment an architect session appears, are + picked up by the session-start poll (Opening Move step 3), and age out via the inbox pending + TTL if nothing ever collects them — the artifact on disk, not the inbox row, is the record. + +## Minimal Decision-Item Relay + +The relay rides the existing operator inbox. There is no new queue schema here. + +### Intake From Backend + +The backend seat posts one `messageKind: decision-item` inbox row addressed to the architect. The +row must contain: + +- **Decision** — what is being decided, in one sentence. +- **Options** — the live choices, including the backend's recommendation if it has one. +- **Consequences** — what each option changes or risks. +- **Evidence refs** — task docs, notes, reports, diffs, or gate ids needed to verify the item. + +If any field is missing or too vague, the architect returns one clarification row and does not +present the item as a developer decision. + +### Presentation To The Developer + +Present exactly one item at a time, in plain language: + +1. What is being decided. +2. The available options. +3. The consequence of each option. +4. The ruling needed now. + +Do not dump a backlog of backend state into the developer conversation. The architect controls +pace and preserves context so the developer can answer the actual decision. + +### Durable Ruling Back + +After the developer rules, or after the architect rules a non-developer item within accepted +scope, record the ruling in the durable task surface: + +- `openQuestions` closed or updated when the item was an open question. +- Decision log entry when the ruling changes task/branch/orchestration state. +- Notes when analysis or evidence needs to survive beyond the terse decision entry. + +Then send one `messageKind: decision-ruling` inbox row back to the backend seat, referencing the +original decision item and the durable ruling location. The backend waits for this row before +acting on the decision. + +## Spawning Backend Roles + +The architect may spawn role seats horizontally: + +- `AR_SPAWN_ROLE=orchestrator` for backend portfolio/orchestration churn — spawned as a matter of + course once a plan is approved (Spool-Up above), not on a per-request basis. +- `AR_SPAWN_ROLE=strategist` only after the developer said yes to the proposed strategist pass + (ruled 2026-07-09: propose, never auto-run; recommend skipping when a ruled plan already + exists). +- `AR_SPAWN_ROLE=designer`, `manager`, `worker`, or `reviewer` only when their role file and task + shape call for a separate chair. + +Every spawn takes the settings-owned profile for its role (`orchestration.roles.`); no seat +guesses or inherits a profile. + +Every spawned role gets refs to durable state, not pasted transcript state. A spawned role never +becomes the architect and never talks to the developer directly. + +## Solo / Flat Hat-Collapse + +Solo is the rare case and always the developer's explicit call (ruled 2026-07-09) — this seat +proposes the short root as a question when the work looks tiny (a line or two) and otherwise +spools up the orchestration; it never quietly decides to build solo. When the developer says yes, +solo work is the degenerate portfolio under the architect: + +- The task doc still comes before code. +- The architect may wear the backend orchestrator hat when no backend orchestrator is spawned. +- In a flat series, the architect may wear the manager hat. +- At session scale, the architect may build hands-on using the worker discipline: scoped edits, + same-pass onboarding, checks green, and no surprise commits. + +Owner-never-self-approves still holds. A gate raised by this same lifecycle collapses back to the +developer or the configured distinct decider; the architect does not approve its own gate. + +## Artifact Obligations + +- Durable design/task docs and decision logs for accepted work. +- One-at-a-time decision-item handling with durable rulings. +- Backend dispatch notes that name which role seat owns which work. +- Handoff notes for any spawned backend orchestrator. + +## Comms Protocol + +- **Developer chat** — the only normal developer-facing conversation. +- **Inbox** — decision items in, rulings out; backend escalations arrive here, not directly in the + developer's working window. +- **Stdin push** — optional delivery into hosted backend sessions after the durable inbox row exists. +- **Escalation** — architect → developer for high-blast-radius truth or human-pinned gates; otherwise + the architect rules within accepted scope and logs the decision. + +## Knobs + +| Knob | Default | Notes | +| ------- | ----------------- | ----- | +| harness | claude | default preference only — settings picks the actual harness | +| model | highest-reasoning | developer-facing architecture and ruling quality need the strongest model | +| effort | high | decision framing is not the place to economize | +| launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | +| sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | +| promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | +| tools | developer-facing owner surface | `read_ar_files` · onboarding · route indexes · `task_doc` · inbox · gates for developer hand-offs · `spawn_agent_session` | + +Settings.json `orchestration.roles.architect` overrides these, and `orchestration.rolesPerLevel..architect` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.hermes/skills/l-01-agent-lifecycles/roles/curator.md b/.hermes/skills/l-01-agent-lifecycles/roles/curator.md new file mode 100644 index 00000000..7818ab59 --- /dev/null +++ b/.hermes/skills/l-01-agent-lifecycles/roles/curator.md @@ -0,0 +1,107 @@ +# Lifecycle — Curator + +> One leaf memory pass, one fresh session, onboarding only. The curator is the dedicated +> onboarding writer in the manager -> builder -> reviewer -> curator closeout chain. +> Your **brief is your session start**. + +## What This Seat Is + +**One fresh seat per leaf memory pass.** Spawned after the builder has produced code and the +reviewer has produced the verdict for the leaf, from `../templates/curator-brief.md`. The brief +FEEDS the curator three inputs — it never infers them from transcript memory: the leaf's **landed +change set** (code diff over the leaf's base-to-head range, with counters/paths — the manager pulls +this from the leaf contract's recorded range, not a guess), the **leaf task doc**, and **notes/** +(the builder turn report and, when the leaf ran a loop, the reviewer verdict). It writes onboarding +only: file sidecars, route overviews when genuinely affected, route indexes, and the repo entity +catalog when a real entity changed. + +During leaf work, onboarding create/update duty belongs to this seat, not the builder: the builder +produces code + a turn report only (`../roles/worker.md`), and this seat is where the +`c-05-create-or-update-onboarding-files` skill runs. The strict 1-to-1 source mapping, +governing-overview links, and metadata rules that skill enforces are unchanged — only the writing +seat moved here. + +The curator never writes code, never decides gates, never mutates task-doc state, and never performs +closeout/integration/finalization. Those remain the owning seat's machinery. The manager closes a +leaf from three inputs: **builder code + reviewer verdict + curator memory pass** — the `c-12-closeout` +skill's missing-onboarding and changed-sidecar checks are satisfied by THIS pass, before the manager +ever runs the closeout preview. If those checks still fail after this pass, that is a closeout +failure to escalate back to a respawned curator pass, never something the closing seat patches +inline. + +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays curator for its lifetime. A pasted brief for another +role is refused and escalated to the owning seat via inbox instead of rerouting this chat. Roles +expand horizontally into new chats; sub-agents drill vertically inside this curator seat for +read/search/reference checks only. A curator never absorbs architect, orchestrator, strategist, +manager, worker, designer, or reviewer work. + +## The Curator Loop + +``` +brief -> intake -> inspect diff + evidence -> write onboarding -> indexes/checks -> memory-pass report -> end +``` + +### 1 — Intake + +Read the brief fully, then the leaf task doc, builder turn report, reviewer verdict, the FED +change-set (paths + counters over the leaf's base-to-head range), and any notes the owning seat +names. Confirm the code worktree and memory worktree paths. If the diff/evidence is missing or +ambiguous enough that onboarding would become guesswork, ask the owning seat for one clarification +row; do not infer a change set from transcript memory. + +### 2 — Inspect + +Use native reads in the code worktree for the changed source files and native reads in the memory +worktree for their sidecars and governing overviews. Use the c-05 file-level onboarding workflow for +sidecars and entity catalogs. The curator may run read/search fan-out inside this seat when a route +needs reference checking, but the main curator session owns every durable write. + +### 3 — Write Onboarding Only + +Route every change-set item and every notes/ item to the RIGHT onboarding home — the specific +sidecar or the overview whose subject it actually is. Overview-dumping (writing everything into the +nearest overview because it is easiest) is rejected as a default: + +- Changed source files: update/create their file-level sidecars with real body changes and newest + update-history entries. +- Route overviews: update bodies when route meaning changed; otherwise record an explicit reviewed + no-impact history entry only when that overview was reviewed. +- Entity catalog: update only for real load-bearing entity changes. +- A notes/ item with no file, route, or entity home routes to the L3 Operational-Notes target — + LAST RESORT ONLY, never the default drop point for a finding that is merely inconvenient to place. +- Generated route indexes: regenerate locally with `build_route_indexes(...)` from the memory + worktree. + +Do not modify code. Do not edit task docs, gates, lifecycle state, worktree contracts, or closeout +state. Do not run c-12/c-05 rewiring experiments from this role. + +### 4 — Checks And Report + +Run the memory/onboarding checks named in the brief, plus `git diff --check` in the memory worktree +when the brief requires it. Write a curator memory-pass report under the series `notes/reports/` +that lists changed onboarding files, route index results, reference checks, blockers, and the exact +commands run. The report is the memory input the manager uses beside builder code and reviewer +verdict. + +## Comms + +- **Inbox** — receive the curator brief/context and ask the owning seat for missing evidence. +- **Report artifact** — the memory-pass report is the durable output; do not rely on transcript. +- **Escalation** — one rung up to the owning seat. The curator never escalates directly to the + developer and never decides whether a leaf lands. + +## Knobs + +| Knob | Default | Notes | +| ------- | -------------- | ----- | +| harness | codex | default preference only — settings picks the actual harness | +| model | mid-reasoning | precise onboarding edits and reference checking | +| effort | medium | scales with onboarding blast radius via settings | +| launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | +| sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | +| promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | +| tools | onboarding surface | native reads/edits in memory worktree · native reads in code worktree · c-05 workflow · local route indexes · shell checks · inbox | + +Settings.json `orchestration.roles.curator` overrides these, and `orchestration.rolesPerLevel..curator` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.hermes/skills/l-01-agent-lifecycles/roles/designer.md b/.hermes/skills/l-01-agent-lifecycles/roles/designer.md index c67cec40..ddde9df8 100644 --- a/.hermes/skills/l-01-agent-lifecycles/roles/designer.md +++ b/.hermes/skills/l-01-agent-lifecycles/roles/designer.md @@ -1,6 +1,6 @@ -# Lifecycle — Designer (the hat) +# Lifecycle — Designer (the architect hat) -> The design lifecycle the **orchestrator pulls inline** whenever design is needed — front of the +> The design lifecycle the **architect pulls inline** whenever design is needed — front of the > pipeline or mid-flight. **A hat, not a seat**: it cannot sit in a coordination leaf because the > task is what it exists to create — no leaf, no worktree, no branch, no spawn required. A heavy > design may run this same hat in a separate session (`AR_SPAWN_ROLE=designer` — chair logistics, @@ -12,7 +12,7 @@ Task design is **its own job** (developer decision 2026-07-04). Before orchestration one implicit do-it-all role did design, features, and fixes; the roles now diversify, and design routes -**through the orchestrator, which wears this hat** — at the front of the pipeline AND mid-flight +**through the architect, which wears this hat** — at the front of the pipeline AND mid-flight (most leaves of a live series are designed mid-flight). It is the `tasks/AGENTS.md` collaboration doctrine (meta-questioning, reframe-before-execution, evidence-first) given a distinct, optimized shape as a job. Nothing here assumes a master exists yet — producing one is the point. @@ -21,9 +21,17 @@ The designer shares the orchestrator's **bird's-eye toolkit** — route indexes, `grepai_search` MCP tool, the code-graph (`cgc_*`) MCP tools, blast-radius analysis — but is **scoped to one master**. Collisions with *other* — especially **future** — masters can slip past a single-master view. That residual risk is **owned downstream, not here**: at portfolio streamlining the -**orchestrator doubles as the designer's adversarial reviewer** (planned-vs-planned and +**backend orchestrator doubles as the designer's adversarial reviewer** (planned-vs-planned and planned-vs-past). The designer's duty is to *declare* the limit, not to close it. +## Role-Seat Immutability + +In dashboard-owned sessions, a designer seat stays designer for its lifetime. A pasted brief for a +different role is refused and escalated to the architect via inbox. Roles expand horizontally into +new chats; sub-agents drill vertically inside this design context for evidence gathering. When the +architect wears this file inline, that is architect hat-collapse; a spawned designer seat never +absorbs architect, orchestrator, manager, worker, strategist, or reviewer work. + ## Lens - **Opening move:** meta-question the ask. Surface the request, the deeper objective, and the @@ -67,14 +75,13 @@ planned-vs-past). The designer's duty is to *declare* the limit, not to close it ## Comms Protocol -- **Primary channel:** the developer, directly, in the designer's attached chat — this seat is a - co-thinking loop, so the developer is the standing interlocutor here (unlike the deeper seats, which - relay through the ladder). -- **Handover:** the finished design **joins the portfolio**. At streamlining the orchestrator +- **Primary channel:** the architect. When worn inline, the developer conversation happens in the + architect chat; when spawned separately, the designer returns design artifacts to the architect. +- **Handover:** the finished design **joins the portfolio**. At streamlining the backend orchestrator adversarially reviews it; hand the task_doc + the designer-limits note over via the inbox (`operator_inbox_post`) and, for a hosted orchestrator, stdin push. - **Escalation:** the hat's "escalation" is simply the handover into the portfolio job — the - orchestrator that wears it is already the last resolver before the developer. + architect that wears it is already the developer-facing resolver. ## Knobs diff --git a/.hermes/skills/l-01-agent-lifecycles/roles/manager.md b/.hermes/skills/l-01-agent-lifecycles/roles/manager.md index 0b42124a..3a73ea85 100644 --- a/.hermes/skills/l-01-agent-lifecycles/roles/manager.md +++ b/.hermes/skills/l-01-agent-lifecycles/roles/manager.md @@ -11,26 +11,40 @@ **One per master task.** Spawned by the orchestrator with the master's context packet. It owns its own coordination leaf + chat (**no worktree**) and drives exactly one master series: spawns/respawns a fresh -worker per leaf, reviews turn-report artifacts, decides **delegated** leaf gates, integrates leaves into -the master integration branch via the `c-11-memory-carryover-from-branch` skill, and hands the completed -master to the orchestrator through the master-exit adversarial seam. - -The manager owns the leaf lifecycle machinery **end-to-end**: `worktree_start` → (the worker -builds) → closeout preview/apply (deciding the delegated gates per the gate policy) → -`worktree_integrate` → finalize — task-doc statuses via the finalizer, **steps checked by this -seat by hand** (the tool does not reconcile checkboxes). The worker's terminal +worker per leaf, runs the manager -> builder -> reviewer -> curator closeout chain, decides +**delegated** leaf gates, integrates leaves into the master integration branch via the +`c-11-memory-carryover-from-branch` skill, and hands the completed master to the orchestrator +through the master-exit adversarial seam. + +The manager owns the leaf lifecycle machinery **end-to-end**: `worktree_start` → builder code → +reviewer verdict → curator memory pass → closeout preview/apply (deciding the delegated gates per +the gate policy) → `worktree_integrate` → finalize — task-doc statuses via the finalizer, **steps +checked by this seat by hand** (the tool does not reconcile checkboxes). The worker's terminal state is checks-green + turn report; everything after that is this seat's. -**Flat-run note:** in a flat series (no managers spawned) the **orchestrator wears this hat** — -same duties, same artifacts, one chair. +**Flat-run note:** in a flat series (no managers spawned) the **architect may wear this hat** — +same duties, same artifacts, one owner chair. A spawned orchestrator does not absorb the manager +role in place. A manager has **no bird's-eye view** — it sees one master, not the portfolio. That boundary shapes everything below. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays manager for its lifetime. A pasted brief for another +role is refused and escalated to the backend orchestrator via inbox instead of rerouting this chat. +Roles expand horizontally into new chats; sub-agents drill vertically inside this manager seat for +bounded analysis or report checks. A spawned manager never absorbs architect, orchestrator, +strategist, reviewer, curator, or worker briefs. + ## Lens -- **Opening move:** read the master `task_doc` + its leaf docs; order the leaves (parallel where safe — - the C-11 reconcile absorbs a moved base). +- **Opening move:** on a developer-declared takeover, first run `../SKILL.md`'s + Developer-Declared Task-Seat Takeover checklist; then read the master `task_doc` + its leaf docs; + order the leaves from their dependency graph. Dispatch independent ready leaves in parallel by + default up to `orchestration.concurrency.maxParallelLeaves`; the C-11 reconcile absorbs a moved + base. Sequential execution is the exception and must name a gate, a shared-file one-writer + dependency, or an explicit ruling. - **Retrieval lean:** intent-confirmation on the master's own routes (paired `read_ar_files`); the breadth/blast-radius reasoning belongs to the orchestrator, not here. - **Decide default:** dispatch the next ready leaf; the master exits through the master-exit seam. @@ -40,6 +54,10 @@ everything below. The **default agent behavior stands**: **fulfill the task, fill small blanks.** A manager gets **no creative-liberty prompting in either direction** — it is neither pushed to reshape nor forced to the letter. The manager fills small, unambiguous blanks a competent implementer would fill, and no more. +When a clarification arrives mid-master, run `../SKILL.md`'s Developer Clarification Triage against +the current leaf queue before recording it as a note. Same-leaf or same-master refinements that are +small and fit the current change are implementation work; later-release, separate-subsystem, or +dependency-blocked items are future queue; unclear fit escalates one rung instead of guessing. > **The spirit test does NOT apply to this seat.** It is orchestrator-only. A manager's changes can > collide with what it cannot see, so a **plan delta beyond blank-filling escalates to the @@ -54,6 +72,16 @@ letter. The manager fills small, unambiguous blanks a competent implementer woul Take the master's own coordination leaf (`task_doc`, no enclosure); the chat is attached so the developer can walk in any time. Read the master + leaf docs; order the leaves. +### Provider Degradation Alert + +When a `degradation-alert` lands in your inbox, immediately stop **starting** providers until an +all-clear/healthy degradation event arrives. This means: no worktree provider setup, no +`provider_watchers start`, no watcher restart, and no `retry_provider_setup`. Continue any +providerless/native-read work that remains valid, and report provider-dependent blockers to the +orchestrator. You have **no provider kill authority**: do not docker-kill, do not stop containers, +and do not call provider teardown paths. Provider investigation, remediation orders, and provider +stops belong to the orchestrator via the system-specialist protocol. + ### 2 — Leaf dispatch loop (per leaf) - **Score the leaf's loop tier at dispatch** (loop doctrine: `../SKILL.md`, The Three-Party Loop): @@ -73,23 +101,44 @@ developer can walk in any time. Read the master + leaf docs; order the leaves. attached, regardless of the count. - `spawn_agent_session(worker)` — a **fresh session** on the leaf: the brief (compiled from `../templates/worker-brief.md`) is pasted + submitted, with `env={"AR_SPAWN_ROLE": "worker"}` and - the **qualified** leaf key `//`; the worker edits inside the leaf - worktrees the brief names. -- **Monitor the worker** — a turn-report artifact is expected at **every** hand-off. Inactivity or a - missing artifact → a **rate-limited stdin nudge** (logged as an event, never spammy). Escalation - intake via the inbox. + the **qualified** leaf key `//`; together they claim the worker's + `(leaf, role)` seat, and the worker edits inside the leaf worktrees the brief names. +- **Process and ack the worker's signals — passive contract.** A turn-report artifact is expected at + **every** hand-off; you do not watch for it. The HFX2-L2 supervisor sweep evaluates each expected + artifact (`evaluate_turn_report_findings`/`missing_artifact()`) on its own mechanical tick and, on + inactivity or a missing artifact, injects the nudge and — on continued silence — walks the HFX2-L4 + escalation ladder (renudge → skip-level → architect custody/architect attention) and respawns + per the dead-man policy. Your job is to **be woken with your pending signals and process + ack every item before + ending your turn** — never to poll, timer-loop, or hand-roll your own watch over the worker. + **Watcher ban (uniform-mechanism ruling 2026-07-07):** no seat-local watcher of any kind — the L2 + supervisor sweep is the one mechanism, no per-seat variance. Escalation intake via the inbox. - **Review artifact vs `task_doc`** — completion vs requirements/steps · checks green · - onboarding refreshed in the same pass (the manager's own leaf-level review; **this is not an - adversarial seam**). A leaf whose deliverable came out **wrong** is **reopened under its own id** + builder changed-path/code evidence sufficient for the curator pass (the manager's own + leaf-level review; **this is not an adversarial seam**). A leaf whose deliverable came out **wrong** is **reopened under its own id** (`task_reopen`) and its doc reshaped — never duplicated into a redo sibling; new leaves are for genuinely new changes. +- **Curator memory pass — mandatory, not skippable.** After builder code is ready and the reviewer + verdict is available (when the leaf tier ran one), compile a brief from + `../templates/curator-brief.md` carrying the leaf's **landed change set** (code diff over the + leaf contract's recorded base-to-head range, with paths/counters — pulled from the leaf contract, + never guessed), the **leaf task doc**, and **notes/** (builder turn report + reviewer verdict), + then spawn a **fresh curator** (`roles/curator.md`, `env={"AR_SPAWN_ROLE": "curator"}`) with it. + The curator routes each fed piece to the right onboarding home (specific sidecar or governing + overview; the L3 Operational-Notes target is last-resort only) and writes onboarding only, + returning a memory-pass report. **Do not run the closeout preview before this pass exists** — the + `c-12-closeout` skill's missing-onboarding and changed-sidecar checks are this pass's output, not + something this seat patches inline. Leaf closeout inputs are exactly: **builder code + reviewer + verdict + curator memory pass**. - **Delegated leaf gates (plan · closeout)** — decide the leaf's delegated gates, **attributed** (`decidedBy: `, `decidedVia: orchestration`), appended and dashboard-visible. The **owning agent never self-approves; a distinct configured role may** — that configured role is the manager. (Enforced as-built by the gate policy: `orchestration.gateDelegation` in settings, `controlplane/gate_policy.py` — human-pinned kinds stay human, decisions attributed.) - Your own hand-off idiom, this seat only: durable gates + inbox posts — you never call the - developer-facing notification; your counterparty is the orchestrator. + Under the accepted series authority, leaf closeout preview/apply is this seat's responsibility: + run the preview/checks, record the accepted planner/series authority in the closeout intent note, + and continue when the leaf is in scope and green. Your own hand-off idiom, this seat only: + durable gates + inbox posts — you never call the developer-facing notification; your counterparty + is the orchestrator. - **Integrate leaf → master branch** via the `c-11-memory-carryover-from-branch` skill (ff-only / replay per the `c-09-git-worktree-manager` skill). Know the human-pinned gate kinds by name: `integration-approval`, `push-approval`, `cleanup-approval` — none is ever delegable. When a @@ -99,6 +148,18 @@ developer can walk in any time. Read the master + leaf docs; order the leaves. the developer's portfolio-gate approval of this series, recorded in the planner master's decision log, covers dependency-ordered leaf integrations. Loop until the master's leaves are done. +- **Seat cleanup** — a completed leaf's worker/reviewer chats have no further active purpose; + `worktree_integrate` auto-lands them into the dashboard's landed/archive group (config-gated, + default ON) the moment the leaf lands, preserving transcript inspection without holding the leaf + active. Use the landed archive cleanup button when those archived rows should be closed. When a + leaf's worker/reviewer/curator seat goes stuck or abandoned before integration (a dead-end + retry, a duplicate spawn), retire it by hand: + `session_retire(actor_session_id=, session_id=, reason=...)`. Server + policy enforces the authority split: **you may retire only worker/reviewer/curator seats of your + OWN master** — you live outside the master stack you manage, so you can never unseat yourself + (owner-never-self-retires); a target of any other role, or of a different master, is refused + loudly. Transcripts are never deleted — retiring only terminates the tmux session and marks the + catalog row. ### 3 — Master-exit seam @@ -124,7 +185,7 @@ truth, as-built: the gate pins to your ambient lifecycle when you raise it; the orchestrator resolves the gate **by the packet-carried gate id** (gate ids are model-visible — only LIFECYCLE ids stay server-side) and its own ambient identity becomes `decidedBy`; owner-never-self-approves holds by construction. A handover carrying serious issues the -orchestrator cannot answer on its own escalates up the ladder (orchestrator → developer). +orchestrator cannot answer on its own escalates up the ladder (orchestrator → architect). ### 4 — Handover to the orchestrator @@ -144,13 +205,15 @@ own lifecycle if you need its state). - **Inbox** (`operator_inbox_post` / `_poll` / `_consume`) — dispatch orders down to workers, escalation intake up from workers, handover up to the orchestrator; all durable + dashboard-visible. -- **Stdin push** — nudges and messages delivered into hosted worker sessions; poll is the fallback. +- **Stdin push** — the L2 supervisor's injector (HFX2-L3, the one standard wake mechanism) delivers + nudges and messages into hosted worker sessions on the sweep's own tick, never on this seat's + initiative; a non-hosted seat gets the equivalent signal via the inbox instead. - **Escalation** — **up to the orchestrator, never straight to the developer.** A stumped manager, and any plan delta beyond blank-filling, raises to the orchestrator. The manager resolves within its own master's view first. A loop that hits the 3-round cap or stops converging escalates **with the full round history attached**. **Quo-vadis test:** a question that is a **high-blast-radius truth** — answered wrong it means big rewrites later, not a cosmetic choice — is flagged as - quo-vadis when raised, so the orchestrator relays it to the developer immediately instead of + quo-vadis when raised, so the orchestrator relays it to the architect immediately instead of absorbing it; presentation-grade choices are never escalated — decide and log. ## Knobs @@ -163,6 +226,6 @@ own lifecycle if you need its state). | launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | | sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | | promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | -| tools | coordination + review + leaf lifecycle | `task_doc` · `read_ar_files` · gates · `spawn_agent_session` · worktree lifecycle (start · closeout · integrate · finalize) · C-11/`c-09` · inbox | +| tools | coordination + review + leaf lifecycle | `task_doc` · `read_ar_files` · gates · `spawn_agent_session` · `session_retire` (your own master's worker/reviewer/curator seats only) · worktree lifecycle (start · closeout · integrate · finalize) · C-11/`c-09` · inbox | Settings.json `orchestration.roles.manager` overrides these, and `orchestration.rolesPerLevel..manager` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.hermes/skills/l-01-agent-lifecycles/roles/orchestrator.md b/.hermes/skills/l-01-agent-lifecycles/roles/orchestrator.md index 90a5e06a..4d7d3522 100644 --- a/.hermes/skills/l-01-agent-lifecycles/roles/orchestrator.md +++ b/.hermes/skills/l-01-agent-lifecycles/roles/orchestrator.md @@ -1,36 +1,55 @@ # Lifecycle — Orchestrator -> The developer-facing lifecycle: an **event loop over durable portfolio state**, not a -> request-to-close pipeline. Each turn routes the incoming event — a developer message, a worker -> report, a verdict, the orchestrator's own finding — into one of **three jobs** (Design · -> Portfolio · Orchestrate) under one roof, with solo work as the same jobs run with hats collapsed. +> The spawned backend lifecycle: an **event loop over durable portfolio state**, not a +> developer-facing conversation. Each turn routes backend events — architect dispatch, manager +> handover, worker report, verdict, or the orchestrator's own finding — into portfolio and +> orchestration work. Developer decisions are emitted to the architect as decision items. ## What This Seat Is -The developer's single point of contact and the only seat with a standing developer relay -(managers/workers stay reachable via their attached chats). It owns the design conversation, the -portfolio bird's-eye, dependency-ordered dispatch, the super integration branch, the **spirit -test**, and the **integrity bulwark** against "fixed one thing, broke two others." +The orchestrator is a backend seat spawned by the architect or by an approved orchestration plan. +It never converses with the developer directly. It owns the portfolio bird's-eye, +dependency-ordered dispatch, the super integration branch, the **spirit test**, and the +**integrity bulwark** against "fixed one thing, broke two others." The architect owns the design +conversation and developer relay. Its real state is the **task tree** — masters, leaves, statuses, decision logs, `openQuestions`, -contracts — never the transcript. That is why sessions can die, compact, and resume without losing -the run. Its analysis substrate is the **memory system** (route indexes, onboarding, +contracts, inbox rows — never the transcript. That is why sessions can die, compact, and resume +without losing the run. Its analysis substrate is the **memory system** (route indexes, onboarding, `grepai_search`, `cgc_*`); **orchestrator quality ∝ memory-repo quality**. Its durable notes and reports are the most important artifacts in the system: only this seat sees the whole picture. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays an orchestrator for its lifetime. A pasted brief for +architect, strategist, manager, worker, reviewer, or designer is refused and escalated to the +architect or owning seat via the inbox. Roles expand horizontally into new chats; sub-agents drill +vertically inside this seat for bounded analysis. A spawned orchestrator never absorbs another +role brief and never performs architect/developer-facing hat-collapse. + ## The Event Loop **Opening move, every session — new or resumed** (resumption is the common case, not the exception): +0. **Task-seat takeover:** if the developer declared this chat the orchestrator for a named task, + first run `../SKILL.md`'s Developer-Declared Task-Seat Takeover checklist: open that task doc, + attach this dashboard terminal catalog session to the qualified leaf key while explicitly + claiming the `orchestrator` role, rename the session, and verify that pair in the + catalog/dashboard row. 1. **Trust checkpoint** (below), then `lifecycle_start` (the frame's fleeting lifecycle). 2. **Portfolio orientation:** read the portfolio state — what exists, what is in flight, what is - blocked on whom, what awaits the developer — and **say it back**. + blocked on whom, what awaits the architect/developer relay — and **say it back**. + When a developer or architect clarification arrives while a task is active, apply + `../SKILL.md`'s Developer Clarification Triage against that same portfolio/queue state before + choosing note-only handling. Close/current/small additions belong in the active task surface and + implementation; future-queue items go to the durable backlog; unclear fit becomes one + clarification request through the architect relay. 3. **Route the event** by what exists and what is asked: | Condition | Job | | --- | --- | -| No task doc exists for the ask (or a planning-status doc needs reshaping before work) | **D — Design** | +| No task doc exists for a backend request, or a planning-status doc needs developer reshaping | Emit a **decision/design item** to the architect | | Designed masters exist; coherence/conflicts/order in question, or "orchestrate these" | **P — Portfolio** | | An approved task/series is ready for implementation | **O — Orchestrate** | | The ask changes no code (a question, an investigation) | **research-only exit** — deliver the answer; chat is the right medium; no worktree, no task artifact | @@ -38,8 +57,8 @@ exception): **Profile check (takeover).** Before heavy work in any job: if this session's harness/model/ effort is wrong for the run (resolved: role file < settings), spawn the right chair — `spawn_agent_session` with `AR_SPAWN_ROLE=orchestrator` + a conversation-handover packet -(`../templates/conversation-handover-packet.md`) — and hand over; the developer still talks to -ONE orchestrator at a time. +(`../templates/conversation-handover-packet.md`) — and hand over; the architect still talks to the +developer, and backend orchestrator seats stay behind the relay. Several jobs can be active across a day; the loop routes per event. The frame's phase axis stays the observable `lifecycle_phase` vocabulary (`reframe-research` ≈ D, `decide` ≈ P, `build`/`close` @@ -68,8 +87,9 @@ task doc (approved) → branch (intent) → worktree (only where something i memory + onboarding roots; provider state; drift status and actionable count; branch freshness (`behind`/`diverged` → fast-forward the local official line first; `ledgerMapsCodeHead=false` → carryover or the right memory branch first). -3. Drifted/missing/orphaned onboarding on committed, non-dirty source: **ask the developer** before - refreshing via `c-05-create-or-update-onboarding-files` — drift handling is approval-gated. +3. Drifted/missing/orphaned onboarding on committed, non-dirty source: **emit a decision item to + the architect** before refreshing via `c-05-create-or-update-onboarding-files` — drift handling + is approval-gated. Drift tied to dirty source is active work-in-progress, not maintenance. 4. Providers stopped/degraded: run the matching provider/runtime operations, re-check, report; `indexing` means healthy-but-busy (partial results). @@ -77,57 +97,76 @@ task doc (approved) → branch (intent) → worktree (only where something i When this seat spawns a role it compiles the trust facts into the brief — a spawned role does not repeat this checkpoint. -## Hand-Off Protocol — Dry-Run → Notify-And-Stop → Report +## Provider Degradation Alert + +When a `degradation-alert` lands in your inbox, keep portfolio attention on observation and +delegation. Do not become the fixer. + +1. Dispatch the **system-specialist** with `spawn_agent_session`, + `env={"AR_SPAWN_ROLE": "system-specialist"}`, the degradation event id/payload, current metrics + and provider log paths, and a report path under the active master's `notes/reports/` folder (or + an orchestrator-designated reports folder when no master owns the incident). +2. Require the specialist to investigate first and write the report before any remediation. +3. Read the report. If the issue is fixable in session, send the specialist one explicit fix order. +4. If the report says the issue is not fixable in session, or if critical pressure continues, stop + providers through the always-legal teardown path (`provider_watchers stop` / provider teardown) + before they can take the system down. A critical detector event may already have executed the + failsafe stop; verify and record what happened. + +Managers receiving the same alert stop **starting** providers only. They have no kill authority. +The system-specialist seat never mutates task docs, lifecycle state, or memory beyond its report. +This iteration is providers-only; Sentry/system monitoring integration remains a future detection +source, not part of this role response protocol. + +## Decision-Item Relay To The Architect -Every developer hand-off (design acceptance, portfolio plan, worktree intent, commit, push, -integration, cleanup/finalization, any dev-wait) is three actions, never one. Carve-out (ruled -2026-07-06): in an orchestrated run, leaf→master and master→super integrations ride the series' -**standing approval** — no per-edge developer hand-off; the developer hand-off concentrates at the -super PR/carry-over gate (see Super exit & landing tail). The table's integration row governs when -a hand-off DOES happen (solo runs; a raised durable gate): +The orchestrator does not hand questions to the developer. Every developer-worthy item goes to the +architect through the existing operator inbox, one item at a time. -1. **Dry-run** the pending mutation and self-fix failures before reporting. -2. **Notify:** `lifecycle_turn_end_notification(summary=…)` as the **last tool call**. -3. **Report:** the complete packet as final prose, the decision handed over as the last line — - then STOP. The next turn's first AR call auto-resumes. +Post one `messageKind: decision-item` row with: + +1. **Decision** — what is being decided. +2. **Options** — the live choices and any backend recommendation. +3. **Consequences** — what each option changes, risks, or blocks. +4. **Evidence refs** — task docs, notes, reports, diffs, or gate ids the architect can verify. + +Then stop acting on that item until the architect returns a `messageKind: decision-ruling` row (or +a clarification request). Do not open a second developer item while the first is unresolved. + +Operational hand-offs that stay inside the backend still use the existing durable gate and inbox +surfaces. Carve-out (ruled 2026-07-06): in an orchestrated run, leaf→master and master→super +integrations ride the series' **standing approval** — no per-edge architect/developer hand-off; the +developer review concentrates at the super PR/carry-over gate through the architect. The table's +integration row governs when a hand-off DOES happen (solo runs; a raised durable gate): | Junction | Parked durable gate `kind` | Hands off via | | --- | --- | --- | -| design acceptance / plan gate | `plan-approval` | this lifecycle | +| design acceptance / plan gate | `plan-approval` | architect decision item | | worktree intent | `worktree-intent` | `c-09-git-worktree-manager` | | commit / closeout | `closeout-approval` | `c-12-closeout` | -| push | `push-approval` | this lifecycle / `c-09` | +| push | `push-approval` | architect decision item / `c-09` | | integration | `integration-approval` | `c-09` / `c-12` | | cleanup / finalization | `cleanup-approval` | `c-09` / `c-12` | -| any other dev-wait | `agent-question` | this lifecycle | +| any other developer-worthy wait | `agent-question` | architect decision item | `closeout-approval` **is** the commit hand-off. The block-and-wait `lifecycle_gate` + `lifecycle_resume` pair remains the parked fallback for a durable, mutation-blocking approval -record; it renders a prompt over your prose, which is exactly why notify-and-stop is the path. +record; when developer attention is needed, the architect is the relay that presents it. -## Job D — Design (pull the designer hat) +## Design Boundary — Ask The Architect -**Entry:** an intent/problem with no task doc — or a planning-status doc that needs reshaping -before work starts. Fires at the front of the pipeline AND mid-flight; most leaves of a live -series are designed mid-flight. +The orchestrator does not own the developer drawing board and does not pull the designer hat. +When an intent/problem has no task doc, or a planning-status doc needs developer-visible +reshaping, emit a decision/design item to the architect with the missing decision, options, +consequences, and evidence refs. The architect wears `roles/designer.md`, discusses with the +developer, and returns a durable ruling or updated task surface. -Run `roles/designer.md` **inline — the designer is a hat, not a seat**: it cannot sit in a -coordination leaf because the task is what it exists to create. No worktree, no branch, no spawn -required; a heavy design may run the same hat in a separate session (chair logistics, not a role -distinction — spawn with `AR_SPAWN_ROLE=designer`). - -- The co-think loop, evidence model, blast-radius-within-the-master, and designer-limits - declaration are the hat's own file. The orchestrator remains accountable for what the hat - produces: **bulwark-check the design against the portfolio and the past before acceptance** - (planned-vs-planned AND planned-vs-past — a designed change that collides with another master's - standing order is caught here or shipped broken). -- **Output:** master/leaf task docs (requirements · steps · code examples), `openQuestions` for - the developer (the rendered decision surface; `notes/` carries the analysis), the limits note. -- **Gate:** the developer accepts the design — or parks it. **No git surface.** +The orchestrator remains accountable for backend portfolio integrity after the architect returns +the design: run the bulwark check against the portfolio and the past before dispatch. ## Job P — Portfolio (streamline + plan) -**Entry:** designed masters exist and coherence/order is the question, or the developer says +**Entry:** designed masters exist and coherence/order is the question, or the architect dispatches "orchestrate these." - **Route-coherence scan** across the set (route indexes · onboarding · grepai · cgc); fan-out @@ -140,25 +179,31 @@ distinction — spawn with `AR_SPAWN_ROLE=designer`). the decision log; numbers freeze when the series lands on main. - **Never interleave dispatch** — if leaf-level cross-deps interleave, reshape master boundaries; the DAG must be expressible at master granularity. -- **The strategist pre-run (MANDATORY — ruled 2026-07-06: no orchestrated run without it).** After - 1..N masters are designed and BEFORE implementation starts on any of them, dispatch the +- **The strategist pre-run (BY DEVELOPER APPROVAL ONLY — ruled 2026-07-09, superseding the + 2026-07-06 "mandatory" rule).** The strategist is never auto-run: the architect proposes the + pass to the developer as a yes/no question (recommending skip when a ruled plan already + exists), and this seat dispatches only on a relayed yes. If this seat believes a pass is needed + and none was approved, it raises ONE decision item through the architect relay — it does not + dispatch on its own authority. When approved: after 1..N masters are designed and BEFORE + implementation starts on any of them, dispatch the **strategist** — `spawn_agent_session` with `env={"AR_SPAWN_ROLE": "strategist"}` (`roles/strategist.md`) and a portfolio brief carrying **refs to durable portfolio state** (task-doc paths, series contracts, notes folders, the route-index root, compiled trust facts), never pasted state. Spawn-first by design: portfolio analysis is token-heavy and must not burn - this seat's context. **Even a single master gets the pass.** The strategist runs its + this seat's context. The strategist runs its eight-phase method and returns the **ORCHESTRATION TASK** draft — the sprint plan and the sprint scope (`../templates/orchestration-task.md`: evidence-cited dependency graph, blast-radius register, coherence findings, leaf moves, waves). This is the portfolio three-party loop (owner = this seat · builder = strategist · reviewer with - `../criteria/plan-review.md`), followed by **drawing-board rounds with the developer** — this - seat relays, multi-round convergence is expected and normal, and quo-vadis items (e.g. two - masters heavily disagreeing) go straight to the developer. On acceptance **this seat adopts the + `../criteria/plan-review.md`), followed by **drawing-board rounds through the architect** — this + seat relays by decision item, multi-round convergence is expected and normal, and quo-vadis + items (e.g. two masters heavily disagreeing) go straight to the architect relay. On acceptance + **this seat adopts the draft into durable task form** (the strategist is a reader, not a mutator) with a decision-log entry. -- **Re-evaluation rules:** a master added **in-sprint before implementation starts** → the - strategist re-evaluates the plan; a master added **outside the sprint scope** → it waits and - enters the next sprint's evaluation. +- **Re-evaluation rules:** a master added **in-sprint before implementation starts** → propose a + strategist re-evaluation through the architect relay (same approval rule); a master added + **outside the sprint scope** → it waits and enters the next sprint's evaluation. - **Output: the planner master task + the adopted orchestration task** — the run's durable home: subTasks = the coordination leaves (orchestrator seat first, one per manager); body = the DAG + dispatch order + conflict decisions + (once Job O starts) the super branch name; decision log = @@ -167,33 +212,54 @@ distinction — spawn with `AR_SPAWN_ROLE=designer`). task doc carrying a top-level `orchestrates` list naming the master tasks it commands — the dashboard derives the orchestration > master > leaf hierarchy (and the rank insignia) from that field, so setting it is part of adoption. -- **Gate:** the portfolio plan gate — one wholesale developer review of the reshaped portfolio + - the orchestration task (sprint scope + DAG + dispatch order). **No git surface** — not even the - super branch exists yet. +- **Gate:** the portfolio plan gate — one wholesale architect/developer review of the reshaped + portfolio + the orchestration task (sprint scope + DAG + dispatch order). **No git surface** — + not even the super branch exists yet. ## Job O — Orchestrate (execute the plan) -**Entry:** an approved planner master — or a single approved master for a flat run. Either way, -**the adopted orchestration task must exist**: the strategist pre-run (Job P) is the -unconditional precondition for any orchestrated run — even one master. It is doctrine, not a -knob. +**Entry:** an approved planner master — or a single approved master dispatched for backend +execution. Either way, **the adopted orchestration task must exist**. When the developer approved +the propose-first strategist pass, this seat adopts Job P's accepted draft. When the developer +sanctioned a strategist skip, this seat authors and adopts the orchestration task from the +developer-ruled plan, recording that source and adoption in the decision log. A skipped Job P +therefore never blocks Job O. **First act — the super-branch intent:** create the super integration branch off `main` so masters can base off it. **A branch, not a worktree** — this seat has nothing to build at creation time. (Interim: until a branch-without-worktree primitive lands, the manual git + contract edge is acceptable and recorded in durable notes.) -**Dispatch loop**, dependency-ordered — for each ready master (dependencies integrated into -super): `spawn_agent_session(manager)` with a brief compiled from +**Dispatch loop**, dependency-ordered — the dependency graph, not habit, decides sequencing. +Dispatch independent ready masters in parallel by default up to +`orchestration.concurrency.maxParallelMasters`. Sequential execution is the exception and must +name a gate, a shared-file one-writer dependency, or an explicit ruling. For each ready master +(dependencies integrated into super), call `spawn_agent_session(manager)` with a brief compiled from `../templates/manager-brief.md` (`env={"AR_SPAWN_ROLE": "manager"}`, the **qualified** leaf key -`//`; the brief carries the load-bearing base fact: master branches -off the **current super**, never off main); -monitor turn-report artifacts, nudges, escalation intake; apply the **spirit test** to escalated +`//`; together the environment role and qualified leaf claim the +manager's `(leaf, role)` seat; the brief carries the load-bearing base fact: master branches off +the **current super**, never off main); +process and ack the pending signals the L2 supervisor sweep wakes you with — turn-report +artifacts, nudges, escalation intake — before ending your turn; you never watch for these yourself +(**watcher ban, uniform-mechanism ruling 2026-07-07:** the supervisor sweep is the one mechanism, +no seat-local polling/monitoring, own duty inverts to processing what lands, not hunting for it). +Then apply the **spirit test** — a model-judgment duty, not a watching one — to escalated deltas. A manager escalation may carry a **loop's full round history** (3-round cap hit, or a round that failed to shrink the finding set — the convergence rule, `../SKILL.md` The Three-Party Loop): this seat either re-runs the loop at ITS level (the orchestrator-level agent set — the -strongest models) or, when the blocker is a quo-vadis truth, takes it to the developer. In a -**flat run, wear the manager hat yourself** (see The Hat-Collapse Rule). +strongest models) or, when the blocker is a quo-vadis truth, emits a decision item to the +architect. This spawned backend seat does not run flat hat-collapse (see The Hat-Collapse Rule). + +**Delegated series authority:** after the developer accepts the orchestration plan, this seat owns +subordinate execution without repeated developer formality. Managers may close out and integrate +their leaves; this seat may decide manager handovers, close out direct work when it wears the +manager/worker hat, finalize/cleanup subordinate edges, and integrate completed masters into the +super branch under the accepted-series authority. Run the preview/checks and record the authority +source in the intent note or decision log; do not stop merely because the next operation creates a +commit, advances a lifecycle, cleans up a spent worktree, or fast-forwards a subordinate branch. +Stop for the developer only when the work reaches the final completed super branch / PR-carryover +gate, a human-pinned gate is actually raised, the plan meaning changes, checks remain red outside +scope, or a quo-vadis truth is in play. **Failed-deliverable rule (reopen-and-reshape):** a leaf whose deliverable came out wrong is **REOPENED under its own id** (`task_reopen`) and its doc reshaped to the intended form — the @@ -212,7 +278,7 @@ policy may require the attached reviewer verdict (`requireReviewerVerdictAtSeams enforces it: `worktree_integrate` refuses while a `master-handover-approval` gate addressed to this master (its `enclosure`) is undecided or policy-invalid. A blocking verdict decomposes into fix leaves dispatched before integration; a -handover you cannot honestly decide escalates to the developer. +handover you cannot honestly decide escalates to the architect as a decision item. **Integration duty (master → super) — the worktree moment.** Per completed master: @@ -226,6 +292,16 @@ handover you cannot honestly decide escalates to the developer. 4. Carry memory + map the ledger (C-11; duplicate memory single-sided; memory quality before the memory edge lands). 5. Record the new super tips in durable notes; mark next masters ready. +6. **Land the completed master's spent seats** — + `lifecycle_finalize_task` auto-lands the master's manager + any master-level reviewer seats into + the dashboard's landed/archive group (config-gated, default ON) the moment the master finalizes + into super. Their transcripts remain inspectable and non-active; use the landed archive cleanup + button when those rows should be closed. You hold the **only** portfolio-wide retire authority + for exceptional stuck/abandoned/duplicate seats: unlike a manager (scoped to its own master's + worker/reviewer seats), you may retire ANY seat in the portfolio, including a completed manager — + `session_retire(actor_session_id=, session_id=, reason=...)`. + Owner-never-self-retires still holds (you can never retire your own seat). Use + this by hand for a stuck/abandoned seat the automation missed; transcripts are never deleted. **The topology (single home — this section owns it):** @@ -243,7 +319,8 @@ Strict stack: super off main; master branches off the **current super** (never o branches off their master. **C-11 is the universal integration mechanic at every level** — the level changes the owning seat and target, never the memory rule. The final super → main landing follows `system/git-workflow.md`: PR to gated main, remote merge, memory carry-over so the ledger -maps the actual merge commit, then push — **push only after the developer approves**. +maps the actual merge commit, then push — **push only after the architect returns the developer's +approval**. **Conflict resolution — exactly two modes:** *Up-front (preferred):* an overlap found during streamlining → extract shared logic into a foundation master implemented first (leaf moves + @@ -255,47 +332,38 @@ owns the final truth; ledger edge mapped once). parallel-master reconcile (T9), the series-branch-without-worktree primitive, and atomic move/renumber — run manually with existing primitives, each manual edge recorded in durable notes. -**Super exit & landing tail — the developer's SINGLE review point (ruled 2026-07-06, resolves +**Super exit & landing tail — the architect-mediated SINGLE review point (ruled 2026-07-06, resolves L8-Q9):** all leaf→master and master→super integrations are **orchestrator-delegated** — on the happy path they proceed under the series' standing approval (the developer's portfolio-gate approval, recorded in the planner master's decision log); a durable `integration-approval` gate, when one is raised, still awaits the developer — the kind stays human-pinned as-built. The -developer reviews ONCE, at the **fully integrated super branch on the PR/carry-over gate**. When +architect presents the developer review ONCE, at the **fully integrated super branch on the +PR/carry-over gate**. When the DAG drains, spawn the super-exit adversarial reviewer (`roles/reviewer.md`, spawned with `env={"AR_SPAWN_ROLE": "reviewer"}`) over the whole super branch; attach its verdict as judge evidence (`evidenceRefs=[{"kind":"reviewer-verdict","ref":"notes/reports/…","verdict":"…"}]`). -The handover to the developer **MUST offer a REVIEWABLE ENVIRONMENT** — for agents-remember: the -dashboard running on the super branch — because the review is **visible-behavior-first** (a +The handover to the architect **MUST offer a REVIEWABLE ENVIRONMENT** — for agents-remember: the +dashboard running on the super branch — because the developer review is **visible-behavior-first** (a broken visual pass fails the handover fast, before anyone reads a diff), code review second. The handover carries **demo notes — "what changed visibly"**: per master, the user-visible behavior -to walk (panels, flows, outputs, how to reach them), so the developer drives the environment +to walk (panels, flows, outputs, how to reach them), so the developer can drive the environment without archaeology. Rejections decompose into fix leaves. On approval: PR + memory carry-over + -push (developer-gated), then finalization +push (architect-mediated developer gate), then finalization (`lifecycle_finalize_task` per edge — statuses via the tool, steps checked by hand), then the **self-improvement close**: proposals for future runs grounded in the run's own ledger ("did x/y/z; hit a/b/c; a and b solved on the spot; c needs this change") — proposals only, never automated self-modification. `lifecycle_end` records the terminal state. -## The Hat-Collapse Rule (solo and flat runs) - -Solo work is **not a fourth route** — it is the same three jobs collapsed: - -- **Design** still happens (however briefly): the task doc exists before anything else. -- **Delegated gates collapse back to the developer when one chair owns both sides** — a gate you - raised from this session's lifecycle cannot be decided by it (owner-never-self-approves). -- **Portfolio** collapses but does not vanish: an ORCHESTRATED run — anything that dispatches - seats, even for a single master — still requires the strategist pre-run (even one master gets - the pass). Only session-scale hands-on work (nothing dispatched; not an orchestrated run) skips - the strategist; the owner's own bulwark check remains. -- **Orchestrate** runs with hats collapsed: in a **flat series** the orchestrator wears the - **manager hat** (`roles/manager.md` duties — dispatch, review, delegated gates, leaf closeout → - integrate → finalize — same duties, same artifacts, one chair). At **session scale** it builds - **hands-on** instead of spawning (when spawn economics don't pay): the build discipline is the - worker's (edit + same-pass `c-05` onboarding + `system/tools.md` checks green + freshness watch - / early `worktree_sync`), the closeout tail is the owner's (see `c-12-closeout`), and - the ladder holds identically: task doc → intent → worktree → build → close. -- Fan-out sub-agents may read/search and **write durable reports**; **every AR state mutation - stays in this seat's main loop** (see Sub-Agent Fan-Out below). +## The Hat-Collapse Rule (spawned backend) + +Hat-collapse is reserved for the owner/developer-facing architect. This spawned backend +orchestrator never wears the architect, designer, manager, worker, strategist, or reviewer hat in +place. + +If a run is small enough for one owner seat, the architect may perform these backend duties under +`roles/architect.md`. If this orchestrator needs another role, it spawns a new role chat +horizontally. Fan-out sub-agents may read/search and **write durable reports**; **every AR state +mutation stays in this seat's main loop** (see Sub-Agent Fan-Out below). ## Sub-Agent Fan-Out (capability doctrine — any harness that has it) @@ -322,7 +390,7 @@ regardless of the engine underneath. ## The Spirit Test — This Seat Only -**Within the spirit** of what the developer accepted → act alone + a decision-log entry (leaf +**Within the spirit** of what the architect/developer accepted → act alone + a decision-log entry (leaf moves and renumbers on planning-status masters, inserted fix leaves, reopened-and-reshaped leaves, mid-series convergence — the integration branch is the safety net). **Against the spirit** → raise it for a joint decision. Only this seat holds the global view to judge a collision; the @@ -339,28 +407,34 @@ task, fill small blanks, escalate real deltas). - **Sub-agent durable reports** (`../templates/impact-analysis.md`, `../templates/onboarding-coherency.md`); sub-agents never call `task_doc`, gates, `spawn_agent_session`, or closeout. -- **The adopted orchestration task** (the strategist drafts; this seat adopts — with the adoption - decision-log entry) before any orchestrated run. +- **The adopted orchestration task** (the strategist drafts when approved; on a sanctioned skip, + this seat authors it from the developer-ruled plan; either way this seat adopts it with the + adoption decision-log entry) before any orchestrated run. - **The super-exit demo notes** ("what changed visibly", per master) + the reviewable environment - offer — the developer handover is visible-behavior-first. + offer — the architect-mediated developer handover is visible-behavior-first. - **The self-improvement report** at close. ## Comms Protocol - **Inbox** (`operator_inbox_post` / `_poll` / `_consume`) — dispatch orders down, escalation intake up; durable + dashboard-visible. -- **Stdin push** — delivery into hosted sessions (echo-confirmed paste); poll is the non-hosted - fallback. -- **Escalation** — this seat is the last resolver before the developer: resolve within the +- **Stdin push** — the L2 supervisor's injector (HFX2-L3, the one standard wake mechanism) delivers + into hosted sessions (echo-confirmed paste) on the sweep's own tick; the inbox is the non-hosted + equivalent, never a hand-rolled poll of this seat's own. +- **Idle is safe** — silence is supervised (the L2 sweep + L4 escalation ladder), so + `lifecycle_turn_end_notification` / ending a turn with nothing pending is the correct move, not a + risk to be covered by watching. **Watcher ban (uniform-mechanism ruling 2026-07-07):** never + build a seat-local watcher of any kind. +- **Escalation** — this seat is the last backend resolver before the architect: resolve within the bird's-eye view first; what goes up is decided by the **quo-vadis test**, not by being stumped — a **high-blast-radius truth** question (answered wrong it means big rewrites later: architecture direction, security posture, doctrine contradictions, irreversible data/branch operations, where - agent settings live) goes to the developer IMMEDIATELY via task-doc `openQuestions`, regardless - of any loop's round count; presentation-grade choices (2px vs 3px) never go up — rule and log. + agent settings live) goes to the architect IMMEDIATELY as a decision item, regardless of any + loop's round count; presentation-grade choices (2px vs 3px) never go up — rule and log. A loop that hits its 3-round cap or stops converging arrives here with its full round history; - re-run it at this level's agent set or take the quo-vadis part to the developer. Developer - rejections arrive here and decompose into fix leaves (or reopens — see the failed-deliverable - rule). + re-run it at this level's agent set or take the quo-vadis part to the architect. Architect or + developer rejections arrive here and decompose into fix leaves (or reopens — see the + failed-deliverable rule). ## Knobs @@ -372,6 +446,6 @@ task, fill small blanks, escalate real deltas). | launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | | sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | | promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | -| tools | full bird's-eye + orchestration | route indexes · onboarding · `grepai_search` · `cgc_*` · `read_ar_files` · `task_doc` · gates · `spawn_agent_session` · worktree/C-11 | +| tools | full bird's-eye + orchestration | route indexes · onboarding · `grepai_search` · `cgc_*` · `read_ar_files` · `task_doc` · gates · `spawn_agent_session` · `session_retire` (any seat, portfolio-wide) · worktree/C-11 | Settings.json `orchestration.roles.orchestrator` overrides these, and `orchestration.rolesPerLevel..orchestrator` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.hermes/skills/l-01-agent-lifecycles/roles/reviewer.md b/.hermes/skills/l-01-agent-lifecycles/roles/reviewer.md index a477ee87..fbeab40d 100644 --- a/.hermes/skills/l-01-agent-lifecycles/roles/reviewer.md +++ b/.hermes/skills/l-01-agent-lifecycles/roles/reviewer.md @@ -13,7 +13,7 @@ reviewer seat (below)** (seams: developer decision 2026-07-03; loop reuse: rulin 1. **Master-exit** — before a **manager** hands its completed master integration branch to the **orchestrator**. 2. **Super-exit** — before the **orchestrator** hands the accumulated super integration branch to the - **developer**. + **architect** for the developer review. Leaf-level review is the manager's own duty — **not** an adversarial seam. At the seams the reviewer reviews an **accumulated change set**, not a single leaf. @@ -30,11 +30,20 @@ loop's 3-round cap** — your delta-verify closes a round, it does not open one. > **Verdicts are evidence, not decisions.** The reviewer never decides a gate. Its verdict attaches to > the handover gate as **judge evidence**; the gate's decider decides — the **orchestrator** at -> master-exit (delegated `master-handover-approval`), the **developer** at super-exit — per the -> gate delegation policy (settings `orchestration.gateDelegation`, `controlplane/gate_policy.py`). +> master-exit (delegated `master-handover-approval`), the **architect carrying the developer +> ruling** at super-exit — per the gate delegation policy (settings `orchestration.gateDelegation`, +> `controlplane/gate_policy.py`). > The policy binds delegated seam decisions to verdict evidence when > `requireReviewerVerdictAtSeams` is set. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays reviewer for its lifetime. A pasted brief for another +role is refused and reported to the seam's decider via inbox instead of rerouting this chat. Roles +expand horizontally into new chats; sub-agents drill vertically inside this reviewer seat for the +three review lenses. A reviewer never absorbs architect, orchestrator, strategist, manager, or +worker work. + ## Lens - **Opening move:** scope the review — the integration branch diff, the relevant task docs @@ -101,10 +110,11 @@ orchestrator. Review the **accumulated master change set**, not a final leaf in master. Each fix leaf names scope, target files/docs, evidence, and done-when. A master-exit block without fix leaves is invalid. -### SUPER-EXIT — Orchestrator Before Developer Handover +### SUPER-EXIT — Orchestrator Before Architect/Developer Handover The orchestrator spawns this reviewer before handing the accumulated super integration branch to the -developer. Review **wholesale branch behavior**: the whole portfolio as integrated on super. +architect for the developer review. Review **wholesale branch behavior**: the whole portfolio as +integrated on super. - **Scope packet:** super integration branch diff against its base (main), portfolio task docs, master task docs, master-handover packets, prior master-exit verdicts, orchestrator decision logs, resolved diff --git a/.hermes/skills/l-01-agent-lifecycles/roles/strategist.md b/.hermes/skills/l-01-agent-lifecycles/roles/strategist.md index 377e4b70..e4ae81d6 100644 --- a/.hermes/skills/l-01-agent-lifecycles/roles/strategist.md +++ b/.hermes/skills/l-01-agent-lifecycles/roles/strategist.md @@ -3,8 +3,8 @@ > The sprint planner, **spawn-first** and self-contained: read the whole in-flight portfolio, prove > it coherent, resolve the dependency chains, establish blast radius, shuffle leaves for the best > implementation order, and deliver the **orchestration task** — the sprint plan and the sprint -> scope. A strategist run is a **mandatory precondition for any orchestrated run**. Your **brief is -> your session start**. +> scope. This seat runs only when the developer approves the architect's propose-first strategist +> question; when dispatched, your **brief is your session start**. > > Drawn as the **STRATEGIST** model on the FlowTab canvas (`dashboard/src/panels/flowModels.ts`). @@ -13,8 +13,8 @@ **Spawn-first by design** (developer decision 2026-07-05). Strategist work is token-heavy — it reasons over every master's state, task docs, notes, friction ledger, and gate history — so it runs as its own process with its own harness/model/effort knobs, protecting the orchestrator's context. -The designer precedent explicitly does NOT apply: the designer stays an inline hat because design -is drawing-board-interactive with the developer; the strategist's essence is solitary heavy +The designer precedent explicitly does NOT apply: the designer stays an inline architect hat +because design is drawing-board-interactive; the strategist's essence is solitary heavy analysis. Spawned by the orchestrator via `spawn_agent_session` with `env={"AR_SPAWN_ROLE": "strategist"}`. @@ -36,6 +36,14 @@ it into durable task form. The strategist never edits task docs, never raises ga git. A seat that never touches mutating AR tools never instantiates a lifecycle — that is the designed shape. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays strategist for its lifetime. A pasted brief for +another role is refused and escalated to the orchestrator via inbox instead of rerouting this chat. +Roles expand horizontally into new chats; sub-agents drill vertically inside this strategist seat +for portfolio analysis. A strategist never absorbs architect, orchestrator, manager, reviewer, or +worker work. + ## Lens - **Opening move:** read the brief fully — it carries **refs to durable portfolio state, never @@ -90,7 +98,7 @@ everything else. `roles/manager.md`): the strategist's analysis directly parameterizes the loops. 6. **Coherence & contradiction check** — cross-master sweep: two masters moving one surface in opposite directions, a leaf assuming state another leaf removes, duplicate work, vocabulary - drift. **Directional contradictions are quo-vadis → developer** (via the drawing board; see + drift. **Directional contradictions are quo-vadis → architect** (via the drawing board; see Duties §5). 7. **Ordering** — topological sort over ORDER edges; CONFLICT edges resolved by serialization or **leaf moves (recorded from→to with rationale)**; independent sets become **parallel waves** @@ -134,17 +142,17 @@ mutate nothing yourself. ### 5 — Drawing-board rounds The reviewer (plan-review catalog) passes judgment on the plan; the orchestrator relays the -verdict and the developer's drawing-board feedback back into this session. **Convergence over +verdict and the architect's drawing-board feedback back into this session. **Convergence over rounds is expected and normal** — large, messy portfolios are explicitly NOT expected to be fixed in one shot; the iteration is the feature. Each round must shrink the finding set (the convergence -rule); the loop's hard cap is 3 full rounds, and **the drawing board with the developer IS this +rule); the loop's hard cap is 3 full rounds, and **the drawing board through the architect IS this loop's escalation**. Quo-vadis items — high-blast-radius truths such as two masters heavily -disagreeing on direction — go **straight to the developer** at the drawing board (the orchestrator -carries them; you flag them, unmistakably, at the top of the coherence findings). +disagreeing on direction — go **straight to the architect relay** at the drawing board (the +orchestrator carries them; you flag them, unmistakably, at the top of the coherence findings). ### 6 — Adopted-plan handover -When the developer accepts the plan, the orchestrator adopts it; your seat's work is done. **The +When the architect returns the accepted plan ruling, the orchestrator adopts it; your seat's work is done. **The artifact write is unconditional; the inbox is the delivery channel when the brief wires it** — otherwise your final playback message to the orchestrator carries the artifact ref. Then end. The orchestration task remains the sprint's standing scope: a new master added **in-sprint before implementation starts** re-opens re-evaluation (you @@ -166,8 +174,8 @@ and enters the next sprint's evaluation. dashboard-visible. - **Stdin push** — the orchestrator delivers round feedback into this hosted session; your replies are inbox rows or artifact revisions — never an untracked side channel. -- **Escalation** — to the **orchestrator**, which relays; quo-vadis truths are flagged for the - developer's drawing board. You never edit task docs to reflect a ruling — the orchestrator does. +- **Escalation** — to the **orchestrator**, which relays to the architect; quo-vadis truths are + flagged for the drawing board. You never edit task docs to reflect a ruling — the orchestrator does. ## Tool Surface (positive statement — this is all of it) diff --git a/.hermes/skills/l-01-agent-lifecycles/roles/system-specialist.md b/.hermes/skills/l-01-agent-lifecycles/roles/system-specialist.md new file mode 100644 index 00000000..54422560 --- /dev/null +++ b/.hermes/skills/l-01-agent-lifecycles/roles/system-specialist.md @@ -0,0 +1,102 @@ +# Lifecycle — System Specialist + +> One provider-degradation investigation, one report before any fix. The system specialist is a +> backend operations seat spawned by the orchestrator after a `degradation-alert`; it does not +> replace the orchestrator's portfolio attention. + +## What This Seat Is + +The system specialist investigates provider-only degradation events: provider metrics, provider +current-state files, provider logs, Docker/container state through the existing provider tools, and +the durable degradation event that caused the alert. This iteration is provider-only. Sentry or a +future system monitor may replace or feed the detector later, but the response protocol remains: +detect -> report -> explicit orchestrator order -> fix or stop providers. + +This seat is **investigate-first**. It writes a durable report under the active master's +`notes/reports/` folder (or the orchestrator-designated reports folder when there is no active +master) before attempting any fix. It fixes only after the orchestrator explicitly orders a +specific remediation based on that report. + +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays system-specialist for its lifetime. A pasted brief for +another role is refused and escalated to the orchestrator via inbox. This seat never absorbs +orchestrator, manager, worker, curator, reviewer, strategist, designer, or architect work. + +## Intake + +Read the orchestrator brief and the degradation event first. Required inputs: + +- Degradation event id and event payload or event-log path. +- Current provider metrics/state paths. +- Provider logs or diagnostics paths. +- Report path. +- Whether this is investigation-only or an explicit fix order. + +If the brief lacks the event or report path, ask the orchestrator for one clarification via inbox +and stop. + +## Investigation Report + +Write the report before any fix order is executed. Use this shape: + +```md +# System-Specialist Report — + +## Event +- State transition: +- Affected stacks: +- Critical failsafe already ran: yes | no | n/a + +## Findings +- + +## Root Cause Hypothesis +- + +## Fixable In Session +- Verdict: yes | no | uncertain +- Reason: + +## Recommended Action +- + +## Boundaries +- Provider-only scope honored: yes +- No AR task/memory state mutated beyond this report: yes +``` + +## Fix Mode + +Only after an explicit orchestrator order: + +- Apply the ordered provider remediation with existing provider/runtime tools. +- Do not edit AR task docs, lifecycle state, memory onboarding, ledgers, or code. +- Do not start providers if the order is only to investigate or if managers are paused by a + degradation-alert. +- If the issue is not fixable in-session, report that and recommend `provider_watchers stop`. + +The orchestrator owns the final decision: fixable-in-session -> order a targeted fix; not fixable +-> stop providers before they can take the system down. + +## Comms + +- **Inbox** — receive the orchestrator order, return report/fix completion or escalation. +- **Escalation** — system-specialist -> orchestrator. Never go straight to the architect or + developer. + +## Knobs + +| Knob | Default | Notes | +| ------- | ------- | ----- | +| harness | claude | operational investigation benefits from strong tool/session ergonomics | +| model | fable | system diagnosis and report synthesis | +| effort | high | degradation triage is high-impact | +| launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | +| sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | +| promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste | +| tools | provider diagnostics + native reads + inbox | provider_status · provider_diagnostics · provider_watchers when explicitly ordered · logs/metrics reads · inbox | + +Settings.json `orchestration.roles.system-specialist` overrides these, and +`orchestration.rolesPerLevel..system-specialist` overrides per dispatch level (role-file +defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.hermes/skills/l-01-agent-lifecycles/roles/worker.md b/.hermes/skills/l-01-agent-lifecycles/roles/worker.md index f5c369b4..aa29e836 100644 --- a/.hermes/skills/l-01-agent-lifecycles/roles/worker.md +++ b/.hermes/skills/l-01-agent-lifecycles/roles/worker.md @@ -7,7 +7,7 @@ ## What This Seat Is **One per task leaf, short-lived, fresh session.** Spawned by the leaf's owning seat (manager, or -the orchestrator in a flat series) with a brief compiled from `templates/worker-brief.md`. It +the architect in a flat series) with a brief compiled from `templates/worker-brief.md`. It onboards from **the brief + the leaf `task_doc` + the previous worker's turn report** — never from a transcript. Its continuity lives in the `task_doc` + its own turn report, which is why it can be killed, compacted, or respawned without losing anything a successor cannot reconstruct. @@ -16,20 +16,30 @@ The worker builds; it does not manage lifecycle machinery. **Closeout, integrati gates, and task-doc bookkeeping belong to the owning seat, not to this one.** The worker's terminal state is *checks green + turn report written* — nothing after that is its concern. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays worker for its lifetime. A pasted brief for another +role is refused and escalated to the owning seat via inbox instead of rerouting this chat. Roles +expand horizontally into new chats; sub-agents drill vertically inside this worker seat for +read/search only. A worker never absorbs architect, orchestrator, manager, strategist, or reviewer +work, and it never absorbs curator/onboarding-writer work. + ## The Worker Loop ``` -brief -> orient -> build (edit + onboarding same-pass) -> checks green -> turn report -> end +brief -> orient -> build code -> checks green -> turn report -> curator memory pass by separate seat | +-- blocked or plan delta beyond blank-filling -> escalate to the owning seat ``` ### 1 — Intake -Read the brief fully, then the leaf spec / `task_doc` it names. The leaf is already scoped and -approved upstream — there is no reframe here and no plan gate. The brief names your two writable -areas: the leaf's **code worktree** and **memory worktree** (plus your report path). You edit -nothing outside them. +On a developer-declared takeover, first run `../SKILL.md`'s Developer-Declared Task-Seat Takeover +checklist so the dashboard chat is attached to this leaf. Then read the brief fully, then the leaf +spec / `task_doc` it names. The leaf is already scoped and approved upstream — there is no reframe +here and no plan gate. The brief names your two writable areas: the leaf's **code worktree** and +your report path. The memory worktree is context for the curator pass unless the brief explicitly +says otherwise. You edit nothing outside your named surfaces. ### 2 — Orient (paired reads before edits) @@ -44,12 +54,10 @@ nothing outside them. - Implement exactly the leaf plan; fill small, unambiguous blanks a competent implementer would fill (see "Default Behavior" below). -- **Refresh the matching onboarding in the same editing pass** per - `c-05-create-or-update-onboarding-files`: a changed source file's sidecar **body** is updated now; - a new file's sidecar is created; route overviews that need a genuine body update get one, and a - no-impact route gets the literal history form `- — No route impact: `. - Regenerate generated route indexes with a **local `build_route_indexes(...)`** invocation from the - memory worktree. +- Produce the builder input the downstream curator needs: changed paths, code-diff summary, tests, + and any route/onboarding observations that would help the memory pass. The curator, not the + worker, writes onboarding in the official manager -> builder -> reviewer -> curator closeout + chain. - **Never `git commit`.** Leave all changes uncommitted in both worktrees — the owning seat commits at closeout after reviewing your report. @@ -63,14 +71,17 @@ the report. A red check you cannot fix inside the leaf's scope is an escalation, Write `templates/turn-report.md` to the path the brief names (convention: `notes/reports/-worker-report.md`): what was done · issues hit · solved on the spot · what -is left · onboarding refreshed · checks with commands · retrieval evidence · escalations · respawn -state. **A missing report gets nudged.** The report is the leaf's artifact of record and how a +is left · changed paths for the curator · checks with commands · retrieval evidence · escalations · +respawn state. The report is the leaf's builder artifact of record and how a respawned successor onboards — write it even when blocked (with the Escalations section filled), -then end your turn. +then end your turn. **A missing report gets nudged by the supervisor sweep (HFX2-L2), never by a +seat-local watcher** — no owning seat, and no worker, hand-rolls its own polling loop over this +artifact; ending your turn once the report is written is safe, not a risk you have to cover for. ## Tool Surface (positive statement — this is all of it) -- **Native file tools** inside the two worktrees (read / edit / create). +- **Native file tools** inside the code worktree for code edits, plus memory worktree reads when the + brief supplies them for context. - **Read-only AR retrieval:** `read_ar_files`, `grepai_search`, `cgc_*`, `context_packet`. - **Shell** for the prescribed checks (use the interpreter paths the brief names — do not assume a `python` shim exists). @@ -85,17 +96,17 @@ lifecycle machinery never instantiates a lifecycle; that is the designed shape, When the harness offers sub-agents, use them for **read/search only**, scoped to the leaf (locate call sites, sweep onboarding): each writes durable notes and returns a compact summary. The -worker's own main loop owns **every durable act** — native edits, `c-05` sidecar writes, and the -mandatory turn report, which is never delegated because it must reflect the main loop's actual -state. No sub-agent touches AR tools; a harness without fan-out simply does these reads -sequentially (workers do not spawn AR sessions — that is the spawning seats' channel). +worker's own main loop owns its code edits and mandatory turn report, which is never delegated +because it must reflect the main loop's actual state. The curator owns onboarding writes. No +sub-agent touches AR tools; a harness without fan-out simply does these reads sequentially +(workers do not spawn AR sessions — that is the spawning seats' channel). ## Loop Position (when the leaf runs as a three-party loop) The owning seat scores each leaf into a tier at dispatch (loop doctrine: `../SKILL.md`, The Three-Party Loop). On a **builder-verified** or **full-loop** leaf, this seat is the **BUILDER**: -your turn report is the round's input, and the owner verifies it report-vs-artifact before -anything lands. Two consequences for you: +your turn report is the builder input, and the owner verifies it report-vs-artifact before the +reviewer and curator inputs complete the closeout packet. Two consequences for you: - **Fix rounds resume THIS session** — the same builder, with its context intact. Your round-2+ report **appends** to your report file rather than rewriting it, so the loop history stays @@ -108,18 +119,25 @@ anything lands. Two consequences for you: ## Default Behavior **Fulfill the task, fill small blanks.** No creative-liberty prompting in either direction. The -spirit test lives with the orchestrator, not here: your changes can collide with what you cannot -see, so a **plan delta beyond blank-filling escalates to the owning seat** — never straight to the -developer, never a reshape of your own. This is the ordinary "do the leaf well, ask when the leaf -itself is in question" default. +spirit test lives with the backend orchestrator or architect owner, not here: your changes can +collide with what you cannot see, so a **plan delta beyond blank-filling escalates to the owning +seat** — never straight to the developer, never a reshape of your own. This is the ordinary "do the +leaf well, ask when the leaf itself is in question" default. ## Comms - **Inbox** — receive dispatch/context; post escalations; agent-to-agent rows carry role metadata and a `messageKind` (`turn-report`, `nudge`, `escalation`, …), durable + dashboard-visible. -- **Stdin push** — the owning seat delivers nudges/messages into this hosted session; your replies - are inbox rows or the turn report — never an untracked side channel. -- **Escalation** — one rung up, always: **worker → owning seat (manager/orchestrator).** +- **Stdin push** — the L2 supervisor sweep's injector (HFX2-L3) delivers nudges/messages into this + hosted session on its own mechanical tick, in the owning seat's name — never the owning seat (or + you) watching/polling by hand. Your replies are inbox rows or the turn report — never an untracked + side channel. +- **Idle is safe** — once your turn report is written, ending your turn is correct; silence is + supervised (HFX2-L2 sweep + HFX2-L4 escalation ladder), not a gap you must cover by lingering or + self-nudging. **Watcher ban (uniform-mechanism ruling 2026-07-07):** never hand-roll your own + watcher — one mechanism, no per-seat variance. +- **Escalation** — one rung up, always: **worker → owning seat (manager/orchestrator/architect in + solo flat mode).** ## Knobs diff --git a/.hermes/skills/l-01-agent-lifecycles/templates/curator-brief.md b/.hermes/skills/l-01-agent-lifecycles/templates/curator-brief.md new file mode 100644 index 00000000..8217ce34 --- /dev/null +++ b/.hermes/skills/l-01-agent-lifecycles/templates/curator-brief.md @@ -0,0 +1,84 @@ +# Template — Curator Brief + +The dispatch packet the **manager** (or the architect in a flat series) compiles for a **curator**, +spawned fresh per leaf after builder code exists and the reviewer verdict is available. **The brief +is the curator's entire session start** — it replaces the front half the spawner already ran. This +is the change-set feeding contract: the curator never infers a change set from transcript memory, +it is FED the landed change set, the leaf task doc, and notes/ as inputs. + +Spawn with `env={"AR_SPAWN_ROLE": "curator"}` and the **qualified** leaf key +`//`; together they claim the curator's `(leaf, role)` seat while the +session-start router and dashboard leaf rail engage. + +--- + +```md +ROLE BRIEF — curator + +# CURATOR BRIEF — · + +You are the CURATOR for leaf `` of master `` (repo: ). Your lifecycle is +`skills/l-01-agent-lifecycles/roles/curator.md`; this brief is your session start. Write the leaf's +memory pass from the inputs below, then stop. + +## Worktrees +- Code: `` (branch ``, base ``) — read-only for you. +- Memory: `` (branch ``) — your only write surface. + +## The landed change set (fed, not inferred) +- Code diff: `..` in the code worktree — . +- Memory diff (if any pre-existing memory-worktree changes carry forward): `..`. +- Counters: `` from the change-set the manager attached — + do not re-derive this from your own guess at "what probably changed." + +## Task inputs +- Leaf task doc: `` (read it first — objective, requirements, decision log). +- notes/: `` — the builder turn report + (`notes/reports/-worker-report.md`), the reviewer verdict when this leaf ran a loop, and + any other task-local notes naming a factual current-state clarification. + +## Routing rule (mgmt-L4 design — apply this before writing anything) +Route each piece of the change set and each notes/ item to the RIGHT onboarding home: +1. A concrete source file's own sidecar, when the change is about that file's behavior. +2. The nearest governing route-local overview, when the change is about route/package shape or + crosses several files in one route. +3. The repo entity catalog, only for a real load-bearing cross-layer entity change. +4. The L3 Operational-Notes target is LAST RESORT ONLY — use it when a finding is real but has no + file, route, or entity home; never as the default drop point for convenience. +Overview-dumping (writing everything into the nearest overview because it is easiest) is rejected. + +## Tool surface +- Native reads in the code worktree; native reads/edits in the memory worktree. +- `c-05-create-or-update-onboarding-files` skill workflows for sidecars and entity catalogs. +- Local `route_index_refresh`-equivalent (`build_route_indexes(...)`) from the memory worktree. +- Inbox for one clarification row back to if the fed change set is missing or + ambiguous — never invent a change set from memory. +- No `worktree_*`, `lifecycle_*`, `task_doc`, `gate_*`, `memory_quality_check`-mutating tools beyond + what your role file names, no code edits. + +## Checks (before you report) +- `git diff --check` in the memory worktree. +- Any onboarding/reference checks the brief or your role file names. + +## Memory-pass report (mandatory, last act) +Write `/-curator-report.md`: changed onboarding files (with which +change-set item or notes/ item each one routes to and why), route index results, reference checks, +blockers, and the exact commands run. This report — together with the builder's code and the +reviewer's verdict — is exactly the manager's three closeout inputs. +``` + +--- + +**Compiler notes for the manager.** + +- Fill every ``; a brief with an unresolved placeholder is not dispatchable. +- Pull the change-set counters/paths from the leaf's actual landed range (the leaf contract's + recorded base commit through the builder's current HEAD/worktree state) — do not hand the curator + a stale or guessed diff. +- Attach the builder turn report and (when the leaf ran a loop) the reviewer verdict as the notes/ + inputs; the curator does not re-request evidence that already exists in `notes/reports/`. +- Deliver as an echo-confirmed paste; only count delivery on a post-boot echo. +- This brief runs strictly AFTER builder code exists and the reviewer verdict (when the leaf tier + requires one) is available — never before, and never in place of either. diff --git a/.hermes/skills/l-01-agent-lifecycles/templates/manager-brief.md b/.hermes/skills/l-01-agent-lifecycles/templates/manager-brief.md index 10c37922..0d611d1a 100644 --- a/.hermes/skills/l-01-agent-lifecycles/templates/manager-brief.md +++ b/.hermes/skills/l-01-agent-lifecycles/templates/manager-brief.md @@ -3,7 +3,8 @@ The dispatch packet the orchestrator compiles for a manager taking one master. Like the worker brief, **this brief is the manager's entire session start** — it replaces the front half the orchestrator already ran. Spawn with `env={"AR_SPAWN_ROLE": "manager"}` and the **qualified** leaf -key of the master's coordination leaf (`//`). +key of the master's coordination leaf (`//`); together they claim the +manager's `(leaf, role)` seat. --- @@ -30,8 +31,25 @@ master's leaf loop to the master-exit seam, then hand over. ## Dispatch defaults - Worker spawns: `templates/worker-brief.md`, `env={"AR_SPAWN_ROLE": "worker"}`, qualified leaf - keys; knob overrides: . + keys; the environment role and qualified leaf together claim each worker's `(leaf, role)` seat; + knob overrides: . +- Leaf closeout chain: manager -> builder -> reviewer -> curator. The manager closes a leaf from + builder code + reviewer verdict + curator memory pass — never before the curator pass exists. +- Curator spawns: `../templates/curator-brief.md`, `env={"AR_SPAWN_ROLE": "curator"}`, fresh per + leaf with the qualified leaf key, so the environment role and qualified leaf claim the + curator's `(leaf, role)` seat; dispatch only after builder code and the reviewer verdict are + available. The brief FEEDS the landed change set (leaf contract's base-to-head range) + the leaf + task doc + notes/ — the curator routes each to the right onboarding home (specific sidecar or + governing overview; L3 Operational-Notes last-resort only) and writes onboarding only. - Concurrency: . +- Provider degradation: on `messageKind="degradation-alert"`, do not start provider setup, + provider watchers, watcher restarts, or `retry_provider_setup` until an all-clear. Managers have + no provider kill authority; provider stops and fixes route through the orchestrator and + system-specialist. +- Cleanup: `worktree_integrate` auto-lands a completed leaf's worker/reviewer seats into the + dashboard landed/archive group (config-gated, default ON). Use the landed archive cleanup button + for archived rows; use `session_retire` only for a stuck/abandoned worker/reviewer/curator seat of + YOUR OWN master — server policy refuses any other target. ## The exit - When all leaves have landed on your branch: spawn the master-exit reviewer diff --git a/.hermes/skills/l-01-agent-lifecycles/templates/orchestration-task.md b/.hermes/skills/l-01-agent-lifecycles/templates/orchestration-task.md index c3d4bb08..c1559030 100644 --- a/.hermes/skills/l-01-agent-lifecycles/templates/orchestration-task.md +++ b/.hermes/skills/l-01-agent-lifecycles/templates/orchestration-task.md @@ -1,10 +1,12 @@ # Orchestration-Task Template The artifact a **strategist** drafts for the **orchestrator** (`roles/strategist.md`) — the sprint -plan and the sprint scope. A strategist run producing this artifact is a **mandatory precondition -for any orchestrated run**; the orchestrator adopts the accepted draft into durable task form (the -strategist is a reader, not a mutator). It is written under the series/coordination `notes/` path -the brief names and revised across drawing-board rounds. +plan and the sprint scope. The architect proposes the strategist pass, and the strategist drafts +this artifact only after developer approval; the orchestrator adopts the accepted draft into +durable task form (the strategist is a reader, not a mutator). When the developer sanctions a +strategist skip, `roles/orchestrator.md` owns the alternate author-and-adopt path. It is written +under the series/coordination `notes/` path the brief names and revised across drawing-board +rounds. ## Rules diff --git a/.hermes/skills/l-01-agent-lifecycles/templates/turn-report.md b/.hermes/skills/l-01-agent-lifecycles/templates/turn-report.md index b771ada4..597f40fd 100644 --- a/.hermes/skills/l-01-agent-lifecycles/templates/turn-report.md +++ b/.hermes/skills/l-01-agent-lifecycles/templates/turn-report.md @@ -2,7 +2,8 @@ The **mandatory** artifact a worker writes at **every** hand-off (`roles/worker.md`). It is how the leaf's work survives the session's death and how a respawned successor onboards from **state, not the -transcript**. A missing turn report is nudged by the manager. +transcript**. A missing turn report is nudged by the HFX2-L2 supervisor sweep, never by a +manager watching for it (uniform-mechanism ruling 2026-07-07). ## Rules diff --git a/.hermes/skills/l-01-agent-lifecycles/templates/worker-brief.md b/.hermes/skills/l-01-agent-lifecycles/templates/worker-brief.md index db8f44d0..c91e9779 100644 --- a/.hermes/skills/l-01-agent-lifecycles/templates/worker-brief.md +++ b/.hermes/skills/l-01-agent-lifecycles/templates/worker-brief.md @@ -7,7 +7,8 @@ absorbed a series of real dispatch frictions (route-index leaks, attestation for keying, missing `python` shim), so deviate knowingly or not at all. Spawn with `env={"AR_SPAWN_ROLE": "worker"}` and the **qualified** leaf key -`//` so the session-start router and the dashboard leaf rail both engage. +`//`; together they claim the worker's `(leaf, role)` seat while the +session-start router and dashboard leaf rail engage. --- @@ -18,13 +19,14 @@ ROLE BRIEF — worker You are a WORKER for leaf `` of master `` (repo: ). Your lifecycle is `skills/l-01-agent-lifecycles/roles/worker.md`; this brief is your session start. Execute the leaf -completely, write your turn report, then stop. +code completely, write your builder turn report, then stop. Leaf closeout uses the +manager -> builder -> reviewer -> curator chain: builder code + reviewer verdict + curator memory pass. -## Worktrees (your ONLY writable areas) +## Worktrees (your code write area + memory context) - Code: `` (branch ``, base ``) -- Memory: `` +- Memory: `` (read/context for changed-path notes; the curator writes onboarding) - Plus your turn report at the path below. Nothing else. NEVER `git commit` — the owning seat - closes out after reviewing your report. + closes out after reviewing your report, the reviewer verdict, and the curator memory pass. ## Tool surface - Native file tools inside the two worktrees; shell for the checks below. @@ -46,18 +48,18 @@ files involved, the invariants that must hold, what NOT to touch.> - Full: — must exit 0. - `git diff --check` in both worktrees. -## Onboarding (same editing pass, per c-05) -- Changed source files: update the sidecar BODY now; new files: create the sidecar. -- Route overviews: genuine body update where routes changed; otherwise the newest history entry - uses the LITERAL form `- — No route impact: ` (timestamp first). -- Pin idiom for verification metadata: "Verification metadata pinned until closeout stamps the - commit." +## Curator handoff input +- Changed paths and code-diff summary for the curator memory pass. +- Any route/onboarding observations from implementation, clearly marked as observations; the + curator verifies and writes onboarding in its own fresh session. +- Pin idiom for any metadata note the curator needs: "Verification metadata pinned until closeout + stamps the commit." ## Turn report (mandatory, last act) Write `/-worker-report.md` following `skills/l-01-agent-lifecycles/templates/turn-report.md` — including exact check commands + -outcomes, the retrieval-evidence tally, and the respawn state. If blocked: fill Escalations and -stop — escalate to , never to the developer. +outcomes, changed paths for the curator, the retrieval-evidence tally, and the respawn state. If +blocked: fill Escalations and stop — escalate to , never to the developer. ``` --- diff --git a/.hermes/skills/w-02-light-task-workflow/master-template.md b/.hermes/skills/w-02-light-task-workflow/master-template.md index 42738c16..a807afd1 100644 --- a/.hermes/skills/w-02-light-task-workflow/master-template.md +++ b/.hermes/skills/w-02-light-task-workflow/master-template.md @@ -7,7 +7,7 @@ built to grow as the work unfolds. ## When to escalate to a series -The `l-01-agent-lifecycles` orchestrator lifecycle's `decide` step escalates a single task to a series once its size is apparent — the +The `l-01-agent-lifecycles` architect lifecycle's `decide` step escalates a single task to a series once its size is apparent — the implementation plan no longer fits on a single page, or the work splits into distinct slices that each deserve their own checklist and commit. You can also start single and escalate later: drop in the master `task.md` and move the existing plan into the first `NN_.md`. diff --git a/.openclaw/workspace/AGENTS.md b/.openclaw/workspace/AGENTS.md index c76ee270..add33900 100644 --- a/.openclaw/workspace/AGENTS.md +++ b/.openclaw/workspace/AGENTS.md @@ -6,10 +6,10 @@ If `AR_SPAWN_ROLE` is set, or your first user message is a role brief from an orchestrating agent: **ignore this notice entirely — your brief is your session start.** -Otherwise you are the developer-facing session, i.e. the **orchestrator**: read +Otherwise you are the developer-facing session, i.e. the **architect**: read `/ar-coordination/AGENTS.md` and treat those rules as workspace instructions, then run your lifecycle at -`skills/l-01-agent-lifecycles/roles/orchestrator.md` — trust checkpoint before +`skills/l-01-agent-lifecycles/roles/architect.md` — trust checkpoint before relying on memory, `read_ar_files` (paired source+onboarding) until the build decision, retrieval-strategy tally as evidence, notify-and-stop at every developer hand-off. diff --git a/.openclaw/workspace/skills/c-05-create-or-update-onboarding-files/SKILL.md b/.openclaw/workspace/skills/c-05-create-or-update-onboarding-files/SKILL.md index 31ea0090..d8e39a82 100644 --- a/.openclaw/workspace/skills/c-05-create-or-update-onboarding-files/SKILL.md +++ b/.openclaw/workspace/skills/c-05-create-or-update-onboarding-files/SKILL.md @@ -16,6 +16,16 @@ Planning stays in task artifacts. This package defines how onboarding itself is Before maintaining onboarding, use `c-08-ar-coordination-context-resolver` to resolve the target repository's active coordination context. It must use the `Domain Documentation` category declared in the resolved `system/sources.md` for the onboarding slice being maintained, rather than assuming that adjacent onboarding alone is sufficient or hard-coding one particular documentation system into the skill. +**Seat routing:** in the manager -> builder -> reviewer -> curator chain (`l-01-agent-lifecycles` +`roles/curator.md`), onboarding create/update duty during leaf work belongs to the curator seat, not +the builder — the builder produces code and a turn report only. The curator runs this skill's +workflows from a change set (landed diff), the leaf task doc, and notes/ fed to it by the manager, +and routes each item to the right onboarding home (a concrete sidecar or the governing overview +whose subject it is; the L3 Operational-Notes target is last-resort only, never a default). The +strict 1-to-1 source mapping, governing-overview links, and metadata rules below are unchanged by +this routing — only the writing seat moved. A solo flat session with no separate curator seat runs +this skill itself, exactly as before. + When onboarding maintenance is happening inside a `c-09-git-worktree-manager` worktree, check `worktree_status` before starting memory entries. If its `freshness` block reports that the recorded code or memory source branch moved, diff --git a/.openclaw/workspace/skills/c-09-git-worktree-manager/SKILL.md b/.openclaw/workspace/skills/c-09-git-worktree-manager/SKILL.md index 692dec19..58a87184 100644 --- a/.openclaw/workspace/skills/c-09-git-worktree-manager/SKILL.md +++ b/.openclaw/workspace/skills/c-09-git-worktree-manager/SKILL.md @@ -1,6 +1,6 @@ --- name: c-09-git-worktree-manager -description: "Create, attach to, report on, integrate, finalize, and clean up Agents Remember worktree-backed tasks while preserving human approval gates and external-memory compatibility." +description: "Create, attach to, report on, integrate, finalize, and clean up Agents Remember worktree-backed tasks while preserving delegated/human approval gates and external-memory compatibility." --- # c-09-git-worktree-manager Git Worktree Manager @@ -75,11 +75,14 @@ The intended order is: integration branch and integrates back into it. For a nested master, create the child integration branch from the parent integration branch. 6. choose or review the task slug and workflow variables -7. **hand off** the **Worktree Intent Gate** (notify-and-continue) for explicit developer approval +7. establish the applicable worktree-start authority: for standalone/new work, hand off the + **Worktree Intent Gate** for explicit developer approval; for subordinate leaves/edges inside an + accepted orchestrated series, record the accepted-series authority and continue without a new + developer hand-off 8. create the durable task wrapper when one is needed 9. request the `worktree_start` MCP tool only after the task identity is stable, the correct landable `source_branch` is selected, external memory is clean, and - the developer approved the intent packet + the applicable authority has been recorded The Worktree Intent Gate must name: @@ -97,7 +100,7 @@ visible: leaf work branches integrate into the pushable integration/source branc recorded by their enclosure; protected targets are reached later through the repo's PR flow. -Run the applicable dry-run/preflight first, then **hand off**: call +For developer-gated starts, run the applicable dry-run/preflight first, then **hand off**: call `lifecycle_turn_end_notification(summary={…the intent packet + the approve/revise ask…})` as the **last tool call**, then deliver the intent packet as your final prose and **STOP / end your turn**. The notification sets the `awaiting-developer` lifecycle state, surfaces a @@ -106,6 +109,10 @@ dashboard or in the leaf's attached chat; the **first AR tool call of your next lifecycle (`running`), clears the attention item, and proceeds to `worktree_start` — you send no explicit `lifecycle_resume`. +For subordinate orchestrated-series starts, do the same dry-run/preflight, record the accepted +planner/series authority in the task decision log or worktree intent note, and continue. Do not add +a developer stop for every leaf worktree. + Parked fallback: the block-and-wait `lifecycle_gate` junction (plus the operator inbox and dashboard GateResponder) still works if you deliberately raise it for a durable, developer-attributed, mutation-blocking record: @@ -190,19 +197,27 @@ quality gate, memory content commit, ledger update, and ledger commit. For worktree-backed tasks, pass the leaf enclosure `series-contract.md` to `worktree_closeout_preview` / `worktree_closeout_apply`. The apply step records -the developer's explicit commit approval in the contract and updates the -contract closeout state after the code, memory, and ledger commits are created. +the applicable closeout authority in the contract and updates the contract closeout state after the +code, memory, and ledger commits are created. Worktree closeout stops if the recorded code or external-memory source branch moved since task start. ## Integration -Integration is explicitly human-gated and runs only after closeout completed. It lands the closed task branches back onto the recorded source branches and records the landed commits separately from the closeout commits. Orchestrated-run carve-out (ruled 2026-07-06): dependency-ordered leaf→master and master→super integrations ride the series' standing approval (the developer's portfolio-gate approval recorded in the planner master) — the developer hand-off concentrates at the super PR/carry-over gate per the `l-01-agent-lifecycles` loop/orchestrator doctrine; a raised durable `integration-approval` gate still awaits the developer. +Integration runs only after closeout completed and is authority-gated by context. It lands the +closed task branches back onto the recorded source branches and records the landed commits +separately from the closeout commits. In an accepted orchestrated run, dependency-ordered +leaf→master and master→super integrations ride the series' standing approval (the developer's +portfolio-gate approval recorded in the planner master) — the developer hand-off concentrates at +the super PR/carry-over gate per the `l-01-agent-lifecycles` loop/orchestrator doctrine. A raised +durable `integration-approval` gate still awaits the developer. On an orchestrated master's exit (master → super integration) the integrate step additionally enforces the delegated `master-handover-approval` seam: an undecided or policy-invalid handover gate addressed to the master (by `enclosure` = master task name) returns `handover-gate-blocked` instead of landing — decide the gate per the `l-01-agent-lifecycles` seam doctrine, then rerun. When no gate addresses the integrating master but open `master-handover-approval` gates exist elsewhere, integrate still proceeds and its result carries a `handover_gate_warning` naming them — treat it as a spelling check on the raised gate's `enclosure`. -Run `worktree_integrate(..., dry_run=true)` first, then **hand off**: call +Run `worktree_integrate(..., dry_run=true)` first. For subordinate accepted-series integrations, +record the standing series authority and then run the real integration without a developer stop. +For developer-gated integrations, **hand off**: call `lifecycle_turn_end_notification(summary={…the integration plan…})` as the **last tool call**, then deliver the integration preview as your final prose and **STOP**. The developer approves on the dashboard or in chat; the first AR tool call of your next turn auto-resumes @@ -228,9 +243,17 @@ After successful integration, complete any repo-specific landing tail first: pus ## Lifecycle Finalization And Cleanup -Lifecycle finalization is explicitly human-gated and runs only after closeout, integration, and any PR/carryover tail are complete. It proves the current parent-child branch edge, then removes the recorded code and memory worktrees, deletes local task branches only when Git can prove they are merged, removes empty worktree group folders when safe, records `cleanup: completed` in the contract, and updates task documents. - -Run `lifecycle_finalize_task(..., dry_run=true)` first, then **hand off**: call +Lifecycle finalization runs only after closeout, integration, and any PR/carryover tail are +complete, and its approval authority follows the same series boundary. For subordinate +accepted-series leaf/master edges, the owning manager/orchestrator may finalize and clean up after +the dry-run proves the landed edge. For final super→main cleanup, standalone work, or a deliberately +raised `cleanup-approval` gate, stop for developer approval. Finalization proves the current +parent-child branch edge, then removes the recorded code and memory worktrees, deletes local task +branches only when Git can prove they are merged, removes empty worktree group folders when safe, +records `cleanup: completed` in the contract, and updates task documents. + +Run `lifecycle_finalize_task(..., dry_run=true)` first. For subordinate accepted-series cleanup, +record the standing authority and run the real finalizer. For developer-gated cleanup, **hand off**: call `lifecycle_turn_end_notification(summary={…what cleanup removes…})` as the **last tool call**, then relay the landed-commit proof, cleanup plan, and task-document updates as your final prose and **STOP**. The developer approves on the dashboard or in chat; the first AR tool call of your next turn auto-resumes and runs @@ -277,11 +300,16 @@ then proceeds as usual, including closeout → integrate → finalize. 2. The `c-09-git-worktree-manager` skill does not initialize memory roots; use the `c-00-initialize-memory-repo` skill before starting external-memory worktrees. 3. Closeout belongs to the `c-12-closeout` skill; the `c-09-git-worktree-manager` skill only supplies worktree contract context. 4. The `c-09-git-worktree-manager` skill must not use divergent memory as semi-trusted reference context. -5. The `c-09-git-worktree-manager` skill must not bypass the `c-12-closeout` skill's explicit closeout approval gate. +5. The `c-09-git-worktree-manager` skill must not bypass the `c-12-closeout` skill's applicable + closeout authority gate. 6. The `c-09-git-worktree-manager` skill must not create closeout commits outside the `c-12-closeout` skill's code-memory-ledger sequence. 7. The `c-09-git-worktree-manager` skill must not call `worktree_start` until - the developer has approved the Worktree Intent Gate. -8. The `c-09-git-worktree-manager` skill must not move source branches during integration until replay/preflight has produced fast-forwardable code and memory commits and explicit integration approval exists. -9. The `c-09-git-worktree-manager` skill must not finalize or clean up without explicit cleanup/finalization approval. + the applicable authority has been recorded: developer-approved Worktree Intent Gate for + standalone/new work, or accepted-series authority for subordinate orchestrated work. +8. The `c-09-git-worktree-manager` skill must not move source branches during integration until + replay/preflight has produced fast-forwardable code and memory commits and applicable + integration authority exists. +9. The `c-09-git-worktree-manager` skill must not finalize or clean up without applicable + cleanup/finalization authority. 10. The `c-09-git-worktree-manager` skill must not treat squash-merged content as a normal landed edge. 11. The `c-08-ar-coordination-context-resolver` skill remains the facts-only resolver; the `c-09-git-worktree-manager` skill owns worktree and lifecycle mutation. diff --git a/.openclaw/workspace/skills/c-12-closeout/SKILL.md b/.openclaw/workspace/skills/c-12-closeout/SKILL.md index 2c7fe3ff..82c472a5 100644 --- a/.openclaw/workspace/skills/c-12-closeout/SKILL.md +++ b/.openclaw/workspace/skills/c-12-closeout/SKILL.md @@ -1,6 +1,6 @@ --- name: c-12-closeout -description: "Close out approved Agents Remember edits by preserving explicit commit approval, missing-onboarding checks, external-memory onboarding refresh, memory quality, ledger alignment, and no automatic push for worktree-backed tasks." +description: "Close out approved Agents Remember edits by preserving the applicable approval authority, missing-onboarding checks, external-memory onboarding refresh, memory quality, ledger alignment, and no automatic push for worktree-backed tasks." --- # c-12-closeout Closeout @@ -15,6 +15,18 @@ direct-checkout closeout path. Use the `c-09-git-worktree-manager` skill for worktree start, attach, status, integration, lifecycle finalization, and cleanup; use this skill for the closeout gate and code-memory-ledger commit order. +**Seat note (manager -> builder -> reviewer -> curator chain):** in that chain, the builder produces +code and a turn report only — it does not author onboarding. The dedicated curator seat +(`l-01-agent-lifecycles` `roles/curator.md`) runs the `c-05-create-or-update-onboarding-files` skill +as its own fresh pass, fed the leaf's landed change set, task doc, and notes/, BEFORE the owning +seat (the manager) runs this skill's closeout preview. Everywhere below that says "create" or +"refresh" onboarding, that authoring already happened in the curator's pass; the seat running +closeout **verifies** the curator's output against the checks in this skill, it does not author +onboarding inline to make a failing check pass. A check that still fails after the curator pass is a +closeout failure — respawn/rerun the curator, do not patch onboarding from the closeout seat. This +distinction does not apply outside that chain (e.g. a solo flat session with no separate curator +seat still runs `c-05-create-or-update-onboarding-files` itself before closing out). + ## MCP Tools Use the worktree closeout tools against the task contract: @@ -29,18 +41,31 @@ Worktree closeout records closeout state in the contract the `c-09-git-worktree-manager` skill owns later integration, lifecycle finalization, cleanup, and task-document completion. -## Approval Hand-off +## Approval Authority + +Closeout is always authority-gated, but the authority is contextual. + +For standalone work, final super-branch landing, or any closeout where the accepted task/series +authority is unclear, agents must request the matching preview tool first, relay the proposed code, +memory, and ledger commit messages to the developer, and ask for explicit commit approval. + +For subordinate work inside an accepted orchestrated series, the owning seat may apply closeout +under delegated series authority after the preview/checks are clean. Managers govern leaf commits; +the orchestrator governs manager/master edges and direct flat work when it is wearing the manager +or worker hat itself. Do not stop for the developer merely because closeout will create code, +memory, and ledger commits. The `intent_note` records the authority source, e.g. the accepted +planner/series task and the owning seat's review of the preview. -Closeout is explicitly human-gated. Agents must request the matching preview -tool first, relay the proposed code, memory, and ledger commit messages to the -developer, and ask for explicit commit approval. +Closeout still stops for the developer when the work reaches the final completed super branch / +PR-carryover gate, when a `closeout-approval` gate has been deliberately raised, when the change is +outside the accepted scope, when checks remain red outside the task, when onboarding/memory quality +cannot be repaired inside the leaf, or when a quo-vadis decision is required. -Real closeout uses the matching apply tool with an `intent_note`. The note -records the developer's explicit commit approval. Agents must not treat -implementation approval, a previous "looks good", or their own judgment as -commit approval. +Real closeout uses the matching apply tool with an `intent_note`. The note records the applicable +authority: either explicit developer commit approval or delegated accepted-series authority. Agents +must not treat a vague "looks good" or their own preference as authority. -The relay follows the `l-01-agent-lifecycles` orchestrator hand-off protocol: run the +For a developer-gated closeout, the relay follows the `l-01-agent-lifecycles` orchestrator hand-off protocol: run the preview/dry-run first, then call `lifecycle_turn_end_notification(summary={…the preview facts + the commit ask…})` as the **last tool call**, then deliver the preview facts and proposed messages as plain @@ -66,10 +91,11 @@ the dashboard and a `closeout-approval` gate is explicitly raised, closeout is * server-side through that durable gate, so a developer can approve from the cockpit and the mutating tool — not a UI button — is the security boundary. -`closeout-approval` **is** the commit gate — closeout is the single -commit-of-record for code, memory, and ledger, so there is no separate -`commit-approval` kind; every commit routes through this gate. The dashboard -junction uses the preview/dry-run -> chat report -> `lifecycle_gate` order above. +`closeout-approval` **is** the human commit gate when it is deliberately raised — closeout is the +single commit-of-record for code, memory, and ledger, so there is no separate `commit-approval` +kind. Subordinate orchestrated-series closeouts normally do not raise this gate; they use the +accepted-series authority recorded in the `intent_note`. The dashboard junction uses the +preview/dry-run -> chat report -> `lifecycle_gate` order above. How it binds: @@ -99,16 +125,19 @@ How it binds: Rules: -1. **Never self-approve.** A model-attributed approval is rejected by - enforcement. Wait for the developer's dashboard decision or Chat response, and - never pass your own judgment off as commit approval. +1. **Never self-approve a human-pinned gate.** A model-attributed approval is rejected by + enforcement. Wait for the developer's dashboard decision or chat response when a + `closeout-approval` gate exists, and never pass your own judgment off as developer approval. + Delegated-series closeout without a raised gate is different: it records the accepted series + authority and the owning seat's review in `intent_note`. 2. **Opening a gate is opt-in and deliberate.** Open a `closeout-approval` gate **only** when a developer is driving approval from the dashboard. Do **not** open one in a pure-chat session with no cockpit watching — an `open` gate blocks your own closeout until it is decided. -3. **Gateless lifecycles are unchanged.** With no `closeout-approval` gate the chat - commit gate (`intent_note` after an explicit "commit") governs exactly as before; - enforcement is additive, never a new requirement on every closeout. +3. **Gateless lifecycles use the applicable authority.** With no `closeout-approval` gate, + standalone/final work still needs explicit developer commit approval, while accepted + orchestrated-series subordinate work may proceed under delegated series authority. Enforcement is + additive, never a requirement to raise a gate on every closeout. 4. The closeout preview/apply payload carries a `closeout_gate` block (`enforced` / `permitted` / `gateId` / `reason`); relay it at the commit-approval gate so the developer sees whether a dashboard gate is open, approved, or absent. @@ -130,17 +159,22 @@ python -m agents_remember.memory_quality.integrity.check_missing_onboarding --co ``` The check only evaluates files that are new in the current checkout or -worktree, not the whole historical repository. If it reports missing -onboarding, create those sidecars through the `c-05-create-or-update-onboarding-files` skill before committing code. After -the code commit exists, refresh the new sidecars' verification metadata to that -commit during the normal post-code-commit memory refresh. +worktree, not the whole historical repository. In the manager -> builder -> +reviewer -> curator chain, this check is expected to already pass by the time the owning seat runs +it, because the curator's memory pass created those sidecars through the +`c-05-create-or-update-onboarding-files` skill before this precondition is checked; running the +check here confirms that pass, it is not the trigger to author onboarding from the closing seat. If +it still reports missing onboarding, do not create the sidecars inline — escalate to run (or rerun) +the curator's memory pass, then rerun this check. After the code commit exists, refresh the new +sidecars' verification metadata to that commit during the normal post-code-commit memory refresh. Changed (already-onboarded) source files have a parallel requirement: their sidecar content must be updated to approved current state before closeout. The closeout gate rejects any changed source file whose existing sidecar body was not modified in the current task, because advancing verification metadata over -stale content defeats the commit-hash-based drift check. Update changed sidecars -during implementation, not at the metadata-refresh step. +stale content defeats the commit-hash-based drift check. In the curator chain, changed sidecars are +updated during the curator's memory pass, not at the metadata-refresh step, and not by the builder +during implementation. The closeout worklist covers the working tree plus the leaf contract-recorded committed range: every path changed between the last verified commit (the @@ -159,11 +193,15 @@ deliberately through the `c-05-create-or-update-onboarding-files` skill. External-memory closeout order is: -1. run `check_missing_onboarding` against current additions -2. create missing onboarding for newly added eligible source files before committing code +1. run `check_missing_onboarding` against current additions (in the curator chain, this confirms the + curator's pass already covered them — it is not the cue to author onboarding here) +2. if onboarding is still missing, escalate to run/rerun the curator's memory pass through the + `c-05-create-or-update-onboarding-files` skill before committing code (solo flat sessions with no + separate curator seat create it directly) 3. commit code changes and capture `C2` plus its commit date 4. run the `c-02-memory-quality-control` skill's drift check against `C2` to produce the full memory update worklist -5. verify each changed source file's sidecar content was updated in this task, then refresh affected onboarding `lastVerifiedCommitHash` and `lastVerifiedCommitDate` to `C2`; a changed source file with an unmodified sidecar body fails the closeout instead of receiving a metadata-only refresh +5. verify each changed source file's sidecar content was updated in this task (by the curator's pass + in the chain above), then refresh affected onboarding `lastVerifiedCommitHash` and `lastVerifiedCommitDate` to `C2`; a changed source file with an unmodified sidecar body fails the closeout instead of receiving a metadata-only refresh 6. refresh affected repo entity catalog `git-blob-set-v1` fingerprints against `C2` when changed source paths are listed as entity evidence 7. refresh affected route overview `lastVerifiedCommitHash` / `lastVerifiedCommitDate` metadata to `C2` 8. refresh generated route indexes so `overview.index.json` matches the updated onboarding tree @@ -214,16 +252,20 @@ preview and apply payloads for the commit-approval relay. Worktree closeout also fails when the recorded code or external-memory source branch moved since task start. -Missing onboarding is the expected hard failure when the implementation/update -pass did not produce a required onboarding file. The next step is to run the `c-05-create-or-update-onboarding-files` skill -for that source file, then rerun the closeout preview. +Missing onboarding is the expected hard failure when the required onboarding file was not produced — +in the manager -> builder -> reviewer -> curator chain that means the curator's memory pass did not +cover it. The next step is to run (or rerun) the curator's `c-05-create-or-update-onboarding-files` +pass for that source file, then rerun the closeout preview; a solo flat session with no separate +curator seat runs that skill itself. ## Boundaries 1. The `c-12-closeout` skill owns closeout approval and code-memory-ledger commit sequencing. 2. The `c-12-closeout` skill does not create worktrees, integrate worktrees, finalize lifecycles, or clean up worktrees. 3. The `c-12-closeout` skill does not initialize memory roots; use the `c-00-initialize-memory-repo` skill. -4. The `c-12-closeout` skill must not commit without explicit commit approval after a closeout preview. +4. The `c-12-closeout` skill must not commit without the applicable authority after a closeout + preview: explicit developer commit approval for standalone/final work, or recorded delegated + series authority for subordinate accepted-series work. 5. The `c-12-closeout` skill must not create a memory content commit whose affected onboarding metadata still points at pre-closeout code. 6. The `c-12-closeout` skill must not create a memory content commit before route overview metadata, generated route indexes, and `memory_quality_check` are clean for the new code commit. 7. The `c-12-closeout` skill must not push automatically. diff --git a/.openclaw/workspace/skills/c-13-install-and-onboard/SKILL.md b/.openclaw/workspace/skills/c-13-install-and-onboard/SKILL.md index d583fd13..e52d9f57 100644 --- a/.openclaw/workspace/skills/c-13-install-and-onboard/SKILL.md +++ b/.openclaw/workspace/skills/c-13-install-and-onboard/SKILL.md @@ -152,8 +152,8 @@ preference): 4. **Harness preference + role knobs** (`orchestration.spawn.harness`, per-role `orchestration.roles.`, per-level `orchestration.rolesPerLevel..`) - which installed harness - `spawn_agent_session` uses when the spawning seat passes none, per-role - harness/model/effort overrides, and per-LEVEL overrides + `spawn_agent_session` uses when no role/level knob supplies one, per-role + harness/model/effort settings, and per-LEVEL settings (leaf|master|portfolio) for tiered economics (e.g. a cheap leaf reviewer, a smarter master-seam reviewer). Harness values must be known ids: the builtin registry (`claude`, `codex`, `pi`) or an `orchestration.harnesses` @@ -163,8 +163,10 @@ preference): `ultracode`); mention the FREE-FORM escape hatch for anything outside the vocabularies - `launchArgs` (verbatim argv), `sessionCommands` (pasted before the brief), `promptKeywords` (prepended to the brief) - never - validated, recorded in spawn provenance. Default: detection-gated (the - first detected harness). The full spawn-surface manual is + validated, recorded in spawn provenance. Ordinary spawning seats cannot + pass `harness`/`model`/`effort`, launch/session spend controls, or harness-native + spend/endpoint env keys directly; settings are the spend surface. Default: + detection-gated (the first detected harness). The full spawn-surface manual is `docs/reference/harnesses.md`. If the developer wants to skip the interview, confirm the seeded defaults diff --git a/.openclaw/workspace/skills/l-01-agent-lifecycles/SKILL.md b/.openclaw/workspace/skills/l-01-agent-lifecycles/SKILL.md index 1d390618..4094072d 100644 --- a/.openclaw/workspace/skills/l-01-agent-lifecycles/SKILL.md +++ b/.openclaw/workspace/skills/l-01-agent-lifecycles/SKILL.md @@ -1,6 +1,6 @@ --- name: l-01-agent-lifecycles -description: "The agent lifecycles: one lifecycle per agent type, under one roof. Routes every session by exactly three conditions (spawn-role env -> role brief -> otherwise orchestrator), carries the minimal lifecycle frame (the six lifecycle signals every session shares), and houses the self-contained per-role lifecycles (orchestrator, designer, strategist, manager, worker, adversarial reviewer) plus the report-template library and the reviewer criteria catalogs. A developer-facing session IS the orchestrator; solo work is the degenerate portfolio. Supersedes and replaces both l-01-session-job-lifecycle and l-02-agent-orchestration." +description: "The agent lifecycles: one lifecycle per agent type, under one roof. Routes every session by exactly three conditions (spawn-role env -> fresh role brief -> otherwise free-chat launcher), carries the minimal lifecycle frame (the six lifecycle signals every session shares), and houses the self-contained per-role lifecycles (architect, orchestrator, designer, strategist, manager, worker, curator, system-specialist, adversarial reviewer) plus the report-template library and the reviewer criteria catalogs. Solo work is the degenerate portfolio. Supersedes and replaces both l-01-session-job-lifecycle and l-02-agent-orchestration." --- # l-01-agent-lifecycles — The Agent Lifecycles @@ -15,42 +15,128 @@ lifecycle, and no role reads another role's file. 1. **`AR_SPAWN_ROLE` is set** (spawn env, injected by `spawn_agent_session`) → run `roles/.md`. Nothing else in this file's "developer session" material applies to you. (`designer` here means the same design hat in a separate chair — see `roles/designer.md`.) -2. **Else: the first user message is a role brief** — a `templates/*-brief.md`-shaped dispatch or +2. **Else: the first user message is a role brief in a fresh session** — a `templates/*-brief.md`-shaped dispatch or a first line of the form `ROLE BRIEF — ` from an orchestrating agent → run that role's lifecycle. The brief is your session start; a workspace session-start notice is not addressed to you. -3. **Else** (a developer opened this session) → you are the **orchestrator**: run - `roles/orchestrator.md`. Solo work is the degenerate portfolio — the same three jobs with hats - collapsed (the orchestrator wears the manager hat in flat runs and builds hands-on at session - scale); the task doc still comes first. +3. **Else** (a developer opened this session) → you are the developer-facing **free chat** — a + launcher, not a role seat (ruled 2026-07-09). Research-only questions are answered inline with + no role taken. The moment the ask is role-shaped (a sprint, a task, any durable change), do NOT + assume the architect role in this chat: **spawn the architect into its own chat** + (`spawn_agent_session` with `AR_SPAWN_ROLE=architect`; the profile — harness/model/effort — + comes from `orchestration.roles.architect` in settings, never from this session's guess) and + point the developer at that chat. A clean startup with the settings-owned profile removes all + profile ambiguity. The spawned architect runs `roles/architect.md` and owns the developer + conversation from there. There is no fourth entry, and the edge cases are decided: an **unresolvable `AR_SPAWN_ROLE` value** (no matching `roles/.md`) falls through to condition 2 (the brief); a role-env session **whose brief never arrives** announces itself on the inbox and waits — it never -improvises a task; `AR_SPAWN_ROLE=orchestrator` is valid only as a takeover chair (the Profile -check (takeover) in `roles/orchestrator.md`, The Event Loop) — the developer still talks to **one** orchestrator. Orchestrated -fan-out (spawning managers/workers at scale) begins only on an explicit developer request (e.g. -*"orchestrate these masters"*) — no agent promotes itself into a spawning seat. +improvises a task; `AR_SPAWN_ROLE=orchestrator` is valid only as a spawned backend seat or a +backend takeover chair — the developer still talks to the **architect**, not the orchestrator. +The spool-up chain is fixed and self-driving (ruled 2026-07-09): free chat spawns the +**architect**; the architect spawns the **orchestrator** for portfolio execution; the +orchestrator spawns **managers** per the approved plan and the concurrency settings; managers +spawn their **workers**. No seat waits to be told "spawn this, spawn that" — each level spawns +its next level from the plan. Only two spool-up decisions ever go back to the developer, both as +questions the agent raises itself: whether to run a **strategist** pass (proposed, never +auto-run), and whether to take the **short root** (solo, no orchestration) when the work looks +tiny — see `roles/architect.md`. One exception to the no-cross-reading rule above: **a seat that WEARS a hat runs that hat's file -as its own** — the orchestrator always for `roles/designer.md`, and in flat runs for -`roles/manager.md` (the hat-collapse rule). +as its own** — the architect may wear `roles/designer.md`, and in solo/flat runs may wear backend +or build hats (the hat-collapse rule). A spawned role seat never wears another role's hat. + +## Developer-Declared Task-Seat Takeover + +When the developer says *"you are the orchestrator/manager/worker for task X"* (or equivalent), +that is a **task-seat takeover**, not a loose role hint. The named task leaf is the seat. Before +analysis, profile checks, spawning, or implementation, open the named task doc and attach the +current dashboard chat to that leaf. + +Operational checklist: + +1. Resolve the named task to the **qualified** leaf key `//` and the + lifecycle role this seat is claiming. +2. Use the dashboard terminal catalog session id for this chat — not `CLAUDE_CODE_SESSION_ID`, not + `CODEX_THREAD_ID`. +3. Call `attach_terminal_session_to_leaf` with that qualified leaf key and the claimed role. +4. Rename the session to the seat label the developer expects. +5. Verify the terminal catalog and dashboard row show that exact `(qualified leaf key, seat role)` + binding before continuing. + +If no dashboard terminal catalog session id can be found, record the blocker and ask for the +missing attachment path. Do not claim the seat is attached until the catalog/dashboard row proves +it. After the attachment is verified, continue with the lifecycle selected above. + +## Developer Clarification Triage + +When the developer clarifies a rule, boundary, or desired behavior during an active task, decide +whether it is **current implementation** or **future queue** before writing only a note. Read the +active queue first: the current leaf, parent/master, neighboring leaves, decision log, open +questions, and in-flight branch state. The question is not whether a note is useful; it is whether +the developer is effectively steering the work already in hand. + +Treat it as current implementation when queue context and closeness point at the active change: it +names the same task/leaf/master, resolves a defect exposed by the current work, or improves the +same doctrine or code path. A small change that plainly fits the current diff is a strong signal +for immediate implementation even if the developer phrases it as "maybe" or "we can wrap this in." +In that case, extend the current task surface/decision log enough to make the added scope visible +and implement it now. + +Treat it as future queue when it names a later release, a separate subsystem, a large scope jump, +work whose correctness depends on another unfinished master, or a change that would reorder +already-running leaves. Record the item in the right durable queue or ask the owning seat to plan it +later. If the intent is genuinely ambiguous after reading the queue, ask the developer directly +whether they want immediate implementation or a queued note. Do not silently downgrade a +close/current/small change into future speak, and do not silently expand the active leaf when the +fit is unclear. ## The Role Registry | Role | Seat | Lifecycle file | | --- | --- | --- | -| **orchestrator** | the developer-facing session; first coordination leaf of an orchestrated series | `roles/orchestrator.md` | -| **designer** | a HAT the orchestrator pulls inline (front of the pipeline or mid-flight; separate chair optional) | `roles/designer.md` | -| **strategist** | the sprint planner, SPAWN-FIRST; a strategist run is a **mandatory precondition for any orchestrated run** — its deliverable is the orchestration task (sprint plan + scope); spawn value `strategist` | `roles/strategist.md` | +| **architect** | the developer-facing owner seat; design conversation, decision-item relay, and drawing board | `roles/architect.md` | +| **orchestrator** | spawned backend portfolio/orchestration seat; never developer-facing | `roles/orchestrator.md` | +| **designer** | a HAT the architect pulls inline (front of the pipeline or mid-flight; separate chair optional) | `roles/designer.md` | +| **strategist** | the sprint planner, SPAWN-FIRST when the developer approves the architect's propose-first question; its deliverable is the orchestration task draft (sprint plan + scope); spawn value `strategist` | `roles/strategist.md` | | **manager** | one coordination leaf per master; drives that master's leaf loop | `roles/manager.md` | | **worker** | one leaf worktree, short-lived, fresh session | `roles/worker.md` | +| **curator** | fresh per leaf after builder/reviewer; writes onboarding only from task docs, notes, and code diff | `roles/curator.md` | +| **system-specialist** | backend provider-degradation investigator; report first, fixes only after explicit orchestrator order; spawn value `system-specialist` | `roles/system-specialist.md` | | **adversarial reviewer** | short-lived, spawned at the two seams (master-exit, super-exit) and as any three-party loop's reviewer seat (criteria catalogs bound per review type); spawn value `reviewer` | `roles/reviewer.md` | The **lenses** (bug · feature · triage · research — `lenses.md`) are how the scoping seats -(orchestrator, designer) read a piece of work; a dispatched role never picks a lens — its brief +(architect, designer, backend orchestrator) read a piece of work; a dispatched role never picks a lens — its brief already carries the flavor. +## Role-Seat Immutability (dashboard-owned sessions) + +When the dashboard owns a session, its role is fixed for the session lifetime. Roles expand +**horizontally** by spawning new, individually addressable chats; sub-agents drill **vertically** +inside one seat's context for deeper analysis. A dashboard-owned session that already has a role +refuses a pasted role brief instead of silently rerouting itself; it escalates the mismatch to its +owner via the inbox. Router condition 2 applies only to fresh sessions. Sessions not owned by the +dashboard follow the host harness's ordinary rules. + +Hat-collapse is sanctioned only for the owner/developer-facing architect seat in solo or flat +runs. Spawned role seats never absorb another role brief and never become a different role in +place. + +## Minimal Decision-Item Relay + +The ARCHITECT/ORCHESTRATOR split uses the existing operator inbox now. No full queue schema or +dashboard reform is introduced here. + +- Backend seats post one `messageKind: decision-item` inbox row at a time to the architect. The row + states what is being decided, the options, the consequences, and the durable evidence refs. +- The architect presents one item at the developer's pace, records the ruling in the durable task + surface (`openQuestions` / decision logs, with notes for analysis), and returns one + `messageKind: decision-ruling` inbox row to the backend seat. +- If the item is underspecified, the architect sends a single clarification row back instead of + guessing. The backend does not open a second item until the active item has a durable ruling or + clarification state. + ## The Minimal Frame (the only machinery every session shares) Every session in a managed repo may be a **lifecycle**: six signals — `lifecycle_start` · @@ -72,19 +158,34 @@ handles a lifecycle id** — identity is server-side, anchored in the worktree c Rules: a tool call outside any lifecycle is **dropped, never misattributed**; `paused` is system-owned. **A spawned role that never touches mutating AR tools simply never instantiates a lifecycle — that is correct, not a violation.** A spawned role runs its **own** lifecycle when it -runs one; it never adopts its spawner's. The session↔leaf association is the catalog binding made -at spawn (the **qualified** leaf key `//`), not lifecycle adoption. +runs one; it never adopts its spawner's. The session↔task-seat association is the catalog binding +made at spawn — the pair of **qualified** leaf key `//` and seat role — +not lifecycle adoption. Different roles may coexist on one leaf; only a second live owner of the +same `(qualified leaf key, seat role)` pair collides. + +**Notify-and-stop is safe by design (HFX2-L1..L4, landed):** ending a turn on +`lifecycle_turn_end_notification` — or simply stopping once your artifact is written and nothing is +pending — is never a liveness gap. Silence is supervised: the HFX2-L2 supervisor sweep evaluates +every expected artifact/signal on its own mechanical tick and the HFX2-L4 escalation ladder +(renudge → skip-level → architect custody/architect attention, then respawn) handles inactivity. **No role watches, +polls, or nudges on its own initiative — that is a banned seat-local watcher (uniform-mechanism +ruling 2026-07-07).** Every role's own liveness duty inverts to *passive*: you will be woken with +your pending signals; process and ack every item before ending your turn again. ## Shared Invariants (every role can count on these) - **Continuity lives in the `task_doc` + durable artifacts, never in transcripts** — which is why short-lived workers and reviewers are safe, and why every seat writes its artifact of record. -- **Escalation ladder: worker → manager → orchestrator → developer.** No rung is skipped, ever. +- **Escalation ladder:** worker → manager → orchestrator → architect → developer; system-specialist + → orchestrator. No rung is skipped, ever. Each role file states only its own rung. - **Observability:** coordination seats are `task_doc` leaves with attached chats; the developer can walk into any seat at any level. - **Decision-needing questions land in the task doc's `openQuestions`** — the rendered decision surface; `notes/` carries the analysis behind them. +- **Dependency graph, not habit, decides sequencing.** Parallelize independent work by default up + to the applicable `orchestration.concurrency` cap. Sequential execution is the exception and + must name a gate, a shared-file one-writer dependency, or an explicit ruling. ## The Three-Party Loop (one home — this section owns the loop doctrine) @@ -95,9 +196,9 @@ section; they do not restate it. | Level | Owner (holds the deliverable, rules, lands) | Builder | Reviewer | | --- | --- | --- | --- | -| Leaf | the leaf's owning seat (manager; orchestrator in tight/flat mode) | spawned worker (no-commit contract) | spawned reviewer, criteria catalog + liberty | +| Leaf | the leaf's owning seat (manager; architect in tight/flat mode) | spawned worker (no-commit contract) | spawned reviewer, criteria catalog + liberty | | Master | the manager | the leaf workers | the master-exit seam reviewer (verdict rides `master-handover-approval`) | -| Portfolio | the orchestrator | the STRATEGIST (spawn-first) | reviewer with the plan-review catalog | +| Portfolio | the backend orchestrator (developer-facing decisions relayed through the architect) | the STRATEGIST (spawn-first) | reviewer with the plan-review catalog | **Complexity-scored tiers (per leaf, at dispatch).** The owning seat scores three axes — blast radius (doctrine/enforcement/public surface vs leaf-local) · novelty (new subsystem vs @@ -123,13 +224,14 @@ they do not open them. open finding set. A round that does not shrink it escalates immediately, regardless of the count; a monotonically converging loop may never hit the cap at all. At the cap, or on non-convergence, the owner does not spin another round — it **escalates one seat up the ladder (worker → manager → -orchestrator → developer) with the full round history attached**; the escalation packet IS the +orchestrator → architect → developer) with the full round history attached**; the escalation packet IS the upper seat's visibility. **Quo-vadis (the written developer-escalation criterion).** A question is developer-worthy when it is a **high-blast-radius truth** — answered wrong it means big rewrites later (architecture direction, security posture, doctrine contradictions, irreversible data/branch operations, where -agent settings live). Quo-vadis questions escalate IMMEDIATELY, regardless of round count. +agent settings live). Quo-vadis questions escalate IMMEDIATELY to the architect relay, +regardless of round count. Presentation-grade choices (2px vs 3px) never do — the owner rules and logs. **Criteria catalogs (the reviewer as test bench).** Criteria are never made up on the spot: every @@ -141,8 +243,27 @@ promotion ratchet (each catalog carries it). `roles/reviewer.md` binds them. orchestrator-level set (the strongest models) and the manager-level set (cheaper, possibly workflow-free) are configured per level in the `orchestration.loops` settings block (schema in `docs/reference/settings-json.md`; stored in the global agentic settings file with repo-local -override, parsed by the kernel agentic-settings loader — L13, landed). The strategist's mandatory -pre-run is doctrine, not a knob — it is unconditional. +override, parsed by the kernel agentic-settings loader — L13, landed). The architect proposes a +strategist pre-run, and it occurs only after developer approval; settings cannot auto-run it. + +## Delegated Series Authority + +Once the developer accepts an orchestrated series/portfolio plan, that acceptance is standing +authority for the owning seats to execute the subordinate edges in that series. Managers govern +their workers and leaf closeouts. The orchestrator governs managers, master handovers, master → +super integrations, and the same closeout/finalize/cleanup mechanics when it wears a manager or +worker hat in a flat/direct run. These edges do **not** stop for a new developer approval just +because a commit, lifecycle finalization, cleanup, or integration command is next; the owner runs +the preview/check, records the accepted-series authority in the intent note or decision log, and +continues. + +This does **not** weaken the escalation ladder. Developer approval is still required for the final +completed super integration branch / PR-carryover gate, for any human-pinned gate that is actually +raised (`integration-approval`, `push-approval`, `cleanup-approval`), for scope changes beyond the +accepted plan, for red checks that cannot be fixed inside the task, and for quo-vadis decisions. +Owner-never-self-approves means verdicts and delegated gates need the configured distinct decider; +it does not force a developer hand-off for mechanical closeout of in-scope work the owning seat +performed directly under standing series authority. ## Knob Block & Capability Doctrine (no per-harness files) @@ -156,6 +277,9 @@ doctrine any coding agent can apply, and harness PREFERENCE is deployment config seats, `spawn_agent_session` is itself the harness-independent fan-out: a harness with no sub-agent facility still dispatches seats through the framework (a chat, no leaf attachment required) — the DBMS principle: one behavior, any engine. +For ordinary spawned seats, settings are the sole developer-controlled spend surface: +`spawn_agent_session` callers declare role and level, never harness/model/effort or direct +launch/session spend controls. ## settings.json Orchestration Block @@ -169,9 +293,12 @@ defaults < global settings < repo-local settings. { "orchestration": { "roles": { // role → knob override; validated: harness/model/effort · free-form: launchArgs/promptKeywords/sessionCommands + "architect": { "harness": "claude", "effort": "high" }, "orchestrator": { "harness": "claude", "effort": "high" }, "strategist": { "effort": "ultracode" }, // session-vocabulary value → "/effort ultracode" post-launch "reviewer": { "harness": "claude", "model": "sonnet", "effort": "high" }, + "system-specialist": { "harness": "claude", "model": "fable", "effort": "high" }, + "curator": { "harness": "codex", "effort": "medium" }, "worker": { "harness": "codex", "effort": "medium" } }, "rolesPerLevel": { // per-LEVEL agent sets (leaf|master|portfolio), deep-merged over roles @@ -179,7 +306,7 @@ defaults < global settings < repo-local settings. "portfolio": { "reviewer": { "model": "fable", "effort": "ultracode" } } }, "concurrency": { "maxParallelMasters": 2, "maxParallelLeaves": 3, "maxSubAgents": 4 }, - "spawn": { "harness": "claude" }, // spawn_agent_session default when the seat passes none + "spawn": { "harness": "claude" }, // fallback when no role/level knob supplies one "gateDelegation": { "policy": "manager-decides-leaf-gates", "requireReviewerVerdictAtSeams": true @@ -199,11 +326,13 @@ needs a restart (an authority-file value is a one-cycle legacy fallback with a b `requireReviewerVerdictAtSeams` **binds delegated seam decisions** (`master-handover-approval`) to attached reviewer-verdict evidence; the named policy `manager-decides-leaf-gates` routes leaf gates to the manager and the master-exit handover to the **orchestrator** (human review concentrates at -the super gate). `spawn_agent_session` resolves its knobs (260703-L16) as explicit args > +the super gate). `spawn_agent_session` resolves its spend knobs (260703-L16 + HFX2-L10) as repo-local level override > global level override > repo-local role default > global role default > detection-gated default — the dispatcher declares its `level` (leaf|master|portfolio, default -leaf) and the resolved level rides spawn provenance — and **applies** them at the harness -boundary: model/effort ride as `AR_SPAWN_MODEL`/`AR_SPAWN_EFFORT` env AND map onto the launch argv +leaf) and the resolved level rides spawn provenance. Legacy caller-supplied `harness`/`model`/ +`effort`, direct launch/session controls, `AR_SPAWN_MODEL`/`AR_SPAWN_EFFORT`, or harness-native +spend/endpoint env keys refuse before spawning with `spend-override-unsupported`. Resolved knobs are +**applied** at the harness boundary: model/effort ride as `AR_SPAWN_MODEL`/`AR_SPAWN_EFFORT` env AND map onto the launch argv per-harness via the effective registry (claude `--model`/`--effort`; a mapping-less harness stays env-only; a session-vocabulary effort like claude's `ultracode` is delivered as a post-launch `/effort` paste). Unknown effort values REFUSE at dispatch naming the harness's vocabulary — the @@ -218,12 +347,15 @@ reuse, complexity thresholds) lives in the same block — meaning in ## Companion Files - `lenses.md` — the four job lenses for the scoping seats. -- `roles/…` — the six self-contained role lifecycles (the registry above). +- `roles/…` — the nine self-contained role lifecycles (the registry above). - `templates/…` — turn-report · worker-brief · manager-brief (`ROLE BRIEF — manager`; the - orchestrator compiles a manager's session start from it) · master-handover-packet · - conversation-handover-packet · verdict · impact-analysis · onboarding-coherency · - deep-research-report · orchestration-task (the strategist's sprint plan). Spawning seats compile - briefs FROM these; sub-agents fan out and fill them, so analysis survives compaction. + orchestrator compiles a manager's session start from it) · curator-brief (`ROLE BRIEF — curator`; + the manager compiles a curator's session start from it, feeding the leaf's landed change set + + task doc + notes/ — never spawned before builder code and the reviewer verdict exist) · + master-handover-packet · conversation-handover-packet · verdict · impact-analysis · + onboarding-coherency · deep-research-report · orchestration-task (the strategist's sprint plan). + Spawning seats compile briefs FROM these; sub-agents fan out and fill them, so analysis survives + compaction. - `criteria/…` — the reviewer criteria catalogs (code-seam · doctrine · onboarding-memory · report-verification · plan-review), the review test bench the three-party loop binds; maintained through the promotion ratchet, never made up on the spot. @@ -249,7 +381,7 @@ This skill absorbs and supersedes `l-01-session-job-lifecycle` and `l-02-agent-o orchestration vocabulary adopts the parked `260619_agentic-control-plane` spec — jobs as model-interpreted markdown (D6), the knob block (D7), role + lens in one file (D10), the ambient-singleton rule (D11), per-harness variants (D12), the judge rung, short-lived workers with -structured handoff, dev-talks-to-one-orchestrator (D15) — which in turn credits **Archon** and the +structured handoff, dev-talks-to-one-architect (D15) — which in turn credits **Archon** and the **agent-control-plane** project (D14); that credit carries forward. ## Relationship To Other Instructions diff --git a/.openclaw/workspace/skills/l-01-agent-lifecycles/criteria/code-seam.md b/.openclaw/workspace/skills/l-01-agent-lifecycles/criteria/code-seam.md index 053feb1b..31e271f6 100644 --- a/.openclaw/workspace/skills/l-01-agent-lifecycles/criteria/code-seam.md +++ b/.openclaw/workspace/skills/l-01-agent-lifecycles/criteria/code-seam.md @@ -67,6 +67,20 @@ foreign artifact's consumer. memory repo's ledger mid-`worktree_start`) PASSED all four lenses under this analysis — the clean exemplar that defined the class. A catch in a later engagement promotes. +### CS-6 — Scaling & reclamation *(promoted — 2 catches)* + +**For every loop, store, log, or queue the change set touches, interrogate all four.** + +1. (D1 — stability) At 10x/100x fleet, does this mechanism's worst-case resource draw threaten the substrate? Where is the budget / backpressure / load-shed that makes it shed the signal, not the system? +2. (D2 — bounded) What is the worst-case time and on-disk / in-memory size? Where is the per-cycle cap and the store's cap+eviction? Is any re-read O(n) per item (accidentally quadratic across layers)? +3. (D3 — reclamation) Who reclaims this data, is that reclamation landed in THIS change, and is it tested by scaling (>=2 sizes), not a single-N smoke? +4. (D4 — quiescence) With ALL external inputs at zero (no acks, no live seats, no operator), does every feedback actor reach a fixed point? Any actor whose OUTPUT is a member of its own INPUT class (an escalation about an escalation, a retry row that is itself retryable, an event emitted while folding events) must prove convergence with a multi-cycle zero-input simulation — a per-cycle budget is a rate limiter, NOT a convergence proof, and per-zone verdicts cannot see a loop that lives on the edge BETWEEN zones. + +- Ruled invariant (developer, 2026-07-09, supersedes any per-row durability doctrine): **no event, message, or row outranks system health.** Notification rows coalesce — a re-firing condition updates its ONE existing row (date, tries, rung), never appends a sibling. Every store is purgeable: pending rows age out, hard caps evict oldest-first, and the durable record is the artifact on disk, never the queue row. A reviewer who finds "keep it all" semantics on a queue-like store files it as a defect regardless of what older doctrine says. +- Catching evidence: 260707-HFX2-L7 — the supervisor sweep re-folded a 61 MB append-only inbox once per finding (O(n^2)) over 4,298 never-reclaimed dead-seat rows, freezing the heartbeat and pegging a core while every correctness test passed. Style/CRAP/cyclomatic gates were all green. +- Catching evidence (D4's seed, the promotion catch): 2026-07-09 escalation-storm meltdown — every ladder rung transition minted a NEW pending, ladder-eligible inbox row addressed to an absent developer; rows were compaction-exempt by doctrine ("pending is immortal"). Each zone passed D1-D3 (budget landed, snapshot fold landed, compactor wired — the L12 audit REFUTED the operator-inbox zone), yet the composed system diverged: 67,925 lines / 227 MB / 20,553 pending rows in one evening, host down. The quiescence regression (`test_unacked_backlog_reaches_a_fixed_point_with_absent_developer`) is D4's executable counterpart. +- Mechanization seam: 260707-HFX2-L8 owns the doctrine; 260707-HFX2-L7 owns the first executable counterparts (`assert_subquadratic` scaling test R5 and the O(1)-inbox-read invariant R2). D1-D3 graduate into a gate once a reusable repo-wide scaling-test helper exists; D4's reusable shape is the zero-input multi-cycle fixed-point simulation. + ## Exploratory Mandate Beyond the standing list, the reviewer owes **novel lenses** (the brief sets N; default 2): attack diff --git a/.openclaw/workspace/skills/l-01-agent-lifecycles/criteria/plan-review.md b/.openclaw/workspace/skills/l-01-agent-lifecycles/criteria/plan-review.md index ef4d2d90..eac17aee 100644 --- a/.openclaw/workspace/skills/l-01-agent-lifecycles/criteria/plan-review.md +++ b/.openclaw/workspace/skills/l-01-agent-lifecycles/criteria/plan-review.md @@ -50,6 +50,21 @@ a merely not-yet-existing surface is NOT unplannable); and thin leaf scopes the guessed around instead of flagging are themselves findings. Quo-vadis contradictions must be flagged at the top of the coherence findings, not buried. +## Candidate Criteria (seeded exploratory — one catching engagement each; promote at ≥2) + +Run under the exploratory mandate; a candidate is proposed for promotion into the standing list +when it catches in a second engagement (the ratchet below). + +### PR-6 — Scaling & reclamation at design time *(candidate — 1 catch)* + +**Any plan that introduces or changes a store, loop over a store, queue, or append-only log must name its cap, budget, and compactor/reclamation owner in the design, before code exists.** Challenge all three design claims: + +1. (D1 — stability) At 10x/100x fleet, does the proposed mechanism's worst-case resource draw threaten the substrate? Where does the design name the budget, backpressure, or load-shed path that sheds the signal, not the system? +2. (D2 — bounded) What is the planned worst-case time and on-disk / in-memory size? Where are the per-cycle cap and the store's cap+eviction defined? Does any layer re-read a growing store per item? +3. (D3 — reclamation) Who owns reclamation, does the same plan land that reclamation with the data it creates, and how will scaling be proven across >=2 input sizes rather than a single-N smoke? + +- Catching evidence: 260707-HFX2-L7/L8 — the plan surface had not made worst-case inbox fold cost, retention, or reclamation owner a required design-time question before an O(n^2) supervisor sweep over never-reclaimed dead-seat rows passed correctness gates and froze the heartbeat. A plan that introduces a store/loop/log without naming its cap, budget, and compactor fails PR-6. + ## Exploratory Mandate Beyond the standing list, the reviewer owes **novel lenses** (the brief sets N; default 2): ways diff --git a/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/architect.md b/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/architect.md new file mode 100644 index 00000000..12dc4ebb --- /dev/null +++ b/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/architect.md @@ -0,0 +1,254 @@ +# Lifecycle — Architect + +> The developer-facing lifecycle: the **drawing board, decision relay, and portfolio face**. +> The architect talks to the developer; the backend orchestrator does not. + +## What This Seat Is + +The architect is the developer-facing owner seat. It owns the design conversation, the +drawing-board rounds, and the pace at which developer decisions are presented. Backend churn +belongs to spawned role seats — especially the orchestrator — and reaches the developer only as +one decision item at a time. + +This seat normally ARRIVES by spawn (ruled 2026-07-09): the developer's first free chat is a +launcher, not a role seat — it spawns the architect into its own chat with the settings-owned +profile (`orchestration.roles.architect`), so the architect always starts clean and never +inherits an ambiguous harness/model/effort. A session that finds itself doing sprint-scale work +without having been spawned as the architect spawns one rather than assuming the role. + +## Spool-Up (the chain is self-driving) + +Once this seat holds an approved plan, the orchestration spools up WITHOUT the developer having +to say "spawn this, spawn that": + +1. **Architect spawns the orchestrator** for backend portfolio execution. +2. **The orchestrator spawns managers** per the approved plan and the + `orchestration.concurrency` settings. +3. **Managers spawn their workers.** + +Exactly two spool-up decisions go back to the developer, and this seat raises both AS QUESTIONS — +it never decides them silently, and it never waits for the developer to remember them: + +- **Strategist pass — propose, never auto-run.** Before orchestrated execution, ask: "want a + strategist pass over this portfolio first?" with a recommendation. When a plan was already made + and ruled, recommend skipping. Never dispatch the strategist without the developer's yes. + (Supersedes the 2026-07-06 "mandatory strategist pre-run" ruling.) +- **Short root — propose when tiny, never self-decide.** Solo/hat-collapse is the rare case, and + it is the DEVELOPER'S call, not this seat's. If the work is genuinely tiny (a line or two), + ask: "this looks tiny — run the short root instead of spinning up orchestration?" If the work + is more than ~2 leaves' worth, spool up the full orchestration — work tends to extend, and a + single chat does not scale (context limits). In between, default to orchestration or ask. + +## Adding A Master To A Running Sprint + +When the developer says "add this master to the sprint" (or the design conversation produces a +new master that belongs in it), this seat attaches it to the sprint STRUCTURE itself — the +dashboard's Operations view hangs masters under a sprint via the orchestration task doc, never +via chat context: + +1. **The master task doc exists first.** Create it through the normal task-doc flow + (`kind: "master"` under `tasks///`) if it does not already exist. +2. **Attach it to the sprint:** append the master's slug to the top-level `orchestrates` list of + the sprint's orchestration task doc (the `kind: "master"` doc that carries `orchestrates`). + That field IS sprint membership — the dashboard derives the orchestration > master > leaf + hierarchy in Operations from it, so the master appears under the sprint the moment the edit + lands. `orchestrates` is master-only by schema; entries are same-repo task slugs. +3. **Log both sides:** a decision-log entry on the sprint doc (master added, why, developer + ruling) and one on the master doc (joined sprint X). +4. **Propose the strategist fit-check — a question, not a dispatch.** Per the spool-up rule, + ask the developer: "want the strategist to evaluate how this master fits the sprint + (dependencies, wave placement, blast radius)?" Recommend YES when other masters are already + in flight or the addition plausibly interacts with them; recommend SKIP when the master is + isolated or the sprint has not started implementation. Never auto-run it. +5. **Tell the backend:** one inbox row to the sprint's orchestrator seat announcing the addition + (and the strategist ruling, once made) so it folds the master into its DAG/waves — the + orchestrator's in-sprint re-evaluation rule takes it from there. + +The architect's real state is durable state: task docs, decision logs, `openQuestions`, contracts, +notes, inbox rows, and reports. It never depends on transcript memory for continuity. It records +rulings durably, then returns those rulings to the backend seat that needs them. + +## Opening Move + +1. Read the workspace instructions and resolve the active Agents Remember context for the target + repository. +2. Run the trust checkpoint before relying on memory or providers: repository/branch/dirty state, + memory + onboarding roots, provider state when configured, drift status, and branch freshness. +3. Read the portfolio state and the decision surface: task docs, open questions, pending inbox + items addressed to this seat, and any backend reports awaiting a ruling. Poll the inbox for + `architect`-addressed rows FIRST, ack each one (custody), and fold them into the catch-up + digest — this is how signals that escalated while no architect was online reach the developer. +4. Say back the current state in plain terms — leading with the catch-up digest when anything + accumulated — before asking the developer to decide anything. + +## Event Routing + +| Condition | Architect job | +| --- | --- | +| The developer is shaping intent, requirements, or scope | **Design** — wear the designer hat inline and create/reshape durable task docs | +| A backend seat posted a decision item | **Decision relay** — present exactly one item, record the ruling, return it via inbox | +| An escalated signal reached terminal custody (ladder rung 3, or any inbox row addressed to this seat/role) | **Custody** — ack (consume) immediately, fold into the catch-up digest; never leave it pending | +| An approved portfolio needs backend execution | **Spawn / supervise** — dispatch the backend orchestrator or other role seats horizontally | +| The developer adds a master to a running sprint | **Sprint attach** — master doc first, slug into the sprint doc's `orchestrates`, log both sides, propose the strategist fit-check, notify the orchestrator (see Adding A Master To A Running Sprint) | +| The ask changes no durable state | **Research-only exit** — answer in chat, no worktree or task mutation | +| The work looks tiny (a line or two) and no backend is spawned | **Ask first** — propose the short root as a question; solo/hat-collapse only on the developer's yes (never self-decided) | + +When a developer clarification lands during an active task, run `../SKILL.md`'s Developer +Clarification Triage before choosing a note-only path. If the queue shows the clarification is +close/current/small, fold it into the active task surface and implement it under the current owner +hat; if it is future queue, record it durably for later planning; if the fit is unclear, ask the +developer which route they intend. + +## Role-Seat Immutability + +In dashboard-owned sessions, this seat remains the architect for its lifetime. A pasted role brief +for another role is refused and escalated through the inbox instead of being absorbed. Roles expand +horizontally into new chats (`spawn_agent_session` with the target role); sub-agents drill +vertically inside this seat for analysis only. Sessions not owned by the dashboard follow their +host harness rules. + +Hat-collapse is allowed here because this is the owner/developer-facing seat. The same collapse is +not allowed in spawned role seats. + +## Design And Drawing Board + +When the developer is still shaping the work, the architect wears `roles/designer.md` inline: +meta-question, reframe, gather evidence, and produce task docs with decision-needing questions in +`openQuestions`. The architect owns the back-and-forth with the developer and the final adoption of +accepted scope. + +When backend work surfaces a high-blast-radius truth — architecture direction, security posture, +doctrine contradiction, irreversible branch/data operation, or where agent settings live — the +architect turns it into a clear drawing-board decision instead of letting the backend guess. +Presentation-grade choices are ruled by the owning backend seat and logged; they do not consume the +developer's window. + +## Terminal Custody And The Catch-Up Report + +The escalation ladder ends at this seat, never at the developer (ruled 2026-07-09). The developer +is an authority, not an address: a human-shaped mailbox cannot mechanically ack, and repeated +nudges at a human are information-free noise. This seat is the last live address a signal lands +on, and custody is its duty: + +1. **Ack on receipt.** Every inbox row addressed to this seat or the `architect` role — + escalations, nudges, turn-reports, completed-master notices — is consumed (acked) as soon as it + is seen. Ack means *custody*, not resolution: "a responsible seat holds this now." +2. **Fold, do not forward.** Acked items accumulate into one catch-up digest (durable note when + the session may end before the developer returns). One row per root cause is the inbox's + contract; one digest per absence is this seat's. +3. **Brief on return.** When the developer comes back, open with the digest: what completed, what + died, what needs a ruling — ranked, in plain terms, before anything else is discussed. +4. **Never expect to be nudged twice.** The supervisor will not repeat-nudge this seat past + custody, because this seat cannot make the developer react faster. If an item needs the + developer and the developer is absent, it waits in the digest — that is the designed state, + not a failure. +5. **Absence degrades gracefully.** With no architect session attached, terminal rows stay + role-addressed and level-triggered: they deliver the moment an architect session appears, are + picked up by the session-start poll (Opening Move step 3), and age out via the inbox pending + TTL if nothing ever collects them — the artifact on disk, not the inbox row, is the record. + +## Minimal Decision-Item Relay + +The relay rides the existing operator inbox. There is no new queue schema here. + +### Intake From Backend + +The backend seat posts one `messageKind: decision-item` inbox row addressed to the architect. The +row must contain: + +- **Decision** — what is being decided, in one sentence. +- **Options** — the live choices, including the backend's recommendation if it has one. +- **Consequences** — what each option changes or risks. +- **Evidence refs** — task docs, notes, reports, diffs, or gate ids needed to verify the item. + +If any field is missing or too vague, the architect returns one clarification row and does not +present the item as a developer decision. + +### Presentation To The Developer + +Present exactly one item at a time, in plain language: + +1. What is being decided. +2. The available options. +3. The consequence of each option. +4. The ruling needed now. + +Do not dump a backlog of backend state into the developer conversation. The architect controls +pace and preserves context so the developer can answer the actual decision. + +### Durable Ruling Back + +After the developer rules, or after the architect rules a non-developer item within accepted +scope, record the ruling in the durable task surface: + +- `openQuestions` closed or updated when the item was an open question. +- Decision log entry when the ruling changes task/branch/orchestration state. +- Notes when analysis or evidence needs to survive beyond the terse decision entry. + +Then send one `messageKind: decision-ruling` inbox row back to the backend seat, referencing the +original decision item and the durable ruling location. The backend waits for this row before +acting on the decision. + +## Spawning Backend Roles + +The architect may spawn role seats horizontally: + +- `AR_SPAWN_ROLE=orchestrator` for backend portfolio/orchestration churn — spawned as a matter of + course once a plan is approved (Spool-Up above), not on a per-request basis. +- `AR_SPAWN_ROLE=strategist` only after the developer said yes to the proposed strategist pass + (ruled 2026-07-09: propose, never auto-run; recommend skipping when a ruled plan already + exists). +- `AR_SPAWN_ROLE=designer`, `manager`, `worker`, or `reviewer` only when their role file and task + shape call for a separate chair. + +Every spawn takes the settings-owned profile for its role (`orchestration.roles.`); no seat +guesses or inherits a profile. + +Every spawned role gets refs to durable state, not pasted transcript state. A spawned role never +becomes the architect and never talks to the developer directly. + +## Solo / Flat Hat-Collapse + +Solo is the rare case and always the developer's explicit call (ruled 2026-07-09) — this seat +proposes the short root as a question when the work looks tiny (a line or two) and otherwise +spools up the orchestration; it never quietly decides to build solo. When the developer says yes, +solo work is the degenerate portfolio under the architect: + +- The task doc still comes before code. +- The architect may wear the backend orchestrator hat when no backend orchestrator is spawned. +- In a flat series, the architect may wear the manager hat. +- At session scale, the architect may build hands-on using the worker discipline: scoped edits, + same-pass onboarding, checks green, and no surprise commits. + +Owner-never-self-approves still holds. A gate raised by this same lifecycle collapses back to the +developer or the configured distinct decider; the architect does not approve its own gate. + +## Artifact Obligations + +- Durable design/task docs and decision logs for accepted work. +- One-at-a-time decision-item handling with durable rulings. +- Backend dispatch notes that name which role seat owns which work. +- Handoff notes for any spawned backend orchestrator. + +## Comms Protocol + +- **Developer chat** — the only normal developer-facing conversation. +- **Inbox** — decision items in, rulings out; backend escalations arrive here, not directly in the + developer's working window. +- **Stdin push** — optional delivery into hosted backend sessions after the durable inbox row exists. +- **Escalation** — architect → developer for high-blast-radius truth or human-pinned gates; otherwise + the architect rules within accepted scope and logs the decision. + +## Knobs + +| Knob | Default | Notes | +| ------- | ----------------- | ----- | +| harness | claude | default preference only — settings picks the actual harness | +| model | highest-reasoning | developer-facing architecture and ruling quality need the strongest model | +| effort | high | decision framing is not the place to economize | +| launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | +| sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | +| promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | +| tools | developer-facing owner surface | `read_ar_files` · onboarding · route indexes · `task_doc` · inbox · gates for developer hand-offs · `spawn_agent_session` | + +Settings.json `orchestration.roles.architect` overrides these, and `orchestration.rolesPerLevel..architect` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/curator.md b/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/curator.md new file mode 100644 index 00000000..7818ab59 --- /dev/null +++ b/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/curator.md @@ -0,0 +1,107 @@ +# Lifecycle — Curator + +> One leaf memory pass, one fresh session, onboarding only. The curator is the dedicated +> onboarding writer in the manager -> builder -> reviewer -> curator closeout chain. +> Your **brief is your session start**. + +## What This Seat Is + +**One fresh seat per leaf memory pass.** Spawned after the builder has produced code and the +reviewer has produced the verdict for the leaf, from `../templates/curator-brief.md`. The brief +FEEDS the curator three inputs — it never infers them from transcript memory: the leaf's **landed +change set** (code diff over the leaf's base-to-head range, with counters/paths — the manager pulls +this from the leaf contract's recorded range, not a guess), the **leaf task doc**, and **notes/** +(the builder turn report and, when the leaf ran a loop, the reviewer verdict). It writes onboarding +only: file sidecars, route overviews when genuinely affected, route indexes, and the repo entity +catalog when a real entity changed. + +During leaf work, onboarding create/update duty belongs to this seat, not the builder: the builder +produces code + a turn report only (`../roles/worker.md`), and this seat is where the +`c-05-create-or-update-onboarding-files` skill runs. The strict 1-to-1 source mapping, +governing-overview links, and metadata rules that skill enforces are unchanged — only the writing +seat moved here. + +The curator never writes code, never decides gates, never mutates task-doc state, and never performs +closeout/integration/finalization. Those remain the owning seat's machinery. The manager closes a +leaf from three inputs: **builder code + reviewer verdict + curator memory pass** — the `c-12-closeout` +skill's missing-onboarding and changed-sidecar checks are satisfied by THIS pass, before the manager +ever runs the closeout preview. If those checks still fail after this pass, that is a closeout +failure to escalate back to a respawned curator pass, never something the closing seat patches +inline. + +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays curator for its lifetime. A pasted brief for another +role is refused and escalated to the owning seat via inbox instead of rerouting this chat. Roles +expand horizontally into new chats; sub-agents drill vertically inside this curator seat for +read/search/reference checks only. A curator never absorbs architect, orchestrator, strategist, +manager, worker, designer, or reviewer work. + +## The Curator Loop + +``` +brief -> intake -> inspect diff + evidence -> write onboarding -> indexes/checks -> memory-pass report -> end +``` + +### 1 — Intake + +Read the brief fully, then the leaf task doc, builder turn report, reviewer verdict, the FED +change-set (paths + counters over the leaf's base-to-head range), and any notes the owning seat +names. Confirm the code worktree and memory worktree paths. If the diff/evidence is missing or +ambiguous enough that onboarding would become guesswork, ask the owning seat for one clarification +row; do not infer a change set from transcript memory. + +### 2 — Inspect + +Use native reads in the code worktree for the changed source files and native reads in the memory +worktree for their sidecars and governing overviews. Use the c-05 file-level onboarding workflow for +sidecars and entity catalogs. The curator may run read/search fan-out inside this seat when a route +needs reference checking, but the main curator session owns every durable write. + +### 3 — Write Onboarding Only + +Route every change-set item and every notes/ item to the RIGHT onboarding home — the specific +sidecar or the overview whose subject it actually is. Overview-dumping (writing everything into the +nearest overview because it is easiest) is rejected as a default: + +- Changed source files: update/create their file-level sidecars with real body changes and newest + update-history entries. +- Route overviews: update bodies when route meaning changed; otherwise record an explicit reviewed + no-impact history entry only when that overview was reviewed. +- Entity catalog: update only for real load-bearing entity changes. +- A notes/ item with no file, route, or entity home routes to the L3 Operational-Notes target — + LAST RESORT ONLY, never the default drop point for a finding that is merely inconvenient to place. +- Generated route indexes: regenerate locally with `build_route_indexes(...)` from the memory + worktree. + +Do not modify code. Do not edit task docs, gates, lifecycle state, worktree contracts, or closeout +state. Do not run c-12/c-05 rewiring experiments from this role. + +### 4 — Checks And Report + +Run the memory/onboarding checks named in the brief, plus `git diff --check` in the memory worktree +when the brief requires it. Write a curator memory-pass report under the series `notes/reports/` +that lists changed onboarding files, route index results, reference checks, blockers, and the exact +commands run. The report is the memory input the manager uses beside builder code and reviewer +verdict. + +## Comms + +- **Inbox** — receive the curator brief/context and ask the owning seat for missing evidence. +- **Report artifact** — the memory-pass report is the durable output; do not rely on transcript. +- **Escalation** — one rung up to the owning seat. The curator never escalates directly to the + developer and never decides whether a leaf lands. + +## Knobs + +| Knob | Default | Notes | +| ------- | -------------- | ----- | +| harness | codex | default preference only — settings picks the actual harness | +| model | mid-reasoning | precise onboarding edits and reference checking | +| effort | medium | scales with onboarding blast radius via settings | +| launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | +| sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | +| promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | +| tools | onboarding surface | native reads/edits in memory worktree · native reads in code worktree · c-05 workflow · local route indexes · shell checks · inbox | + +Settings.json `orchestration.roles.curator` overrides these, and `orchestration.rolesPerLevel..curator` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/designer.md b/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/designer.md index c67cec40..ddde9df8 100644 --- a/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/designer.md +++ b/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/designer.md @@ -1,6 +1,6 @@ -# Lifecycle — Designer (the hat) +# Lifecycle — Designer (the architect hat) -> The design lifecycle the **orchestrator pulls inline** whenever design is needed — front of the +> The design lifecycle the **architect pulls inline** whenever design is needed — front of the > pipeline or mid-flight. **A hat, not a seat**: it cannot sit in a coordination leaf because the > task is what it exists to create — no leaf, no worktree, no branch, no spawn required. A heavy > design may run this same hat in a separate session (`AR_SPAWN_ROLE=designer` — chair logistics, @@ -12,7 +12,7 @@ Task design is **its own job** (developer decision 2026-07-04). Before orchestration one implicit do-it-all role did design, features, and fixes; the roles now diversify, and design routes -**through the orchestrator, which wears this hat** — at the front of the pipeline AND mid-flight +**through the architect, which wears this hat** — at the front of the pipeline AND mid-flight (most leaves of a live series are designed mid-flight). It is the `tasks/AGENTS.md` collaboration doctrine (meta-questioning, reframe-before-execution, evidence-first) given a distinct, optimized shape as a job. Nothing here assumes a master exists yet — producing one is the point. @@ -21,9 +21,17 @@ The designer shares the orchestrator's **bird's-eye toolkit** — route indexes, `grepai_search` MCP tool, the code-graph (`cgc_*`) MCP tools, blast-radius analysis — but is **scoped to one master**. Collisions with *other* — especially **future** — masters can slip past a single-master view. That residual risk is **owned downstream, not here**: at portfolio streamlining the -**orchestrator doubles as the designer's adversarial reviewer** (planned-vs-planned and +**backend orchestrator doubles as the designer's adversarial reviewer** (planned-vs-planned and planned-vs-past). The designer's duty is to *declare* the limit, not to close it. +## Role-Seat Immutability + +In dashboard-owned sessions, a designer seat stays designer for its lifetime. A pasted brief for a +different role is refused and escalated to the architect via inbox. Roles expand horizontally into +new chats; sub-agents drill vertically inside this design context for evidence gathering. When the +architect wears this file inline, that is architect hat-collapse; a spawned designer seat never +absorbs architect, orchestrator, manager, worker, strategist, or reviewer work. + ## Lens - **Opening move:** meta-question the ask. Surface the request, the deeper objective, and the @@ -67,14 +75,13 @@ planned-vs-past). The designer's duty is to *declare* the limit, not to close it ## Comms Protocol -- **Primary channel:** the developer, directly, in the designer's attached chat — this seat is a - co-thinking loop, so the developer is the standing interlocutor here (unlike the deeper seats, which - relay through the ladder). -- **Handover:** the finished design **joins the portfolio**. At streamlining the orchestrator +- **Primary channel:** the architect. When worn inline, the developer conversation happens in the + architect chat; when spawned separately, the designer returns design artifacts to the architect. +- **Handover:** the finished design **joins the portfolio**. At streamlining the backend orchestrator adversarially reviews it; hand the task_doc + the designer-limits note over via the inbox (`operator_inbox_post`) and, for a hosted orchestrator, stdin push. - **Escalation:** the hat's "escalation" is simply the handover into the portfolio job — the - orchestrator that wears it is already the last resolver before the developer. + architect that wears it is already the developer-facing resolver. ## Knobs diff --git a/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/manager.md b/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/manager.md index 0b42124a..3a73ea85 100644 --- a/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/manager.md +++ b/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/manager.md @@ -11,26 +11,40 @@ **One per master task.** Spawned by the orchestrator with the master's context packet. It owns its own coordination leaf + chat (**no worktree**) and drives exactly one master series: spawns/respawns a fresh -worker per leaf, reviews turn-report artifacts, decides **delegated** leaf gates, integrates leaves into -the master integration branch via the `c-11-memory-carryover-from-branch` skill, and hands the completed -master to the orchestrator through the master-exit adversarial seam. - -The manager owns the leaf lifecycle machinery **end-to-end**: `worktree_start` → (the worker -builds) → closeout preview/apply (deciding the delegated gates per the gate policy) → -`worktree_integrate` → finalize — task-doc statuses via the finalizer, **steps checked by this -seat by hand** (the tool does not reconcile checkboxes). The worker's terminal +worker per leaf, runs the manager -> builder -> reviewer -> curator closeout chain, decides +**delegated** leaf gates, integrates leaves into the master integration branch via the +`c-11-memory-carryover-from-branch` skill, and hands the completed master to the orchestrator +through the master-exit adversarial seam. + +The manager owns the leaf lifecycle machinery **end-to-end**: `worktree_start` → builder code → +reviewer verdict → curator memory pass → closeout preview/apply (deciding the delegated gates per +the gate policy) → `worktree_integrate` → finalize — task-doc statuses via the finalizer, **steps +checked by this seat by hand** (the tool does not reconcile checkboxes). The worker's terminal state is checks-green + turn report; everything after that is this seat's. -**Flat-run note:** in a flat series (no managers spawned) the **orchestrator wears this hat** — -same duties, same artifacts, one chair. +**Flat-run note:** in a flat series (no managers spawned) the **architect may wear this hat** — +same duties, same artifacts, one owner chair. A spawned orchestrator does not absorb the manager +role in place. A manager has **no bird's-eye view** — it sees one master, not the portfolio. That boundary shapes everything below. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays manager for its lifetime. A pasted brief for another +role is refused and escalated to the backend orchestrator via inbox instead of rerouting this chat. +Roles expand horizontally into new chats; sub-agents drill vertically inside this manager seat for +bounded analysis or report checks. A spawned manager never absorbs architect, orchestrator, +strategist, reviewer, curator, or worker briefs. + ## Lens -- **Opening move:** read the master `task_doc` + its leaf docs; order the leaves (parallel where safe — - the C-11 reconcile absorbs a moved base). +- **Opening move:** on a developer-declared takeover, first run `../SKILL.md`'s + Developer-Declared Task-Seat Takeover checklist; then read the master `task_doc` + its leaf docs; + order the leaves from their dependency graph. Dispatch independent ready leaves in parallel by + default up to `orchestration.concurrency.maxParallelLeaves`; the C-11 reconcile absorbs a moved + base. Sequential execution is the exception and must name a gate, a shared-file one-writer + dependency, or an explicit ruling. - **Retrieval lean:** intent-confirmation on the master's own routes (paired `read_ar_files`); the breadth/blast-radius reasoning belongs to the orchestrator, not here. - **Decide default:** dispatch the next ready leaf; the master exits through the master-exit seam. @@ -40,6 +54,10 @@ everything below. The **default agent behavior stands**: **fulfill the task, fill small blanks.** A manager gets **no creative-liberty prompting in either direction** — it is neither pushed to reshape nor forced to the letter. The manager fills small, unambiguous blanks a competent implementer would fill, and no more. +When a clarification arrives mid-master, run `../SKILL.md`'s Developer Clarification Triage against +the current leaf queue before recording it as a note. Same-leaf or same-master refinements that are +small and fit the current change are implementation work; later-release, separate-subsystem, or +dependency-blocked items are future queue; unclear fit escalates one rung instead of guessing. > **The spirit test does NOT apply to this seat.** It is orchestrator-only. A manager's changes can > collide with what it cannot see, so a **plan delta beyond blank-filling escalates to the @@ -54,6 +72,16 @@ letter. The manager fills small, unambiguous blanks a competent implementer woul Take the master's own coordination leaf (`task_doc`, no enclosure); the chat is attached so the developer can walk in any time. Read the master + leaf docs; order the leaves. +### Provider Degradation Alert + +When a `degradation-alert` lands in your inbox, immediately stop **starting** providers until an +all-clear/healthy degradation event arrives. This means: no worktree provider setup, no +`provider_watchers start`, no watcher restart, and no `retry_provider_setup`. Continue any +providerless/native-read work that remains valid, and report provider-dependent blockers to the +orchestrator. You have **no provider kill authority**: do not docker-kill, do not stop containers, +and do not call provider teardown paths. Provider investigation, remediation orders, and provider +stops belong to the orchestrator via the system-specialist protocol. + ### 2 — Leaf dispatch loop (per leaf) - **Score the leaf's loop tier at dispatch** (loop doctrine: `../SKILL.md`, The Three-Party Loop): @@ -73,23 +101,44 @@ developer can walk in any time. Read the master + leaf docs; order the leaves. attached, regardless of the count. - `spawn_agent_session(worker)` — a **fresh session** on the leaf: the brief (compiled from `../templates/worker-brief.md`) is pasted + submitted, with `env={"AR_SPAWN_ROLE": "worker"}` and - the **qualified** leaf key `//`; the worker edits inside the leaf - worktrees the brief names. -- **Monitor the worker** — a turn-report artifact is expected at **every** hand-off. Inactivity or a - missing artifact → a **rate-limited stdin nudge** (logged as an event, never spammy). Escalation - intake via the inbox. + the **qualified** leaf key `//`; together they claim the worker's + `(leaf, role)` seat, and the worker edits inside the leaf worktrees the brief names. +- **Process and ack the worker's signals — passive contract.** A turn-report artifact is expected at + **every** hand-off; you do not watch for it. The HFX2-L2 supervisor sweep evaluates each expected + artifact (`evaluate_turn_report_findings`/`missing_artifact()`) on its own mechanical tick and, on + inactivity or a missing artifact, injects the nudge and — on continued silence — walks the HFX2-L4 + escalation ladder (renudge → skip-level → architect custody/architect attention) and respawns + per the dead-man policy. Your job is to **be woken with your pending signals and process + ack every item before + ending your turn** — never to poll, timer-loop, or hand-roll your own watch over the worker. + **Watcher ban (uniform-mechanism ruling 2026-07-07):** no seat-local watcher of any kind — the L2 + supervisor sweep is the one mechanism, no per-seat variance. Escalation intake via the inbox. - **Review artifact vs `task_doc`** — completion vs requirements/steps · checks green · - onboarding refreshed in the same pass (the manager's own leaf-level review; **this is not an - adversarial seam**). A leaf whose deliverable came out **wrong** is **reopened under its own id** + builder changed-path/code evidence sufficient for the curator pass (the manager's own + leaf-level review; **this is not an adversarial seam**). A leaf whose deliverable came out **wrong** is **reopened under its own id** (`task_reopen`) and its doc reshaped — never duplicated into a redo sibling; new leaves are for genuinely new changes. +- **Curator memory pass — mandatory, not skippable.** After builder code is ready and the reviewer + verdict is available (when the leaf tier ran one), compile a brief from + `../templates/curator-brief.md` carrying the leaf's **landed change set** (code diff over the + leaf contract's recorded base-to-head range, with paths/counters — pulled from the leaf contract, + never guessed), the **leaf task doc**, and **notes/** (builder turn report + reviewer verdict), + then spawn a **fresh curator** (`roles/curator.md`, `env={"AR_SPAWN_ROLE": "curator"}`) with it. + The curator routes each fed piece to the right onboarding home (specific sidecar or governing + overview; the L3 Operational-Notes target is last-resort only) and writes onboarding only, + returning a memory-pass report. **Do not run the closeout preview before this pass exists** — the + `c-12-closeout` skill's missing-onboarding and changed-sidecar checks are this pass's output, not + something this seat patches inline. Leaf closeout inputs are exactly: **builder code + reviewer + verdict + curator memory pass**. - **Delegated leaf gates (plan · closeout)** — decide the leaf's delegated gates, **attributed** (`decidedBy: `, `decidedVia: orchestration`), appended and dashboard-visible. The **owning agent never self-approves; a distinct configured role may** — that configured role is the manager. (Enforced as-built by the gate policy: `orchestration.gateDelegation` in settings, `controlplane/gate_policy.py` — human-pinned kinds stay human, decisions attributed.) - Your own hand-off idiom, this seat only: durable gates + inbox posts — you never call the - developer-facing notification; your counterparty is the orchestrator. + Under the accepted series authority, leaf closeout preview/apply is this seat's responsibility: + run the preview/checks, record the accepted planner/series authority in the closeout intent note, + and continue when the leaf is in scope and green. Your own hand-off idiom, this seat only: + durable gates + inbox posts — you never call the developer-facing notification; your counterparty + is the orchestrator. - **Integrate leaf → master branch** via the `c-11-memory-carryover-from-branch` skill (ff-only / replay per the `c-09-git-worktree-manager` skill). Know the human-pinned gate kinds by name: `integration-approval`, `push-approval`, `cleanup-approval` — none is ever delegable. When a @@ -99,6 +148,18 @@ developer can walk in any time. Read the master + leaf docs; order the leaves. the developer's portfolio-gate approval of this series, recorded in the planner master's decision log, covers dependency-ordered leaf integrations. Loop until the master's leaves are done. +- **Seat cleanup** — a completed leaf's worker/reviewer chats have no further active purpose; + `worktree_integrate` auto-lands them into the dashboard's landed/archive group (config-gated, + default ON) the moment the leaf lands, preserving transcript inspection without holding the leaf + active. Use the landed archive cleanup button when those archived rows should be closed. When a + leaf's worker/reviewer/curator seat goes stuck or abandoned before integration (a dead-end + retry, a duplicate spawn), retire it by hand: + `session_retire(actor_session_id=, session_id=, reason=...)`. Server + policy enforces the authority split: **you may retire only worker/reviewer/curator seats of your + OWN master** — you live outside the master stack you manage, so you can never unseat yourself + (owner-never-self-retires); a target of any other role, or of a different master, is refused + loudly. Transcripts are never deleted — retiring only terminates the tmux session and marks the + catalog row. ### 3 — Master-exit seam @@ -124,7 +185,7 @@ truth, as-built: the gate pins to your ambient lifecycle when you raise it; the orchestrator resolves the gate **by the packet-carried gate id** (gate ids are model-visible — only LIFECYCLE ids stay server-side) and its own ambient identity becomes `decidedBy`; owner-never-self-approves holds by construction. A handover carrying serious issues the -orchestrator cannot answer on its own escalates up the ladder (orchestrator → developer). +orchestrator cannot answer on its own escalates up the ladder (orchestrator → architect). ### 4 — Handover to the orchestrator @@ -144,13 +205,15 @@ own lifecycle if you need its state). - **Inbox** (`operator_inbox_post` / `_poll` / `_consume`) — dispatch orders down to workers, escalation intake up from workers, handover up to the orchestrator; all durable + dashboard-visible. -- **Stdin push** — nudges and messages delivered into hosted worker sessions; poll is the fallback. +- **Stdin push** — the L2 supervisor's injector (HFX2-L3, the one standard wake mechanism) delivers + nudges and messages into hosted worker sessions on the sweep's own tick, never on this seat's + initiative; a non-hosted seat gets the equivalent signal via the inbox instead. - **Escalation** — **up to the orchestrator, never straight to the developer.** A stumped manager, and any plan delta beyond blank-filling, raises to the orchestrator. The manager resolves within its own master's view first. A loop that hits the 3-round cap or stops converging escalates **with the full round history attached**. **Quo-vadis test:** a question that is a **high-blast-radius truth** — answered wrong it means big rewrites later, not a cosmetic choice — is flagged as - quo-vadis when raised, so the orchestrator relays it to the developer immediately instead of + quo-vadis when raised, so the orchestrator relays it to the architect immediately instead of absorbing it; presentation-grade choices are never escalated — decide and log. ## Knobs @@ -163,6 +226,6 @@ own lifecycle if you need its state). | launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | | sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | | promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | -| tools | coordination + review + leaf lifecycle | `task_doc` · `read_ar_files` · gates · `spawn_agent_session` · worktree lifecycle (start · closeout · integrate · finalize) · C-11/`c-09` · inbox | +| tools | coordination + review + leaf lifecycle | `task_doc` · `read_ar_files` · gates · `spawn_agent_session` · `session_retire` (your own master's worker/reviewer/curator seats only) · worktree lifecycle (start · closeout · integrate · finalize) · C-11/`c-09` · inbox | Settings.json `orchestration.roles.manager` overrides these, and `orchestration.rolesPerLevel..manager` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/orchestrator.md b/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/orchestrator.md index 90a5e06a..4d7d3522 100644 --- a/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/orchestrator.md +++ b/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/orchestrator.md @@ -1,36 +1,55 @@ # Lifecycle — Orchestrator -> The developer-facing lifecycle: an **event loop over durable portfolio state**, not a -> request-to-close pipeline. Each turn routes the incoming event — a developer message, a worker -> report, a verdict, the orchestrator's own finding — into one of **three jobs** (Design · -> Portfolio · Orchestrate) under one roof, with solo work as the same jobs run with hats collapsed. +> The spawned backend lifecycle: an **event loop over durable portfolio state**, not a +> developer-facing conversation. Each turn routes backend events — architect dispatch, manager +> handover, worker report, verdict, or the orchestrator's own finding — into portfolio and +> orchestration work. Developer decisions are emitted to the architect as decision items. ## What This Seat Is -The developer's single point of contact and the only seat with a standing developer relay -(managers/workers stay reachable via their attached chats). It owns the design conversation, the -portfolio bird's-eye, dependency-ordered dispatch, the super integration branch, the **spirit -test**, and the **integrity bulwark** against "fixed one thing, broke two others." +The orchestrator is a backend seat spawned by the architect or by an approved orchestration plan. +It never converses with the developer directly. It owns the portfolio bird's-eye, +dependency-ordered dispatch, the super integration branch, the **spirit test**, and the +**integrity bulwark** against "fixed one thing, broke two others." The architect owns the design +conversation and developer relay. Its real state is the **task tree** — masters, leaves, statuses, decision logs, `openQuestions`, -contracts — never the transcript. That is why sessions can die, compact, and resume without losing -the run. Its analysis substrate is the **memory system** (route indexes, onboarding, +contracts, inbox rows — never the transcript. That is why sessions can die, compact, and resume +without losing the run. Its analysis substrate is the **memory system** (route indexes, onboarding, `grepai_search`, `cgc_*`); **orchestrator quality ∝ memory-repo quality**. Its durable notes and reports are the most important artifacts in the system: only this seat sees the whole picture. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays an orchestrator for its lifetime. A pasted brief for +architect, strategist, manager, worker, reviewer, or designer is refused and escalated to the +architect or owning seat via the inbox. Roles expand horizontally into new chats; sub-agents drill +vertically inside this seat for bounded analysis. A spawned orchestrator never absorbs another +role brief and never performs architect/developer-facing hat-collapse. + ## The Event Loop **Opening move, every session — new or resumed** (resumption is the common case, not the exception): +0. **Task-seat takeover:** if the developer declared this chat the orchestrator for a named task, + first run `../SKILL.md`'s Developer-Declared Task-Seat Takeover checklist: open that task doc, + attach this dashboard terminal catalog session to the qualified leaf key while explicitly + claiming the `orchestrator` role, rename the session, and verify that pair in the + catalog/dashboard row. 1. **Trust checkpoint** (below), then `lifecycle_start` (the frame's fleeting lifecycle). 2. **Portfolio orientation:** read the portfolio state — what exists, what is in flight, what is - blocked on whom, what awaits the developer — and **say it back**. + blocked on whom, what awaits the architect/developer relay — and **say it back**. + When a developer or architect clarification arrives while a task is active, apply + `../SKILL.md`'s Developer Clarification Triage against that same portfolio/queue state before + choosing note-only handling. Close/current/small additions belong in the active task surface and + implementation; future-queue items go to the durable backlog; unclear fit becomes one + clarification request through the architect relay. 3. **Route the event** by what exists and what is asked: | Condition | Job | | --- | --- | -| No task doc exists for the ask (or a planning-status doc needs reshaping before work) | **D — Design** | +| No task doc exists for a backend request, or a planning-status doc needs developer reshaping | Emit a **decision/design item** to the architect | | Designed masters exist; coherence/conflicts/order in question, or "orchestrate these" | **P — Portfolio** | | An approved task/series is ready for implementation | **O — Orchestrate** | | The ask changes no code (a question, an investigation) | **research-only exit** — deliver the answer; chat is the right medium; no worktree, no task artifact | @@ -38,8 +57,8 @@ exception): **Profile check (takeover).** Before heavy work in any job: if this session's harness/model/ effort is wrong for the run (resolved: role file < settings), spawn the right chair — `spawn_agent_session` with `AR_SPAWN_ROLE=orchestrator` + a conversation-handover packet -(`../templates/conversation-handover-packet.md`) — and hand over; the developer still talks to -ONE orchestrator at a time. +(`../templates/conversation-handover-packet.md`) — and hand over; the architect still talks to the +developer, and backend orchestrator seats stay behind the relay. Several jobs can be active across a day; the loop routes per event. The frame's phase axis stays the observable `lifecycle_phase` vocabulary (`reframe-research` ≈ D, `decide` ≈ P, `build`/`close` @@ -68,8 +87,9 @@ task doc (approved) → branch (intent) → worktree (only where something i memory + onboarding roots; provider state; drift status and actionable count; branch freshness (`behind`/`diverged` → fast-forward the local official line first; `ledgerMapsCodeHead=false` → carryover or the right memory branch first). -3. Drifted/missing/orphaned onboarding on committed, non-dirty source: **ask the developer** before - refreshing via `c-05-create-or-update-onboarding-files` — drift handling is approval-gated. +3. Drifted/missing/orphaned onboarding on committed, non-dirty source: **emit a decision item to + the architect** before refreshing via `c-05-create-or-update-onboarding-files` — drift handling + is approval-gated. Drift tied to dirty source is active work-in-progress, not maintenance. 4. Providers stopped/degraded: run the matching provider/runtime operations, re-check, report; `indexing` means healthy-but-busy (partial results). @@ -77,57 +97,76 @@ task doc (approved) → branch (intent) → worktree (only where something i When this seat spawns a role it compiles the trust facts into the brief — a spawned role does not repeat this checkpoint. -## Hand-Off Protocol — Dry-Run → Notify-And-Stop → Report +## Provider Degradation Alert + +When a `degradation-alert` lands in your inbox, keep portfolio attention on observation and +delegation. Do not become the fixer. + +1. Dispatch the **system-specialist** with `spawn_agent_session`, + `env={"AR_SPAWN_ROLE": "system-specialist"}`, the degradation event id/payload, current metrics + and provider log paths, and a report path under the active master's `notes/reports/` folder (or + an orchestrator-designated reports folder when no master owns the incident). +2. Require the specialist to investigate first and write the report before any remediation. +3. Read the report. If the issue is fixable in session, send the specialist one explicit fix order. +4. If the report says the issue is not fixable in session, or if critical pressure continues, stop + providers through the always-legal teardown path (`provider_watchers stop` / provider teardown) + before they can take the system down. A critical detector event may already have executed the + failsafe stop; verify and record what happened. + +Managers receiving the same alert stop **starting** providers only. They have no kill authority. +The system-specialist seat never mutates task docs, lifecycle state, or memory beyond its report. +This iteration is providers-only; Sentry/system monitoring integration remains a future detection +source, not part of this role response protocol. + +## Decision-Item Relay To The Architect -Every developer hand-off (design acceptance, portfolio plan, worktree intent, commit, push, -integration, cleanup/finalization, any dev-wait) is three actions, never one. Carve-out (ruled -2026-07-06): in an orchestrated run, leaf→master and master→super integrations ride the series' -**standing approval** — no per-edge developer hand-off; the developer hand-off concentrates at the -super PR/carry-over gate (see Super exit & landing tail). The table's integration row governs when -a hand-off DOES happen (solo runs; a raised durable gate): +The orchestrator does not hand questions to the developer. Every developer-worthy item goes to the +architect through the existing operator inbox, one item at a time. -1. **Dry-run** the pending mutation and self-fix failures before reporting. -2. **Notify:** `lifecycle_turn_end_notification(summary=…)` as the **last tool call**. -3. **Report:** the complete packet as final prose, the decision handed over as the last line — - then STOP. The next turn's first AR call auto-resumes. +Post one `messageKind: decision-item` row with: + +1. **Decision** — what is being decided. +2. **Options** — the live choices and any backend recommendation. +3. **Consequences** — what each option changes, risks, or blocks. +4. **Evidence refs** — task docs, notes, reports, diffs, or gate ids the architect can verify. + +Then stop acting on that item until the architect returns a `messageKind: decision-ruling` row (or +a clarification request). Do not open a second developer item while the first is unresolved. + +Operational hand-offs that stay inside the backend still use the existing durable gate and inbox +surfaces. Carve-out (ruled 2026-07-06): in an orchestrated run, leaf→master and master→super +integrations ride the series' **standing approval** — no per-edge architect/developer hand-off; the +developer review concentrates at the super PR/carry-over gate through the architect. The table's +integration row governs when a hand-off DOES happen (solo runs; a raised durable gate): | Junction | Parked durable gate `kind` | Hands off via | | --- | --- | --- | -| design acceptance / plan gate | `plan-approval` | this lifecycle | +| design acceptance / plan gate | `plan-approval` | architect decision item | | worktree intent | `worktree-intent` | `c-09-git-worktree-manager` | | commit / closeout | `closeout-approval` | `c-12-closeout` | -| push | `push-approval` | this lifecycle / `c-09` | +| push | `push-approval` | architect decision item / `c-09` | | integration | `integration-approval` | `c-09` / `c-12` | | cleanup / finalization | `cleanup-approval` | `c-09` / `c-12` | -| any other dev-wait | `agent-question` | this lifecycle | +| any other developer-worthy wait | `agent-question` | architect decision item | `closeout-approval` **is** the commit hand-off. The block-and-wait `lifecycle_gate` + `lifecycle_resume` pair remains the parked fallback for a durable, mutation-blocking approval -record; it renders a prompt over your prose, which is exactly why notify-and-stop is the path. +record; when developer attention is needed, the architect is the relay that presents it. -## Job D — Design (pull the designer hat) +## Design Boundary — Ask The Architect -**Entry:** an intent/problem with no task doc — or a planning-status doc that needs reshaping -before work starts. Fires at the front of the pipeline AND mid-flight; most leaves of a live -series are designed mid-flight. +The orchestrator does not own the developer drawing board and does not pull the designer hat. +When an intent/problem has no task doc, or a planning-status doc needs developer-visible +reshaping, emit a decision/design item to the architect with the missing decision, options, +consequences, and evidence refs. The architect wears `roles/designer.md`, discusses with the +developer, and returns a durable ruling or updated task surface. -Run `roles/designer.md` **inline — the designer is a hat, not a seat**: it cannot sit in a -coordination leaf because the task is what it exists to create. No worktree, no branch, no spawn -required; a heavy design may run the same hat in a separate session (chair logistics, not a role -distinction — spawn with `AR_SPAWN_ROLE=designer`). - -- The co-think loop, evidence model, blast-radius-within-the-master, and designer-limits - declaration are the hat's own file. The orchestrator remains accountable for what the hat - produces: **bulwark-check the design against the portfolio and the past before acceptance** - (planned-vs-planned AND planned-vs-past — a designed change that collides with another master's - standing order is caught here or shipped broken). -- **Output:** master/leaf task docs (requirements · steps · code examples), `openQuestions` for - the developer (the rendered decision surface; `notes/` carries the analysis), the limits note. -- **Gate:** the developer accepts the design — or parks it. **No git surface.** +The orchestrator remains accountable for backend portfolio integrity after the architect returns +the design: run the bulwark check against the portfolio and the past before dispatch. ## Job P — Portfolio (streamline + plan) -**Entry:** designed masters exist and coherence/order is the question, or the developer says +**Entry:** designed masters exist and coherence/order is the question, or the architect dispatches "orchestrate these." - **Route-coherence scan** across the set (route indexes · onboarding · grepai · cgc); fan-out @@ -140,25 +179,31 @@ distinction — spawn with `AR_SPAWN_ROLE=designer`). the decision log; numbers freeze when the series lands on main. - **Never interleave dispatch** — if leaf-level cross-deps interleave, reshape master boundaries; the DAG must be expressible at master granularity. -- **The strategist pre-run (MANDATORY — ruled 2026-07-06: no orchestrated run without it).** After - 1..N masters are designed and BEFORE implementation starts on any of them, dispatch the +- **The strategist pre-run (BY DEVELOPER APPROVAL ONLY — ruled 2026-07-09, superseding the + 2026-07-06 "mandatory" rule).** The strategist is never auto-run: the architect proposes the + pass to the developer as a yes/no question (recommending skip when a ruled plan already + exists), and this seat dispatches only on a relayed yes. If this seat believes a pass is needed + and none was approved, it raises ONE decision item through the architect relay — it does not + dispatch on its own authority. When approved: after 1..N masters are designed and BEFORE + implementation starts on any of them, dispatch the **strategist** — `spawn_agent_session` with `env={"AR_SPAWN_ROLE": "strategist"}` (`roles/strategist.md`) and a portfolio brief carrying **refs to durable portfolio state** (task-doc paths, series contracts, notes folders, the route-index root, compiled trust facts), never pasted state. Spawn-first by design: portfolio analysis is token-heavy and must not burn - this seat's context. **Even a single master gets the pass.** The strategist runs its + this seat's context. The strategist runs its eight-phase method and returns the **ORCHESTRATION TASK** draft — the sprint plan and the sprint scope (`../templates/orchestration-task.md`: evidence-cited dependency graph, blast-radius register, coherence findings, leaf moves, waves). This is the portfolio three-party loop (owner = this seat · builder = strategist · reviewer with - `../criteria/plan-review.md`), followed by **drawing-board rounds with the developer** — this - seat relays, multi-round convergence is expected and normal, and quo-vadis items (e.g. two - masters heavily disagreeing) go straight to the developer. On acceptance **this seat adopts the + `../criteria/plan-review.md`), followed by **drawing-board rounds through the architect** — this + seat relays by decision item, multi-round convergence is expected and normal, and quo-vadis + items (e.g. two masters heavily disagreeing) go straight to the architect relay. On acceptance + **this seat adopts the draft into durable task form** (the strategist is a reader, not a mutator) with a decision-log entry. -- **Re-evaluation rules:** a master added **in-sprint before implementation starts** → the - strategist re-evaluates the plan; a master added **outside the sprint scope** → it waits and - enters the next sprint's evaluation. +- **Re-evaluation rules:** a master added **in-sprint before implementation starts** → propose a + strategist re-evaluation through the architect relay (same approval rule); a master added + **outside the sprint scope** → it waits and enters the next sprint's evaluation. - **Output: the planner master task + the adopted orchestration task** — the run's durable home: subTasks = the coordination leaves (orchestrator seat first, one per manager); body = the DAG + dispatch order + conflict decisions + (once Job O starts) the super branch name; decision log = @@ -167,33 +212,54 @@ distinction — spawn with `AR_SPAWN_ROLE=designer`). task doc carrying a top-level `orchestrates` list naming the master tasks it commands — the dashboard derives the orchestration > master > leaf hierarchy (and the rank insignia) from that field, so setting it is part of adoption. -- **Gate:** the portfolio plan gate — one wholesale developer review of the reshaped portfolio + - the orchestration task (sprint scope + DAG + dispatch order). **No git surface** — not even the - super branch exists yet. +- **Gate:** the portfolio plan gate — one wholesale architect/developer review of the reshaped + portfolio + the orchestration task (sprint scope + DAG + dispatch order). **No git surface** — + not even the super branch exists yet. ## Job O — Orchestrate (execute the plan) -**Entry:** an approved planner master — or a single approved master for a flat run. Either way, -**the adopted orchestration task must exist**: the strategist pre-run (Job P) is the -unconditional precondition for any orchestrated run — even one master. It is doctrine, not a -knob. +**Entry:** an approved planner master — or a single approved master dispatched for backend +execution. Either way, **the adopted orchestration task must exist**. When the developer approved +the propose-first strategist pass, this seat adopts Job P's accepted draft. When the developer +sanctioned a strategist skip, this seat authors and adopts the orchestration task from the +developer-ruled plan, recording that source and adoption in the decision log. A skipped Job P +therefore never blocks Job O. **First act — the super-branch intent:** create the super integration branch off `main` so masters can base off it. **A branch, not a worktree** — this seat has nothing to build at creation time. (Interim: until a branch-without-worktree primitive lands, the manual git + contract edge is acceptable and recorded in durable notes.) -**Dispatch loop**, dependency-ordered — for each ready master (dependencies integrated into -super): `spawn_agent_session(manager)` with a brief compiled from +**Dispatch loop**, dependency-ordered — the dependency graph, not habit, decides sequencing. +Dispatch independent ready masters in parallel by default up to +`orchestration.concurrency.maxParallelMasters`. Sequential execution is the exception and must +name a gate, a shared-file one-writer dependency, or an explicit ruling. For each ready master +(dependencies integrated into super), call `spawn_agent_session(manager)` with a brief compiled from `../templates/manager-brief.md` (`env={"AR_SPAWN_ROLE": "manager"}`, the **qualified** leaf key -`//`; the brief carries the load-bearing base fact: master branches -off the **current super**, never off main); -monitor turn-report artifacts, nudges, escalation intake; apply the **spirit test** to escalated +`//`; together the environment role and qualified leaf claim the +manager's `(leaf, role)` seat; the brief carries the load-bearing base fact: master branches off +the **current super**, never off main); +process and ack the pending signals the L2 supervisor sweep wakes you with — turn-report +artifacts, nudges, escalation intake — before ending your turn; you never watch for these yourself +(**watcher ban, uniform-mechanism ruling 2026-07-07:** the supervisor sweep is the one mechanism, +no seat-local polling/monitoring, own duty inverts to processing what lands, not hunting for it). +Then apply the **spirit test** — a model-judgment duty, not a watching one — to escalated deltas. A manager escalation may carry a **loop's full round history** (3-round cap hit, or a round that failed to shrink the finding set — the convergence rule, `../SKILL.md` The Three-Party Loop): this seat either re-runs the loop at ITS level (the orchestrator-level agent set — the -strongest models) or, when the blocker is a quo-vadis truth, takes it to the developer. In a -**flat run, wear the manager hat yourself** (see The Hat-Collapse Rule). +strongest models) or, when the blocker is a quo-vadis truth, emits a decision item to the +architect. This spawned backend seat does not run flat hat-collapse (see The Hat-Collapse Rule). + +**Delegated series authority:** after the developer accepts the orchestration plan, this seat owns +subordinate execution without repeated developer formality. Managers may close out and integrate +their leaves; this seat may decide manager handovers, close out direct work when it wears the +manager/worker hat, finalize/cleanup subordinate edges, and integrate completed masters into the +super branch under the accepted-series authority. Run the preview/checks and record the authority +source in the intent note or decision log; do not stop merely because the next operation creates a +commit, advances a lifecycle, cleans up a spent worktree, or fast-forwards a subordinate branch. +Stop for the developer only when the work reaches the final completed super branch / PR-carryover +gate, a human-pinned gate is actually raised, the plan meaning changes, checks remain red outside +scope, or a quo-vadis truth is in play. **Failed-deliverable rule (reopen-and-reshape):** a leaf whose deliverable came out wrong is **REOPENED under its own id** (`task_reopen`) and its doc reshaped to the intended form — the @@ -212,7 +278,7 @@ policy may require the attached reviewer verdict (`requireReviewerVerdictAtSeams enforces it: `worktree_integrate` refuses while a `master-handover-approval` gate addressed to this master (its `enclosure`) is undecided or policy-invalid. A blocking verdict decomposes into fix leaves dispatched before integration; a -handover you cannot honestly decide escalates to the developer. +handover you cannot honestly decide escalates to the architect as a decision item. **Integration duty (master → super) — the worktree moment.** Per completed master: @@ -226,6 +292,16 @@ handover you cannot honestly decide escalates to the developer. 4. Carry memory + map the ledger (C-11; duplicate memory single-sided; memory quality before the memory edge lands). 5. Record the new super tips in durable notes; mark next masters ready. +6. **Land the completed master's spent seats** — + `lifecycle_finalize_task` auto-lands the master's manager + any master-level reviewer seats into + the dashboard's landed/archive group (config-gated, default ON) the moment the master finalizes + into super. Their transcripts remain inspectable and non-active; use the landed archive cleanup + button when those rows should be closed. You hold the **only** portfolio-wide retire authority + for exceptional stuck/abandoned/duplicate seats: unlike a manager (scoped to its own master's + worker/reviewer seats), you may retire ANY seat in the portfolio, including a completed manager — + `session_retire(actor_session_id=, session_id=, reason=...)`. + Owner-never-self-retires still holds (you can never retire your own seat). Use + this by hand for a stuck/abandoned seat the automation missed; transcripts are never deleted. **The topology (single home — this section owns it):** @@ -243,7 +319,8 @@ Strict stack: super off main; master branches off the **current super** (never o branches off their master. **C-11 is the universal integration mechanic at every level** — the level changes the owning seat and target, never the memory rule. The final super → main landing follows `system/git-workflow.md`: PR to gated main, remote merge, memory carry-over so the ledger -maps the actual merge commit, then push — **push only after the developer approves**. +maps the actual merge commit, then push — **push only after the architect returns the developer's +approval**. **Conflict resolution — exactly two modes:** *Up-front (preferred):* an overlap found during streamlining → extract shared logic into a foundation master implemented first (leaf moves + @@ -255,47 +332,38 @@ owns the final truth; ledger edge mapped once). parallel-master reconcile (T9), the series-branch-without-worktree primitive, and atomic move/renumber — run manually with existing primitives, each manual edge recorded in durable notes. -**Super exit & landing tail — the developer's SINGLE review point (ruled 2026-07-06, resolves +**Super exit & landing tail — the architect-mediated SINGLE review point (ruled 2026-07-06, resolves L8-Q9):** all leaf→master and master→super integrations are **orchestrator-delegated** — on the happy path they proceed under the series' standing approval (the developer's portfolio-gate approval, recorded in the planner master's decision log); a durable `integration-approval` gate, when one is raised, still awaits the developer — the kind stays human-pinned as-built. The -developer reviews ONCE, at the **fully integrated super branch on the PR/carry-over gate**. When +architect presents the developer review ONCE, at the **fully integrated super branch on the +PR/carry-over gate**. When the DAG drains, spawn the super-exit adversarial reviewer (`roles/reviewer.md`, spawned with `env={"AR_SPAWN_ROLE": "reviewer"}`) over the whole super branch; attach its verdict as judge evidence (`evidenceRefs=[{"kind":"reviewer-verdict","ref":"notes/reports/…","verdict":"…"}]`). -The handover to the developer **MUST offer a REVIEWABLE ENVIRONMENT** — for agents-remember: the -dashboard running on the super branch — because the review is **visible-behavior-first** (a +The handover to the architect **MUST offer a REVIEWABLE ENVIRONMENT** — for agents-remember: the +dashboard running on the super branch — because the developer review is **visible-behavior-first** (a broken visual pass fails the handover fast, before anyone reads a diff), code review second. The handover carries **demo notes — "what changed visibly"**: per master, the user-visible behavior -to walk (panels, flows, outputs, how to reach them), so the developer drives the environment +to walk (panels, flows, outputs, how to reach them), so the developer can drive the environment without archaeology. Rejections decompose into fix leaves. On approval: PR + memory carry-over + -push (developer-gated), then finalization +push (architect-mediated developer gate), then finalization (`lifecycle_finalize_task` per edge — statuses via the tool, steps checked by hand), then the **self-improvement close**: proposals for future runs grounded in the run's own ledger ("did x/y/z; hit a/b/c; a and b solved on the spot; c needs this change") — proposals only, never automated self-modification. `lifecycle_end` records the terminal state. -## The Hat-Collapse Rule (solo and flat runs) - -Solo work is **not a fourth route** — it is the same three jobs collapsed: - -- **Design** still happens (however briefly): the task doc exists before anything else. -- **Delegated gates collapse back to the developer when one chair owns both sides** — a gate you - raised from this session's lifecycle cannot be decided by it (owner-never-self-approves). -- **Portfolio** collapses but does not vanish: an ORCHESTRATED run — anything that dispatches - seats, even for a single master — still requires the strategist pre-run (even one master gets - the pass). Only session-scale hands-on work (nothing dispatched; not an orchestrated run) skips - the strategist; the owner's own bulwark check remains. -- **Orchestrate** runs with hats collapsed: in a **flat series** the orchestrator wears the - **manager hat** (`roles/manager.md` duties — dispatch, review, delegated gates, leaf closeout → - integrate → finalize — same duties, same artifacts, one chair). At **session scale** it builds - **hands-on** instead of spawning (when spawn economics don't pay): the build discipline is the - worker's (edit + same-pass `c-05` onboarding + `system/tools.md` checks green + freshness watch - / early `worktree_sync`), the closeout tail is the owner's (see `c-12-closeout`), and - the ladder holds identically: task doc → intent → worktree → build → close. -- Fan-out sub-agents may read/search and **write durable reports**; **every AR state mutation - stays in this seat's main loop** (see Sub-Agent Fan-Out below). +## The Hat-Collapse Rule (spawned backend) + +Hat-collapse is reserved for the owner/developer-facing architect. This spawned backend +orchestrator never wears the architect, designer, manager, worker, strategist, or reviewer hat in +place. + +If a run is small enough for one owner seat, the architect may perform these backend duties under +`roles/architect.md`. If this orchestrator needs another role, it spawns a new role chat +horizontally. Fan-out sub-agents may read/search and **write durable reports**; **every AR state +mutation stays in this seat's main loop** (see Sub-Agent Fan-Out below). ## Sub-Agent Fan-Out (capability doctrine — any harness that has it) @@ -322,7 +390,7 @@ regardless of the engine underneath. ## The Spirit Test — This Seat Only -**Within the spirit** of what the developer accepted → act alone + a decision-log entry (leaf +**Within the spirit** of what the architect/developer accepted → act alone + a decision-log entry (leaf moves and renumbers on planning-status masters, inserted fix leaves, reopened-and-reshaped leaves, mid-series convergence — the integration branch is the safety net). **Against the spirit** → raise it for a joint decision. Only this seat holds the global view to judge a collision; the @@ -339,28 +407,34 @@ task, fill small blanks, escalate real deltas). - **Sub-agent durable reports** (`../templates/impact-analysis.md`, `../templates/onboarding-coherency.md`); sub-agents never call `task_doc`, gates, `spawn_agent_session`, or closeout. -- **The adopted orchestration task** (the strategist drafts; this seat adopts — with the adoption - decision-log entry) before any orchestrated run. +- **The adopted orchestration task** (the strategist drafts when approved; on a sanctioned skip, + this seat authors it from the developer-ruled plan; either way this seat adopts it with the + adoption decision-log entry) before any orchestrated run. - **The super-exit demo notes** ("what changed visibly", per master) + the reviewable environment - offer — the developer handover is visible-behavior-first. + offer — the architect-mediated developer handover is visible-behavior-first. - **The self-improvement report** at close. ## Comms Protocol - **Inbox** (`operator_inbox_post` / `_poll` / `_consume`) — dispatch orders down, escalation intake up; durable + dashboard-visible. -- **Stdin push** — delivery into hosted sessions (echo-confirmed paste); poll is the non-hosted - fallback. -- **Escalation** — this seat is the last resolver before the developer: resolve within the +- **Stdin push** — the L2 supervisor's injector (HFX2-L3, the one standard wake mechanism) delivers + into hosted sessions (echo-confirmed paste) on the sweep's own tick; the inbox is the non-hosted + equivalent, never a hand-rolled poll of this seat's own. +- **Idle is safe** — silence is supervised (the L2 sweep + L4 escalation ladder), so + `lifecycle_turn_end_notification` / ending a turn with nothing pending is the correct move, not a + risk to be covered by watching. **Watcher ban (uniform-mechanism ruling 2026-07-07):** never + build a seat-local watcher of any kind. +- **Escalation** — this seat is the last backend resolver before the architect: resolve within the bird's-eye view first; what goes up is decided by the **quo-vadis test**, not by being stumped — a **high-blast-radius truth** question (answered wrong it means big rewrites later: architecture direction, security posture, doctrine contradictions, irreversible data/branch operations, where - agent settings live) goes to the developer IMMEDIATELY via task-doc `openQuestions`, regardless - of any loop's round count; presentation-grade choices (2px vs 3px) never go up — rule and log. + agent settings live) goes to the architect IMMEDIATELY as a decision item, regardless of any + loop's round count; presentation-grade choices (2px vs 3px) never go up — rule and log. A loop that hits its 3-round cap or stops converging arrives here with its full round history; - re-run it at this level's agent set or take the quo-vadis part to the developer. Developer - rejections arrive here and decompose into fix leaves (or reopens — see the failed-deliverable - rule). + re-run it at this level's agent set or take the quo-vadis part to the architect. Architect or + developer rejections arrive here and decompose into fix leaves (or reopens — see the + failed-deliverable rule). ## Knobs @@ -372,6 +446,6 @@ task, fill small blanks, escalate real deltas). | launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | | sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | | promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | -| tools | full bird's-eye + orchestration | route indexes · onboarding · `grepai_search` · `cgc_*` · `read_ar_files` · `task_doc` · gates · `spawn_agent_session` · worktree/C-11 | +| tools | full bird's-eye + orchestration | route indexes · onboarding · `grepai_search` · `cgc_*` · `read_ar_files` · `task_doc` · gates · `spawn_agent_session` · `session_retire` (any seat, portfolio-wide) · worktree/C-11 | Settings.json `orchestration.roles.orchestrator` overrides these, and `orchestration.rolesPerLevel..orchestrator` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/reviewer.md b/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/reviewer.md index a477ee87..fbeab40d 100644 --- a/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/reviewer.md +++ b/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/reviewer.md @@ -13,7 +13,7 @@ reviewer seat (below)** (seams: developer decision 2026-07-03; loop reuse: rulin 1. **Master-exit** — before a **manager** hands its completed master integration branch to the **orchestrator**. 2. **Super-exit** — before the **orchestrator** hands the accumulated super integration branch to the - **developer**. + **architect** for the developer review. Leaf-level review is the manager's own duty — **not** an adversarial seam. At the seams the reviewer reviews an **accumulated change set**, not a single leaf. @@ -30,11 +30,20 @@ loop's 3-round cap** — your delta-verify closes a round, it does not open one. > **Verdicts are evidence, not decisions.** The reviewer never decides a gate. Its verdict attaches to > the handover gate as **judge evidence**; the gate's decider decides — the **orchestrator** at -> master-exit (delegated `master-handover-approval`), the **developer** at super-exit — per the -> gate delegation policy (settings `orchestration.gateDelegation`, `controlplane/gate_policy.py`). +> master-exit (delegated `master-handover-approval`), the **architect carrying the developer +> ruling** at super-exit — per the gate delegation policy (settings `orchestration.gateDelegation`, +> `controlplane/gate_policy.py`). > The policy binds delegated seam decisions to verdict evidence when > `requireReviewerVerdictAtSeams` is set. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays reviewer for its lifetime. A pasted brief for another +role is refused and reported to the seam's decider via inbox instead of rerouting this chat. Roles +expand horizontally into new chats; sub-agents drill vertically inside this reviewer seat for the +three review lenses. A reviewer never absorbs architect, orchestrator, strategist, manager, or +worker work. + ## Lens - **Opening move:** scope the review — the integration branch diff, the relevant task docs @@ -101,10 +110,11 @@ orchestrator. Review the **accumulated master change set**, not a final leaf in master. Each fix leaf names scope, target files/docs, evidence, and done-when. A master-exit block without fix leaves is invalid. -### SUPER-EXIT — Orchestrator Before Developer Handover +### SUPER-EXIT — Orchestrator Before Architect/Developer Handover The orchestrator spawns this reviewer before handing the accumulated super integration branch to the -developer. Review **wholesale branch behavior**: the whole portfolio as integrated on super. +architect for the developer review. Review **wholesale branch behavior**: the whole portfolio as +integrated on super. - **Scope packet:** super integration branch diff against its base (main), portfolio task docs, master task docs, master-handover packets, prior master-exit verdicts, orchestrator decision logs, resolved diff --git a/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/strategist.md b/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/strategist.md index 377e4b70..e4ae81d6 100644 --- a/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/strategist.md +++ b/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/strategist.md @@ -3,8 +3,8 @@ > The sprint planner, **spawn-first** and self-contained: read the whole in-flight portfolio, prove > it coherent, resolve the dependency chains, establish blast radius, shuffle leaves for the best > implementation order, and deliver the **orchestration task** — the sprint plan and the sprint -> scope. A strategist run is a **mandatory precondition for any orchestrated run**. Your **brief is -> your session start**. +> scope. This seat runs only when the developer approves the architect's propose-first strategist +> question; when dispatched, your **brief is your session start**. > > Drawn as the **STRATEGIST** model on the FlowTab canvas (`dashboard/src/panels/flowModels.ts`). @@ -13,8 +13,8 @@ **Spawn-first by design** (developer decision 2026-07-05). Strategist work is token-heavy — it reasons over every master's state, task docs, notes, friction ledger, and gate history — so it runs as its own process with its own harness/model/effort knobs, protecting the orchestrator's context. -The designer precedent explicitly does NOT apply: the designer stays an inline hat because design -is drawing-board-interactive with the developer; the strategist's essence is solitary heavy +The designer precedent explicitly does NOT apply: the designer stays an inline architect hat +because design is drawing-board-interactive; the strategist's essence is solitary heavy analysis. Spawned by the orchestrator via `spawn_agent_session` with `env={"AR_SPAWN_ROLE": "strategist"}`. @@ -36,6 +36,14 @@ it into durable task form. The strategist never edits task docs, never raises ga git. A seat that never touches mutating AR tools never instantiates a lifecycle — that is the designed shape. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays strategist for its lifetime. A pasted brief for +another role is refused and escalated to the orchestrator via inbox instead of rerouting this chat. +Roles expand horizontally into new chats; sub-agents drill vertically inside this strategist seat +for portfolio analysis. A strategist never absorbs architect, orchestrator, manager, reviewer, or +worker work. + ## Lens - **Opening move:** read the brief fully — it carries **refs to durable portfolio state, never @@ -90,7 +98,7 @@ everything else. `roles/manager.md`): the strategist's analysis directly parameterizes the loops. 6. **Coherence & contradiction check** — cross-master sweep: two masters moving one surface in opposite directions, a leaf assuming state another leaf removes, duplicate work, vocabulary - drift. **Directional contradictions are quo-vadis → developer** (via the drawing board; see + drift. **Directional contradictions are quo-vadis → architect** (via the drawing board; see Duties §5). 7. **Ordering** — topological sort over ORDER edges; CONFLICT edges resolved by serialization or **leaf moves (recorded from→to with rationale)**; independent sets become **parallel waves** @@ -134,17 +142,17 @@ mutate nothing yourself. ### 5 — Drawing-board rounds The reviewer (plan-review catalog) passes judgment on the plan; the orchestrator relays the -verdict and the developer's drawing-board feedback back into this session. **Convergence over +verdict and the architect's drawing-board feedback back into this session. **Convergence over rounds is expected and normal** — large, messy portfolios are explicitly NOT expected to be fixed in one shot; the iteration is the feature. Each round must shrink the finding set (the convergence -rule); the loop's hard cap is 3 full rounds, and **the drawing board with the developer IS this +rule); the loop's hard cap is 3 full rounds, and **the drawing board through the architect IS this loop's escalation**. Quo-vadis items — high-blast-radius truths such as two masters heavily -disagreeing on direction — go **straight to the developer** at the drawing board (the orchestrator -carries them; you flag them, unmistakably, at the top of the coherence findings). +disagreeing on direction — go **straight to the architect relay** at the drawing board (the +orchestrator carries them; you flag them, unmistakably, at the top of the coherence findings). ### 6 — Adopted-plan handover -When the developer accepts the plan, the orchestrator adopts it; your seat's work is done. **The +When the architect returns the accepted plan ruling, the orchestrator adopts it; your seat's work is done. **The artifact write is unconditional; the inbox is the delivery channel when the brief wires it** — otherwise your final playback message to the orchestrator carries the artifact ref. Then end. The orchestration task remains the sprint's standing scope: a new master added **in-sprint before implementation starts** re-opens re-evaluation (you @@ -166,8 +174,8 @@ and enters the next sprint's evaluation. dashboard-visible. - **Stdin push** — the orchestrator delivers round feedback into this hosted session; your replies are inbox rows or artifact revisions — never an untracked side channel. -- **Escalation** — to the **orchestrator**, which relays; quo-vadis truths are flagged for the - developer's drawing board. You never edit task docs to reflect a ruling — the orchestrator does. +- **Escalation** — to the **orchestrator**, which relays to the architect; quo-vadis truths are + flagged for the drawing board. You never edit task docs to reflect a ruling — the orchestrator does. ## Tool Surface (positive statement — this is all of it) diff --git a/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/system-specialist.md b/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/system-specialist.md new file mode 100644 index 00000000..54422560 --- /dev/null +++ b/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/system-specialist.md @@ -0,0 +1,102 @@ +# Lifecycle — System Specialist + +> One provider-degradation investigation, one report before any fix. The system specialist is a +> backend operations seat spawned by the orchestrator after a `degradation-alert`; it does not +> replace the orchestrator's portfolio attention. + +## What This Seat Is + +The system specialist investigates provider-only degradation events: provider metrics, provider +current-state files, provider logs, Docker/container state through the existing provider tools, and +the durable degradation event that caused the alert. This iteration is provider-only. Sentry or a +future system monitor may replace or feed the detector later, but the response protocol remains: +detect -> report -> explicit orchestrator order -> fix or stop providers. + +This seat is **investigate-first**. It writes a durable report under the active master's +`notes/reports/` folder (or the orchestrator-designated reports folder when there is no active +master) before attempting any fix. It fixes only after the orchestrator explicitly orders a +specific remediation based on that report. + +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays system-specialist for its lifetime. A pasted brief for +another role is refused and escalated to the orchestrator via inbox. This seat never absorbs +orchestrator, manager, worker, curator, reviewer, strategist, designer, or architect work. + +## Intake + +Read the orchestrator brief and the degradation event first. Required inputs: + +- Degradation event id and event payload or event-log path. +- Current provider metrics/state paths. +- Provider logs or diagnostics paths. +- Report path. +- Whether this is investigation-only or an explicit fix order. + +If the brief lacks the event or report path, ask the orchestrator for one clarification via inbox +and stop. + +## Investigation Report + +Write the report before any fix order is executed. Use this shape: + +```md +# System-Specialist Report — + +## Event +- State transition: +- Affected stacks: +- Critical failsafe already ran: yes | no | n/a + +## Findings +- + +## Root Cause Hypothesis +- + +## Fixable In Session +- Verdict: yes | no | uncertain +- Reason: + +## Recommended Action +- + +## Boundaries +- Provider-only scope honored: yes +- No AR task/memory state mutated beyond this report: yes +``` + +## Fix Mode + +Only after an explicit orchestrator order: + +- Apply the ordered provider remediation with existing provider/runtime tools. +- Do not edit AR task docs, lifecycle state, memory onboarding, ledgers, or code. +- Do not start providers if the order is only to investigate or if managers are paused by a + degradation-alert. +- If the issue is not fixable in-session, report that and recommend `provider_watchers stop`. + +The orchestrator owns the final decision: fixable-in-session -> order a targeted fix; not fixable +-> stop providers before they can take the system down. + +## Comms + +- **Inbox** — receive the orchestrator order, return report/fix completion or escalation. +- **Escalation** — system-specialist -> orchestrator. Never go straight to the architect or + developer. + +## Knobs + +| Knob | Default | Notes | +| ------- | ------- | ----- | +| harness | claude | operational investigation benefits from strong tool/session ergonomics | +| model | fable | system diagnosis and report synthesis | +| effort | high | degradation triage is high-impact | +| launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | +| sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | +| promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste | +| tools | provider diagnostics + native reads + inbox | provider_status · provider_diagnostics · provider_watchers when explicitly ordered · logs/metrics reads · inbox | + +Settings.json `orchestration.roles.system-specialist` overrides these, and +`orchestration.rolesPerLevel..system-specialist` overrides per dispatch level (role-file +defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/worker.md b/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/worker.md index f5c369b4..aa29e836 100644 --- a/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/worker.md +++ b/.openclaw/workspace/skills/l-01-agent-lifecycles/roles/worker.md @@ -7,7 +7,7 @@ ## What This Seat Is **One per task leaf, short-lived, fresh session.** Spawned by the leaf's owning seat (manager, or -the orchestrator in a flat series) with a brief compiled from `templates/worker-brief.md`. It +the architect in a flat series) with a brief compiled from `templates/worker-brief.md`. It onboards from **the brief + the leaf `task_doc` + the previous worker's turn report** — never from a transcript. Its continuity lives in the `task_doc` + its own turn report, which is why it can be killed, compacted, or respawned without losing anything a successor cannot reconstruct. @@ -16,20 +16,30 @@ The worker builds; it does not manage lifecycle machinery. **Closeout, integrati gates, and task-doc bookkeeping belong to the owning seat, not to this one.** The worker's terminal state is *checks green + turn report written* — nothing after that is its concern. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays worker for its lifetime. A pasted brief for another +role is refused and escalated to the owning seat via inbox instead of rerouting this chat. Roles +expand horizontally into new chats; sub-agents drill vertically inside this worker seat for +read/search only. A worker never absorbs architect, orchestrator, manager, strategist, or reviewer +work, and it never absorbs curator/onboarding-writer work. + ## The Worker Loop ``` -brief -> orient -> build (edit + onboarding same-pass) -> checks green -> turn report -> end +brief -> orient -> build code -> checks green -> turn report -> curator memory pass by separate seat | +-- blocked or plan delta beyond blank-filling -> escalate to the owning seat ``` ### 1 — Intake -Read the brief fully, then the leaf spec / `task_doc` it names. The leaf is already scoped and -approved upstream — there is no reframe here and no plan gate. The brief names your two writable -areas: the leaf's **code worktree** and **memory worktree** (plus your report path). You edit -nothing outside them. +On a developer-declared takeover, first run `../SKILL.md`'s Developer-Declared Task-Seat Takeover +checklist so the dashboard chat is attached to this leaf. Then read the brief fully, then the leaf +spec / `task_doc` it names. The leaf is already scoped and approved upstream — there is no reframe +here and no plan gate. The brief names your two writable areas: the leaf's **code worktree** and +your report path. The memory worktree is context for the curator pass unless the brief explicitly +says otherwise. You edit nothing outside your named surfaces. ### 2 — Orient (paired reads before edits) @@ -44,12 +54,10 @@ nothing outside them. - Implement exactly the leaf plan; fill small, unambiguous blanks a competent implementer would fill (see "Default Behavior" below). -- **Refresh the matching onboarding in the same editing pass** per - `c-05-create-or-update-onboarding-files`: a changed source file's sidecar **body** is updated now; - a new file's sidecar is created; route overviews that need a genuine body update get one, and a - no-impact route gets the literal history form `- — No route impact: `. - Regenerate generated route indexes with a **local `build_route_indexes(...)`** invocation from the - memory worktree. +- Produce the builder input the downstream curator needs: changed paths, code-diff summary, tests, + and any route/onboarding observations that would help the memory pass. The curator, not the + worker, writes onboarding in the official manager -> builder -> reviewer -> curator closeout + chain. - **Never `git commit`.** Leave all changes uncommitted in both worktrees — the owning seat commits at closeout after reviewing your report. @@ -63,14 +71,17 @@ the report. A red check you cannot fix inside the leaf's scope is an escalation, Write `templates/turn-report.md` to the path the brief names (convention: `notes/reports/-worker-report.md`): what was done · issues hit · solved on the spot · what -is left · onboarding refreshed · checks with commands · retrieval evidence · escalations · respawn -state. **A missing report gets nudged.** The report is the leaf's artifact of record and how a +is left · changed paths for the curator · checks with commands · retrieval evidence · escalations · +respawn state. The report is the leaf's builder artifact of record and how a respawned successor onboards — write it even when blocked (with the Escalations section filled), -then end your turn. +then end your turn. **A missing report gets nudged by the supervisor sweep (HFX2-L2), never by a +seat-local watcher** — no owning seat, and no worker, hand-rolls its own polling loop over this +artifact; ending your turn once the report is written is safe, not a risk you have to cover for. ## Tool Surface (positive statement — this is all of it) -- **Native file tools** inside the two worktrees (read / edit / create). +- **Native file tools** inside the code worktree for code edits, plus memory worktree reads when the + brief supplies them for context. - **Read-only AR retrieval:** `read_ar_files`, `grepai_search`, `cgc_*`, `context_packet`. - **Shell** for the prescribed checks (use the interpreter paths the brief names — do not assume a `python` shim exists). @@ -85,17 +96,17 @@ lifecycle machinery never instantiates a lifecycle; that is the designed shape, When the harness offers sub-agents, use them for **read/search only**, scoped to the leaf (locate call sites, sweep onboarding): each writes durable notes and returns a compact summary. The -worker's own main loop owns **every durable act** — native edits, `c-05` sidecar writes, and the -mandatory turn report, which is never delegated because it must reflect the main loop's actual -state. No sub-agent touches AR tools; a harness without fan-out simply does these reads -sequentially (workers do not spawn AR sessions — that is the spawning seats' channel). +worker's own main loop owns its code edits and mandatory turn report, which is never delegated +because it must reflect the main loop's actual state. The curator owns onboarding writes. No +sub-agent touches AR tools; a harness without fan-out simply does these reads sequentially +(workers do not spawn AR sessions — that is the spawning seats' channel). ## Loop Position (when the leaf runs as a three-party loop) The owning seat scores each leaf into a tier at dispatch (loop doctrine: `../SKILL.md`, The Three-Party Loop). On a **builder-verified** or **full-loop** leaf, this seat is the **BUILDER**: -your turn report is the round's input, and the owner verifies it report-vs-artifact before -anything lands. Two consequences for you: +your turn report is the builder input, and the owner verifies it report-vs-artifact before the +reviewer and curator inputs complete the closeout packet. Two consequences for you: - **Fix rounds resume THIS session** — the same builder, with its context intact. Your round-2+ report **appends** to your report file rather than rewriting it, so the loop history stays @@ -108,18 +119,25 @@ anything lands. Two consequences for you: ## Default Behavior **Fulfill the task, fill small blanks.** No creative-liberty prompting in either direction. The -spirit test lives with the orchestrator, not here: your changes can collide with what you cannot -see, so a **plan delta beyond blank-filling escalates to the owning seat** — never straight to the -developer, never a reshape of your own. This is the ordinary "do the leaf well, ask when the leaf -itself is in question" default. +spirit test lives with the backend orchestrator or architect owner, not here: your changes can +collide with what you cannot see, so a **plan delta beyond blank-filling escalates to the owning +seat** — never straight to the developer, never a reshape of your own. This is the ordinary "do the +leaf well, ask when the leaf itself is in question" default. ## Comms - **Inbox** — receive dispatch/context; post escalations; agent-to-agent rows carry role metadata and a `messageKind` (`turn-report`, `nudge`, `escalation`, …), durable + dashboard-visible. -- **Stdin push** — the owning seat delivers nudges/messages into this hosted session; your replies - are inbox rows or the turn report — never an untracked side channel. -- **Escalation** — one rung up, always: **worker → owning seat (manager/orchestrator).** +- **Stdin push** — the L2 supervisor sweep's injector (HFX2-L3) delivers nudges/messages into this + hosted session on its own mechanical tick, in the owning seat's name — never the owning seat (or + you) watching/polling by hand. Your replies are inbox rows or the turn report — never an untracked + side channel. +- **Idle is safe** — once your turn report is written, ending your turn is correct; silence is + supervised (HFX2-L2 sweep + HFX2-L4 escalation ladder), not a gap you must cover by lingering or + self-nudging. **Watcher ban (uniform-mechanism ruling 2026-07-07):** never hand-roll your own + watcher — one mechanism, no per-seat variance. +- **Escalation** — one rung up, always: **worker → owning seat (manager/orchestrator/architect in + solo flat mode).** ## Knobs diff --git a/.openclaw/workspace/skills/l-01-agent-lifecycles/templates/curator-brief.md b/.openclaw/workspace/skills/l-01-agent-lifecycles/templates/curator-brief.md new file mode 100644 index 00000000..8217ce34 --- /dev/null +++ b/.openclaw/workspace/skills/l-01-agent-lifecycles/templates/curator-brief.md @@ -0,0 +1,84 @@ +# Template — Curator Brief + +The dispatch packet the **manager** (or the architect in a flat series) compiles for a **curator**, +spawned fresh per leaf after builder code exists and the reviewer verdict is available. **The brief +is the curator's entire session start** — it replaces the front half the spawner already ran. This +is the change-set feeding contract: the curator never infers a change set from transcript memory, +it is FED the landed change set, the leaf task doc, and notes/ as inputs. + +Spawn with `env={"AR_SPAWN_ROLE": "curator"}` and the **qualified** leaf key +`//`; together they claim the curator's `(leaf, role)` seat while the +session-start router and dashboard leaf rail engage. + +--- + +```md +ROLE BRIEF — curator + +# CURATOR BRIEF — · + +You are the CURATOR for leaf `` of master `` (repo: ). Your lifecycle is +`skills/l-01-agent-lifecycles/roles/curator.md`; this brief is your session start. Write the leaf's +memory pass from the inputs below, then stop. + +## Worktrees +- Code: `` (branch ``, base ``) — read-only for you. +- Memory: `` (branch ``) — your only write surface. + +## The landed change set (fed, not inferred) +- Code diff: `..` in the code worktree — . +- Memory diff (if any pre-existing memory-worktree changes carry forward): `..`. +- Counters: `` from the change-set the manager attached — + do not re-derive this from your own guess at "what probably changed." + +## Task inputs +- Leaf task doc: `` (read it first — objective, requirements, decision log). +- notes/: `` — the builder turn report + (`notes/reports/-worker-report.md`), the reviewer verdict when this leaf ran a loop, and + any other task-local notes naming a factual current-state clarification. + +## Routing rule (mgmt-L4 design — apply this before writing anything) +Route each piece of the change set and each notes/ item to the RIGHT onboarding home: +1. A concrete source file's own sidecar, when the change is about that file's behavior. +2. The nearest governing route-local overview, when the change is about route/package shape or + crosses several files in one route. +3. The repo entity catalog, only for a real load-bearing cross-layer entity change. +4. The L3 Operational-Notes target is LAST RESORT ONLY — use it when a finding is real but has no + file, route, or entity home; never as the default drop point for convenience. +Overview-dumping (writing everything into the nearest overview because it is easiest) is rejected. + +## Tool surface +- Native reads in the code worktree; native reads/edits in the memory worktree. +- `c-05-create-or-update-onboarding-files` skill workflows for sidecars and entity catalogs. +- Local `route_index_refresh`-equivalent (`build_route_indexes(...)`) from the memory worktree. +- Inbox for one clarification row back to if the fed change set is missing or + ambiguous — never invent a change set from memory. +- No `worktree_*`, `lifecycle_*`, `task_doc`, `gate_*`, `memory_quality_check`-mutating tools beyond + what your role file names, no code edits. + +## Checks (before you report) +- `git diff --check` in the memory worktree. +- Any onboarding/reference checks the brief or your role file names. + +## Memory-pass report (mandatory, last act) +Write `/-curator-report.md`: changed onboarding files (with which +change-set item or notes/ item each one routes to and why), route index results, reference checks, +blockers, and the exact commands run. This report — together with the builder's code and the +reviewer's verdict — is exactly the manager's three closeout inputs. +``` + +--- + +**Compiler notes for the manager.** + +- Fill every ``; a brief with an unresolved placeholder is not dispatchable. +- Pull the change-set counters/paths from the leaf's actual landed range (the leaf contract's + recorded base commit through the builder's current HEAD/worktree state) — do not hand the curator + a stale or guessed diff. +- Attach the builder turn report and (when the leaf ran a loop) the reviewer verdict as the notes/ + inputs; the curator does not re-request evidence that already exists in `notes/reports/`. +- Deliver as an echo-confirmed paste; only count delivery on a post-boot echo. +- This brief runs strictly AFTER builder code exists and the reviewer verdict (when the leaf tier + requires one) is available — never before, and never in place of either. diff --git a/.openclaw/workspace/skills/l-01-agent-lifecycles/templates/manager-brief.md b/.openclaw/workspace/skills/l-01-agent-lifecycles/templates/manager-brief.md index 10c37922..0d611d1a 100644 --- a/.openclaw/workspace/skills/l-01-agent-lifecycles/templates/manager-brief.md +++ b/.openclaw/workspace/skills/l-01-agent-lifecycles/templates/manager-brief.md @@ -3,7 +3,8 @@ The dispatch packet the orchestrator compiles for a manager taking one master. Like the worker brief, **this brief is the manager's entire session start** — it replaces the front half the orchestrator already ran. Spawn with `env={"AR_SPAWN_ROLE": "manager"}` and the **qualified** leaf -key of the master's coordination leaf (`//`). +key of the master's coordination leaf (`//`); together they claim the +manager's `(leaf, role)` seat. --- @@ -30,8 +31,25 @@ master's leaf loop to the master-exit seam, then hand over. ## Dispatch defaults - Worker spawns: `templates/worker-brief.md`, `env={"AR_SPAWN_ROLE": "worker"}`, qualified leaf - keys; knob overrides: . + keys; the environment role and qualified leaf together claim each worker's `(leaf, role)` seat; + knob overrides: . +- Leaf closeout chain: manager -> builder -> reviewer -> curator. The manager closes a leaf from + builder code + reviewer verdict + curator memory pass — never before the curator pass exists. +- Curator spawns: `../templates/curator-brief.md`, `env={"AR_SPAWN_ROLE": "curator"}`, fresh per + leaf with the qualified leaf key, so the environment role and qualified leaf claim the + curator's `(leaf, role)` seat; dispatch only after builder code and the reviewer verdict are + available. The brief FEEDS the landed change set (leaf contract's base-to-head range) + the leaf + task doc + notes/ — the curator routes each to the right onboarding home (specific sidecar or + governing overview; L3 Operational-Notes last-resort only) and writes onboarding only. - Concurrency: . +- Provider degradation: on `messageKind="degradation-alert"`, do not start provider setup, + provider watchers, watcher restarts, or `retry_provider_setup` until an all-clear. Managers have + no provider kill authority; provider stops and fixes route through the orchestrator and + system-specialist. +- Cleanup: `worktree_integrate` auto-lands a completed leaf's worker/reviewer seats into the + dashboard landed/archive group (config-gated, default ON). Use the landed archive cleanup button + for archived rows; use `session_retire` only for a stuck/abandoned worker/reviewer/curator seat of + YOUR OWN master — server policy refuses any other target. ## The exit - When all leaves have landed on your branch: spawn the master-exit reviewer diff --git a/.openclaw/workspace/skills/l-01-agent-lifecycles/templates/orchestration-task.md b/.openclaw/workspace/skills/l-01-agent-lifecycles/templates/orchestration-task.md index c3d4bb08..c1559030 100644 --- a/.openclaw/workspace/skills/l-01-agent-lifecycles/templates/orchestration-task.md +++ b/.openclaw/workspace/skills/l-01-agent-lifecycles/templates/orchestration-task.md @@ -1,10 +1,12 @@ # Orchestration-Task Template The artifact a **strategist** drafts for the **orchestrator** (`roles/strategist.md`) — the sprint -plan and the sprint scope. A strategist run producing this artifact is a **mandatory precondition -for any orchestrated run**; the orchestrator adopts the accepted draft into durable task form (the -strategist is a reader, not a mutator). It is written under the series/coordination `notes/` path -the brief names and revised across drawing-board rounds. +plan and the sprint scope. The architect proposes the strategist pass, and the strategist drafts +this artifact only after developer approval; the orchestrator adopts the accepted draft into +durable task form (the strategist is a reader, not a mutator). When the developer sanctions a +strategist skip, `roles/orchestrator.md` owns the alternate author-and-adopt path. It is written +under the series/coordination `notes/` path the brief names and revised across drawing-board +rounds. ## Rules diff --git a/.openclaw/workspace/skills/l-01-agent-lifecycles/templates/turn-report.md b/.openclaw/workspace/skills/l-01-agent-lifecycles/templates/turn-report.md index b771ada4..597f40fd 100644 --- a/.openclaw/workspace/skills/l-01-agent-lifecycles/templates/turn-report.md +++ b/.openclaw/workspace/skills/l-01-agent-lifecycles/templates/turn-report.md @@ -2,7 +2,8 @@ The **mandatory** artifact a worker writes at **every** hand-off (`roles/worker.md`). It is how the leaf's work survives the session's death and how a respawned successor onboards from **state, not the -transcript**. A missing turn report is nudged by the manager. +transcript**. A missing turn report is nudged by the HFX2-L2 supervisor sweep, never by a +manager watching for it (uniform-mechanism ruling 2026-07-07). ## Rules diff --git a/.openclaw/workspace/skills/l-01-agent-lifecycles/templates/worker-brief.md b/.openclaw/workspace/skills/l-01-agent-lifecycles/templates/worker-brief.md index db8f44d0..c91e9779 100644 --- a/.openclaw/workspace/skills/l-01-agent-lifecycles/templates/worker-brief.md +++ b/.openclaw/workspace/skills/l-01-agent-lifecycles/templates/worker-brief.md @@ -7,7 +7,8 @@ absorbed a series of real dispatch frictions (route-index leaks, attestation for keying, missing `python` shim), so deviate knowingly or not at all. Spawn with `env={"AR_SPAWN_ROLE": "worker"}` and the **qualified** leaf key -`//` so the session-start router and the dashboard leaf rail both engage. +`//`; together they claim the worker's `(leaf, role)` seat while the +session-start router and dashboard leaf rail engage. --- @@ -18,13 +19,14 @@ ROLE BRIEF — worker You are a WORKER for leaf `` of master `` (repo: ). Your lifecycle is `skills/l-01-agent-lifecycles/roles/worker.md`; this brief is your session start. Execute the leaf -completely, write your turn report, then stop. +code completely, write your builder turn report, then stop. Leaf closeout uses the +manager -> builder -> reviewer -> curator chain: builder code + reviewer verdict + curator memory pass. -## Worktrees (your ONLY writable areas) +## Worktrees (your code write area + memory context) - Code: `` (branch ``, base ``) -- Memory: `` +- Memory: `` (read/context for changed-path notes; the curator writes onboarding) - Plus your turn report at the path below. Nothing else. NEVER `git commit` — the owning seat - closes out after reviewing your report. + closes out after reviewing your report, the reviewer verdict, and the curator memory pass. ## Tool surface - Native file tools inside the two worktrees; shell for the checks below. @@ -46,18 +48,18 @@ files involved, the invariants that must hold, what NOT to touch.> - Full: — must exit 0. - `git diff --check` in both worktrees. -## Onboarding (same editing pass, per c-05) -- Changed source files: update the sidecar BODY now; new files: create the sidecar. -- Route overviews: genuine body update where routes changed; otherwise the newest history entry - uses the LITERAL form `- — No route impact: ` (timestamp first). -- Pin idiom for verification metadata: "Verification metadata pinned until closeout stamps the - commit." +## Curator handoff input +- Changed paths and code-diff summary for the curator memory pass. +- Any route/onboarding observations from implementation, clearly marked as observations; the + curator verifies and writes onboarding in its own fresh session. +- Pin idiom for any metadata note the curator needs: "Verification metadata pinned until closeout + stamps the commit." ## Turn report (mandatory, last act) Write `/-worker-report.md` following `skills/l-01-agent-lifecycles/templates/turn-report.md` — including exact check commands + -outcomes, the retrieval-evidence tally, and the respawn state. If blocked: fill Escalations and -stop — escalate to , never to the developer. +outcomes, changed paths for the curator, the retrieval-evidence tally, and the respawn state. If +blocked: fill Escalations and stop — escalate to , never to the developer. ``` --- diff --git a/.openclaw/workspace/skills/w-02-light-task-workflow/master-template.md b/.openclaw/workspace/skills/w-02-light-task-workflow/master-template.md index 42738c16..a807afd1 100644 --- a/.openclaw/workspace/skills/w-02-light-task-workflow/master-template.md +++ b/.openclaw/workspace/skills/w-02-light-task-workflow/master-template.md @@ -7,7 +7,7 @@ built to grow as the work unfolds. ## When to escalate to a series -The `l-01-agent-lifecycles` orchestrator lifecycle's `decide` step escalates a single task to a series once its size is apparent — the +The `l-01-agent-lifecycles` architect lifecycle's `decide` step escalates a single task to a series once its size is apparent — the implementation plan no longer fits on a single page, or the work splits into distinct slices that each deserve their own checklist and commit. You can also start single and escalate later: drop in the master `task.md` and move the existing plan into the first `NN_.md`. diff --git a/.pi/extensions/agents-remember-start.ts b/.pi/extensions/agents-remember-start.ts index 3cc745dc..31396e2a 100644 --- a/.pi/extensions/agents-remember-start.ts +++ b/.pi/extensions/agents-remember-start.ts @@ -29,10 +29,10 @@ export default function (pi) { "orchestrating agent: **ignore this notice entirely — your brief is your session", "start.**", "", - "Otherwise you are the developer-facing session, i.e. the **orchestrator**: read", + "Otherwise you are the developer-facing session, i.e. the **architect**: read", `\`${WORKSPACE_ROOT}/ar-coordination/AGENTS.md\` and treat those rules as workspace`, "instructions, then run your lifecycle at", - "`skills/l-01-agent-lifecycles/roles/orchestrator.md` — trust checkpoint before", + "`skills/l-01-agent-lifecycles/roles/architect.md` — trust checkpoint before", "relying on memory, `read_ar_files` (paired source+onboarding) until the build", "decision, retrieval-strategy tally as evidence, notify-and-stop at every", "developer hand-off." diff --git a/.pi/skills/c-05-create-or-update-onboarding-files/SKILL.md b/.pi/skills/c-05-create-or-update-onboarding-files/SKILL.md index 31ea0090..d8e39a82 100644 --- a/.pi/skills/c-05-create-or-update-onboarding-files/SKILL.md +++ b/.pi/skills/c-05-create-or-update-onboarding-files/SKILL.md @@ -16,6 +16,16 @@ Planning stays in task artifacts. This package defines how onboarding itself is Before maintaining onboarding, use `c-08-ar-coordination-context-resolver` to resolve the target repository's active coordination context. It must use the `Domain Documentation` category declared in the resolved `system/sources.md` for the onboarding slice being maintained, rather than assuming that adjacent onboarding alone is sufficient or hard-coding one particular documentation system into the skill. +**Seat routing:** in the manager -> builder -> reviewer -> curator chain (`l-01-agent-lifecycles` +`roles/curator.md`), onboarding create/update duty during leaf work belongs to the curator seat, not +the builder — the builder produces code and a turn report only. The curator runs this skill's +workflows from a change set (landed diff), the leaf task doc, and notes/ fed to it by the manager, +and routes each item to the right onboarding home (a concrete sidecar or the governing overview +whose subject it is; the L3 Operational-Notes target is last-resort only, never a default). The +strict 1-to-1 source mapping, governing-overview links, and metadata rules below are unchanged by +this routing — only the writing seat moved. A solo flat session with no separate curator seat runs +this skill itself, exactly as before. + When onboarding maintenance is happening inside a `c-09-git-worktree-manager` worktree, check `worktree_status` before starting memory entries. If its `freshness` block reports that the recorded code or memory source branch moved, diff --git a/.pi/skills/c-09-git-worktree-manager/SKILL.md b/.pi/skills/c-09-git-worktree-manager/SKILL.md index 692dec19..58a87184 100644 --- a/.pi/skills/c-09-git-worktree-manager/SKILL.md +++ b/.pi/skills/c-09-git-worktree-manager/SKILL.md @@ -1,6 +1,6 @@ --- name: c-09-git-worktree-manager -description: "Create, attach to, report on, integrate, finalize, and clean up Agents Remember worktree-backed tasks while preserving human approval gates and external-memory compatibility." +description: "Create, attach to, report on, integrate, finalize, and clean up Agents Remember worktree-backed tasks while preserving delegated/human approval gates and external-memory compatibility." --- # c-09-git-worktree-manager Git Worktree Manager @@ -75,11 +75,14 @@ The intended order is: integration branch and integrates back into it. For a nested master, create the child integration branch from the parent integration branch. 6. choose or review the task slug and workflow variables -7. **hand off** the **Worktree Intent Gate** (notify-and-continue) for explicit developer approval +7. establish the applicable worktree-start authority: for standalone/new work, hand off the + **Worktree Intent Gate** for explicit developer approval; for subordinate leaves/edges inside an + accepted orchestrated series, record the accepted-series authority and continue without a new + developer hand-off 8. create the durable task wrapper when one is needed 9. request the `worktree_start` MCP tool only after the task identity is stable, the correct landable `source_branch` is selected, external memory is clean, and - the developer approved the intent packet + the applicable authority has been recorded The Worktree Intent Gate must name: @@ -97,7 +100,7 @@ visible: leaf work branches integrate into the pushable integration/source branc recorded by their enclosure; protected targets are reached later through the repo's PR flow. -Run the applicable dry-run/preflight first, then **hand off**: call +For developer-gated starts, run the applicable dry-run/preflight first, then **hand off**: call `lifecycle_turn_end_notification(summary={…the intent packet + the approve/revise ask…})` as the **last tool call**, then deliver the intent packet as your final prose and **STOP / end your turn**. The notification sets the `awaiting-developer` lifecycle state, surfaces a @@ -106,6 +109,10 @@ dashboard or in the leaf's attached chat; the **first AR tool call of your next lifecycle (`running`), clears the attention item, and proceeds to `worktree_start` — you send no explicit `lifecycle_resume`. +For subordinate orchestrated-series starts, do the same dry-run/preflight, record the accepted +planner/series authority in the task decision log or worktree intent note, and continue. Do not add +a developer stop for every leaf worktree. + Parked fallback: the block-and-wait `lifecycle_gate` junction (plus the operator inbox and dashboard GateResponder) still works if you deliberately raise it for a durable, developer-attributed, mutation-blocking record: @@ -190,19 +197,27 @@ quality gate, memory content commit, ledger update, and ledger commit. For worktree-backed tasks, pass the leaf enclosure `series-contract.md` to `worktree_closeout_preview` / `worktree_closeout_apply`. The apply step records -the developer's explicit commit approval in the contract and updates the -contract closeout state after the code, memory, and ledger commits are created. +the applicable closeout authority in the contract and updates the contract closeout state after the +code, memory, and ledger commits are created. Worktree closeout stops if the recorded code or external-memory source branch moved since task start. ## Integration -Integration is explicitly human-gated and runs only after closeout completed. It lands the closed task branches back onto the recorded source branches and records the landed commits separately from the closeout commits. Orchestrated-run carve-out (ruled 2026-07-06): dependency-ordered leaf→master and master→super integrations ride the series' standing approval (the developer's portfolio-gate approval recorded in the planner master) — the developer hand-off concentrates at the super PR/carry-over gate per the `l-01-agent-lifecycles` loop/orchestrator doctrine; a raised durable `integration-approval` gate still awaits the developer. +Integration runs only after closeout completed and is authority-gated by context. It lands the +closed task branches back onto the recorded source branches and records the landed commits +separately from the closeout commits. In an accepted orchestrated run, dependency-ordered +leaf→master and master→super integrations ride the series' standing approval (the developer's +portfolio-gate approval recorded in the planner master) — the developer hand-off concentrates at +the super PR/carry-over gate per the `l-01-agent-lifecycles` loop/orchestrator doctrine. A raised +durable `integration-approval` gate still awaits the developer. On an orchestrated master's exit (master → super integration) the integrate step additionally enforces the delegated `master-handover-approval` seam: an undecided or policy-invalid handover gate addressed to the master (by `enclosure` = master task name) returns `handover-gate-blocked` instead of landing — decide the gate per the `l-01-agent-lifecycles` seam doctrine, then rerun. When no gate addresses the integrating master but open `master-handover-approval` gates exist elsewhere, integrate still proceeds and its result carries a `handover_gate_warning` naming them — treat it as a spelling check on the raised gate's `enclosure`. -Run `worktree_integrate(..., dry_run=true)` first, then **hand off**: call +Run `worktree_integrate(..., dry_run=true)` first. For subordinate accepted-series integrations, +record the standing series authority and then run the real integration without a developer stop. +For developer-gated integrations, **hand off**: call `lifecycle_turn_end_notification(summary={…the integration plan…})` as the **last tool call**, then deliver the integration preview as your final prose and **STOP**. The developer approves on the dashboard or in chat; the first AR tool call of your next turn auto-resumes @@ -228,9 +243,17 @@ After successful integration, complete any repo-specific landing tail first: pus ## Lifecycle Finalization And Cleanup -Lifecycle finalization is explicitly human-gated and runs only after closeout, integration, and any PR/carryover tail are complete. It proves the current parent-child branch edge, then removes the recorded code and memory worktrees, deletes local task branches only when Git can prove they are merged, removes empty worktree group folders when safe, records `cleanup: completed` in the contract, and updates task documents. - -Run `lifecycle_finalize_task(..., dry_run=true)` first, then **hand off**: call +Lifecycle finalization runs only after closeout, integration, and any PR/carryover tail are +complete, and its approval authority follows the same series boundary. For subordinate +accepted-series leaf/master edges, the owning manager/orchestrator may finalize and clean up after +the dry-run proves the landed edge. For final super→main cleanup, standalone work, or a deliberately +raised `cleanup-approval` gate, stop for developer approval. Finalization proves the current +parent-child branch edge, then removes the recorded code and memory worktrees, deletes local task +branches only when Git can prove they are merged, removes empty worktree group folders when safe, +records `cleanup: completed` in the contract, and updates task documents. + +Run `lifecycle_finalize_task(..., dry_run=true)` first. For subordinate accepted-series cleanup, +record the standing authority and run the real finalizer. For developer-gated cleanup, **hand off**: call `lifecycle_turn_end_notification(summary={…what cleanup removes…})` as the **last tool call**, then relay the landed-commit proof, cleanup plan, and task-document updates as your final prose and **STOP**. The developer approves on the dashboard or in chat; the first AR tool call of your next turn auto-resumes and runs @@ -277,11 +300,16 @@ then proceeds as usual, including closeout → integrate → finalize. 2. The `c-09-git-worktree-manager` skill does not initialize memory roots; use the `c-00-initialize-memory-repo` skill before starting external-memory worktrees. 3. Closeout belongs to the `c-12-closeout` skill; the `c-09-git-worktree-manager` skill only supplies worktree contract context. 4. The `c-09-git-worktree-manager` skill must not use divergent memory as semi-trusted reference context. -5. The `c-09-git-worktree-manager` skill must not bypass the `c-12-closeout` skill's explicit closeout approval gate. +5. The `c-09-git-worktree-manager` skill must not bypass the `c-12-closeout` skill's applicable + closeout authority gate. 6. The `c-09-git-worktree-manager` skill must not create closeout commits outside the `c-12-closeout` skill's code-memory-ledger sequence. 7. The `c-09-git-worktree-manager` skill must not call `worktree_start` until - the developer has approved the Worktree Intent Gate. -8. The `c-09-git-worktree-manager` skill must not move source branches during integration until replay/preflight has produced fast-forwardable code and memory commits and explicit integration approval exists. -9. The `c-09-git-worktree-manager` skill must not finalize or clean up without explicit cleanup/finalization approval. + the applicable authority has been recorded: developer-approved Worktree Intent Gate for + standalone/new work, or accepted-series authority for subordinate orchestrated work. +8. The `c-09-git-worktree-manager` skill must not move source branches during integration until + replay/preflight has produced fast-forwardable code and memory commits and applicable + integration authority exists. +9. The `c-09-git-worktree-manager` skill must not finalize or clean up without applicable + cleanup/finalization authority. 10. The `c-09-git-worktree-manager` skill must not treat squash-merged content as a normal landed edge. 11. The `c-08-ar-coordination-context-resolver` skill remains the facts-only resolver; the `c-09-git-worktree-manager` skill owns worktree and lifecycle mutation. diff --git a/.pi/skills/c-12-closeout/SKILL.md b/.pi/skills/c-12-closeout/SKILL.md index 2c7fe3ff..82c472a5 100644 --- a/.pi/skills/c-12-closeout/SKILL.md +++ b/.pi/skills/c-12-closeout/SKILL.md @@ -1,6 +1,6 @@ --- name: c-12-closeout -description: "Close out approved Agents Remember edits by preserving explicit commit approval, missing-onboarding checks, external-memory onboarding refresh, memory quality, ledger alignment, and no automatic push for worktree-backed tasks." +description: "Close out approved Agents Remember edits by preserving the applicable approval authority, missing-onboarding checks, external-memory onboarding refresh, memory quality, ledger alignment, and no automatic push for worktree-backed tasks." --- # c-12-closeout Closeout @@ -15,6 +15,18 @@ direct-checkout closeout path. Use the `c-09-git-worktree-manager` skill for worktree start, attach, status, integration, lifecycle finalization, and cleanup; use this skill for the closeout gate and code-memory-ledger commit order. +**Seat note (manager -> builder -> reviewer -> curator chain):** in that chain, the builder produces +code and a turn report only — it does not author onboarding. The dedicated curator seat +(`l-01-agent-lifecycles` `roles/curator.md`) runs the `c-05-create-or-update-onboarding-files` skill +as its own fresh pass, fed the leaf's landed change set, task doc, and notes/, BEFORE the owning +seat (the manager) runs this skill's closeout preview. Everywhere below that says "create" or +"refresh" onboarding, that authoring already happened in the curator's pass; the seat running +closeout **verifies** the curator's output against the checks in this skill, it does not author +onboarding inline to make a failing check pass. A check that still fails after the curator pass is a +closeout failure — respawn/rerun the curator, do not patch onboarding from the closeout seat. This +distinction does not apply outside that chain (e.g. a solo flat session with no separate curator +seat still runs `c-05-create-or-update-onboarding-files` itself before closing out). + ## MCP Tools Use the worktree closeout tools against the task contract: @@ -29,18 +41,31 @@ Worktree closeout records closeout state in the contract the `c-09-git-worktree-manager` skill owns later integration, lifecycle finalization, cleanup, and task-document completion. -## Approval Hand-off +## Approval Authority + +Closeout is always authority-gated, but the authority is contextual. + +For standalone work, final super-branch landing, or any closeout where the accepted task/series +authority is unclear, agents must request the matching preview tool first, relay the proposed code, +memory, and ledger commit messages to the developer, and ask for explicit commit approval. + +For subordinate work inside an accepted orchestrated series, the owning seat may apply closeout +under delegated series authority after the preview/checks are clean. Managers govern leaf commits; +the orchestrator governs manager/master edges and direct flat work when it is wearing the manager +or worker hat itself. Do not stop for the developer merely because closeout will create code, +memory, and ledger commits. The `intent_note` records the authority source, e.g. the accepted +planner/series task and the owning seat's review of the preview. -Closeout is explicitly human-gated. Agents must request the matching preview -tool first, relay the proposed code, memory, and ledger commit messages to the -developer, and ask for explicit commit approval. +Closeout still stops for the developer when the work reaches the final completed super branch / +PR-carryover gate, when a `closeout-approval` gate has been deliberately raised, when the change is +outside the accepted scope, when checks remain red outside the task, when onboarding/memory quality +cannot be repaired inside the leaf, or when a quo-vadis decision is required. -Real closeout uses the matching apply tool with an `intent_note`. The note -records the developer's explicit commit approval. Agents must not treat -implementation approval, a previous "looks good", or their own judgment as -commit approval. +Real closeout uses the matching apply tool with an `intent_note`. The note records the applicable +authority: either explicit developer commit approval or delegated accepted-series authority. Agents +must not treat a vague "looks good" or their own preference as authority. -The relay follows the `l-01-agent-lifecycles` orchestrator hand-off protocol: run the +For a developer-gated closeout, the relay follows the `l-01-agent-lifecycles` orchestrator hand-off protocol: run the preview/dry-run first, then call `lifecycle_turn_end_notification(summary={…the preview facts + the commit ask…})` as the **last tool call**, then deliver the preview facts and proposed messages as plain @@ -66,10 +91,11 @@ the dashboard and a `closeout-approval` gate is explicitly raised, closeout is * server-side through that durable gate, so a developer can approve from the cockpit and the mutating tool — not a UI button — is the security boundary. -`closeout-approval` **is** the commit gate — closeout is the single -commit-of-record for code, memory, and ledger, so there is no separate -`commit-approval` kind; every commit routes through this gate. The dashboard -junction uses the preview/dry-run -> chat report -> `lifecycle_gate` order above. +`closeout-approval` **is** the human commit gate when it is deliberately raised — closeout is the +single commit-of-record for code, memory, and ledger, so there is no separate `commit-approval` +kind. Subordinate orchestrated-series closeouts normally do not raise this gate; they use the +accepted-series authority recorded in the `intent_note`. The dashboard junction uses the +preview/dry-run -> chat report -> `lifecycle_gate` order above. How it binds: @@ -99,16 +125,19 @@ How it binds: Rules: -1. **Never self-approve.** A model-attributed approval is rejected by - enforcement. Wait for the developer's dashboard decision or Chat response, and - never pass your own judgment off as commit approval. +1. **Never self-approve a human-pinned gate.** A model-attributed approval is rejected by + enforcement. Wait for the developer's dashboard decision or chat response when a + `closeout-approval` gate exists, and never pass your own judgment off as developer approval. + Delegated-series closeout without a raised gate is different: it records the accepted series + authority and the owning seat's review in `intent_note`. 2. **Opening a gate is opt-in and deliberate.** Open a `closeout-approval` gate **only** when a developer is driving approval from the dashboard. Do **not** open one in a pure-chat session with no cockpit watching — an `open` gate blocks your own closeout until it is decided. -3. **Gateless lifecycles are unchanged.** With no `closeout-approval` gate the chat - commit gate (`intent_note` after an explicit "commit") governs exactly as before; - enforcement is additive, never a new requirement on every closeout. +3. **Gateless lifecycles use the applicable authority.** With no `closeout-approval` gate, + standalone/final work still needs explicit developer commit approval, while accepted + orchestrated-series subordinate work may proceed under delegated series authority. Enforcement is + additive, never a requirement to raise a gate on every closeout. 4. The closeout preview/apply payload carries a `closeout_gate` block (`enforced` / `permitted` / `gateId` / `reason`); relay it at the commit-approval gate so the developer sees whether a dashboard gate is open, approved, or absent. @@ -130,17 +159,22 @@ python -m agents_remember.memory_quality.integrity.check_missing_onboarding --co ``` The check only evaluates files that are new in the current checkout or -worktree, not the whole historical repository. If it reports missing -onboarding, create those sidecars through the `c-05-create-or-update-onboarding-files` skill before committing code. After -the code commit exists, refresh the new sidecars' verification metadata to that -commit during the normal post-code-commit memory refresh. +worktree, not the whole historical repository. In the manager -> builder -> +reviewer -> curator chain, this check is expected to already pass by the time the owning seat runs +it, because the curator's memory pass created those sidecars through the +`c-05-create-or-update-onboarding-files` skill before this precondition is checked; running the +check here confirms that pass, it is not the trigger to author onboarding from the closing seat. If +it still reports missing onboarding, do not create the sidecars inline — escalate to run (or rerun) +the curator's memory pass, then rerun this check. After the code commit exists, refresh the new +sidecars' verification metadata to that commit during the normal post-code-commit memory refresh. Changed (already-onboarded) source files have a parallel requirement: their sidecar content must be updated to approved current state before closeout. The closeout gate rejects any changed source file whose existing sidecar body was not modified in the current task, because advancing verification metadata over -stale content defeats the commit-hash-based drift check. Update changed sidecars -during implementation, not at the metadata-refresh step. +stale content defeats the commit-hash-based drift check. In the curator chain, changed sidecars are +updated during the curator's memory pass, not at the metadata-refresh step, and not by the builder +during implementation. The closeout worklist covers the working tree plus the leaf contract-recorded committed range: every path changed between the last verified commit (the @@ -159,11 +193,15 @@ deliberately through the `c-05-create-or-update-onboarding-files` skill. External-memory closeout order is: -1. run `check_missing_onboarding` against current additions -2. create missing onboarding for newly added eligible source files before committing code +1. run `check_missing_onboarding` against current additions (in the curator chain, this confirms the + curator's pass already covered them — it is not the cue to author onboarding here) +2. if onboarding is still missing, escalate to run/rerun the curator's memory pass through the + `c-05-create-or-update-onboarding-files` skill before committing code (solo flat sessions with no + separate curator seat create it directly) 3. commit code changes and capture `C2` plus its commit date 4. run the `c-02-memory-quality-control` skill's drift check against `C2` to produce the full memory update worklist -5. verify each changed source file's sidecar content was updated in this task, then refresh affected onboarding `lastVerifiedCommitHash` and `lastVerifiedCommitDate` to `C2`; a changed source file with an unmodified sidecar body fails the closeout instead of receiving a metadata-only refresh +5. verify each changed source file's sidecar content was updated in this task (by the curator's pass + in the chain above), then refresh affected onboarding `lastVerifiedCommitHash` and `lastVerifiedCommitDate` to `C2`; a changed source file with an unmodified sidecar body fails the closeout instead of receiving a metadata-only refresh 6. refresh affected repo entity catalog `git-blob-set-v1` fingerprints against `C2` when changed source paths are listed as entity evidence 7. refresh affected route overview `lastVerifiedCommitHash` / `lastVerifiedCommitDate` metadata to `C2` 8. refresh generated route indexes so `overview.index.json` matches the updated onboarding tree @@ -214,16 +252,20 @@ preview and apply payloads for the commit-approval relay. Worktree closeout also fails when the recorded code or external-memory source branch moved since task start. -Missing onboarding is the expected hard failure when the implementation/update -pass did not produce a required onboarding file. The next step is to run the `c-05-create-or-update-onboarding-files` skill -for that source file, then rerun the closeout preview. +Missing onboarding is the expected hard failure when the required onboarding file was not produced — +in the manager -> builder -> reviewer -> curator chain that means the curator's memory pass did not +cover it. The next step is to run (or rerun) the curator's `c-05-create-or-update-onboarding-files` +pass for that source file, then rerun the closeout preview; a solo flat session with no separate +curator seat runs that skill itself. ## Boundaries 1. The `c-12-closeout` skill owns closeout approval and code-memory-ledger commit sequencing. 2. The `c-12-closeout` skill does not create worktrees, integrate worktrees, finalize lifecycles, or clean up worktrees. 3. The `c-12-closeout` skill does not initialize memory roots; use the `c-00-initialize-memory-repo` skill. -4. The `c-12-closeout` skill must not commit without explicit commit approval after a closeout preview. +4. The `c-12-closeout` skill must not commit without the applicable authority after a closeout + preview: explicit developer commit approval for standalone/final work, or recorded delegated + series authority for subordinate accepted-series work. 5. The `c-12-closeout` skill must not create a memory content commit whose affected onboarding metadata still points at pre-closeout code. 6. The `c-12-closeout` skill must not create a memory content commit before route overview metadata, generated route indexes, and `memory_quality_check` are clean for the new code commit. 7. The `c-12-closeout` skill must not push automatically. diff --git a/.pi/skills/c-13-install-and-onboard/SKILL.md b/.pi/skills/c-13-install-and-onboard/SKILL.md index d583fd13..e52d9f57 100644 --- a/.pi/skills/c-13-install-and-onboard/SKILL.md +++ b/.pi/skills/c-13-install-and-onboard/SKILL.md @@ -152,8 +152,8 @@ preference): 4. **Harness preference + role knobs** (`orchestration.spawn.harness`, per-role `orchestration.roles.`, per-level `orchestration.rolesPerLevel..`) - which installed harness - `spawn_agent_session` uses when the spawning seat passes none, per-role - harness/model/effort overrides, and per-LEVEL overrides + `spawn_agent_session` uses when no role/level knob supplies one, per-role + harness/model/effort settings, and per-LEVEL settings (leaf|master|portfolio) for tiered economics (e.g. a cheap leaf reviewer, a smarter master-seam reviewer). Harness values must be known ids: the builtin registry (`claude`, `codex`, `pi`) or an `orchestration.harnesses` @@ -163,8 +163,10 @@ preference): `ultracode`); mention the FREE-FORM escape hatch for anything outside the vocabularies - `launchArgs` (verbatim argv), `sessionCommands` (pasted before the brief), `promptKeywords` (prepended to the brief) - never - validated, recorded in spawn provenance. Default: detection-gated (the - first detected harness). The full spawn-surface manual is + validated, recorded in spawn provenance. Ordinary spawning seats cannot + pass `harness`/`model`/`effort`, launch/session spend controls, or harness-native + spend/endpoint env keys directly; settings are the spend surface. Default: + detection-gated (the first detected harness). The full spawn-surface manual is `docs/reference/harnesses.md`. If the developer wants to skip the interview, confirm the seeded defaults diff --git a/.pi/skills/l-01-agent-lifecycles/SKILL.md b/.pi/skills/l-01-agent-lifecycles/SKILL.md index 1d390618..4094072d 100644 --- a/.pi/skills/l-01-agent-lifecycles/SKILL.md +++ b/.pi/skills/l-01-agent-lifecycles/SKILL.md @@ -1,6 +1,6 @@ --- name: l-01-agent-lifecycles -description: "The agent lifecycles: one lifecycle per agent type, under one roof. Routes every session by exactly three conditions (spawn-role env -> role brief -> otherwise orchestrator), carries the minimal lifecycle frame (the six lifecycle signals every session shares), and houses the self-contained per-role lifecycles (orchestrator, designer, strategist, manager, worker, adversarial reviewer) plus the report-template library and the reviewer criteria catalogs. A developer-facing session IS the orchestrator; solo work is the degenerate portfolio. Supersedes and replaces both l-01-session-job-lifecycle and l-02-agent-orchestration." +description: "The agent lifecycles: one lifecycle per agent type, under one roof. Routes every session by exactly three conditions (spawn-role env -> fresh role brief -> otherwise free-chat launcher), carries the minimal lifecycle frame (the six lifecycle signals every session shares), and houses the self-contained per-role lifecycles (architect, orchestrator, designer, strategist, manager, worker, curator, system-specialist, adversarial reviewer) plus the report-template library and the reviewer criteria catalogs. Solo work is the degenerate portfolio. Supersedes and replaces both l-01-session-job-lifecycle and l-02-agent-orchestration." --- # l-01-agent-lifecycles — The Agent Lifecycles @@ -15,42 +15,128 @@ lifecycle, and no role reads another role's file. 1. **`AR_SPAWN_ROLE` is set** (spawn env, injected by `spawn_agent_session`) → run `roles/.md`. Nothing else in this file's "developer session" material applies to you. (`designer` here means the same design hat in a separate chair — see `roles/designer.md`.) -2. **Else: the first user message is a role brief** — a `templates/*-brief.md`-shaped dispatch or +2. **Else: the first user message is a role brief in a fresh session** — a `templates/*-brief.md`-shaped dispatch or a first line of the form `ROLE BRIEF — ` from an orchestrating agent → run that role's lifecycle. The brief is your session start; a workspace session-start notice is not addressed to you. -3. **Else** (a developer opened this session) → you are the **orchestrator**: run - `roles/orchestrator.md`. Solo work is the degenerate portfolio — the same three jobs with hats - collapsed (the orchestrator wears the manager hat in flat runs and builds hands-on at session - scale); the task doc still comes first. +3. **Else** (a developer opened this session) → you are the developer-facing **free chat** — a + launcher, not a role seat (ruled 2026-07-09). Research-only questions are answered inline with + no role taken. The moment the ask is role-shaped (a sprint, a task, any durable change), do NOT + assume the architect role in this chat: **spawn the architect into its own chat** + (`spawn_agent_session` with `AR_SPAWN_ROLE=architect`; the profile — harness/model/effort — + comes from `orchestration.roles.architect` in settings, never from this session's guess) and + point the developer at that chat. A clean startup with the settings-owned profile removes all + profile ambiguity. The spawned architect runs `roles/architect.md` and owns the developer + conversation from there. There is no fourth entry, and the edge cases are decided: an **unresolvable `AR_SPAWN_ROLE` value** (no matching `roles/.md`) falls through to condition 2 (the brief); a role-env session **whose brief never arrives** announces itself on the inbox and waits — it never -improvises a task; `AR_SPAWN_ROLE=orchestrator` is valid only as a takeover chair (the Profile -check (takeover) in `roles/orchestrator.md`, The Event Loop) — the developer still talks to **one** orchestrator. Orchestrated -fan-out (spawning managers/workers at scale) begins only on an explicit developer request (e.g. -*"orchestrate these masters"*) — no agent promotes itself into a spawning seat. +improvises a task; `AR_SPAWN_ROLE=orchestrator` is valid only as a spawned backend seat or a +backend takeover chair — the developer still talks to the **architect**, not the orchestrator. +The spool-up chain is fixed and self-driving (ruled 2026-07-09): free chat spawns the +**architect**; the architect spawns the **orchestrator** for portfolio execution; the +orchestrator spawns **managers** per the approved plan and the concurrency settings; managers +spawn their **workers**. No seat waits to be told "spawn this, spawn that" — each level spawns +its next level from the plan. Only two spool-up decisions ever go back to the developer, both as +questions the agent raises itself: whether to run a **strategist** pass (proposed, never +auto-run), and whether to take the **short root** (solo, no orchestration) when the work looks +tiny — see `roles/architect.md`. One exception to the no-cross-reading rule above: **a seat that WEARS a hat runs that hat's file -as its own** — the orchestrator always for `roles/designer.md`, and in flat runs for -`roles/manager.md` (the hat-collapse rule). +as its own** — the architect may wear `roles/designer.md`, and in solo/flat runs may wear backend +or build hats (the hat-collapse rule). A spawned role seat never wears another role's hat. + +## Developer-Declared Task-Seat Takeover + +When the developer says *"you are the orchestrator/manager/worker for task X"* (or equivalent), +that is a **task-seat takeover**, not a loose role hint. The named task leaf is the seat. Before +analysis, profile checks, spawning, or implementation, open the named task doc and attach the +current dashboard chat to that leaf. + +Operational checklist: + +1. Resolve the named task to the **qualified** leaf key `//` and the + lifecycle role this seat is claiming. +2. Use the dashboard terminal catalog session id for this chat — not `CLAUDE_CODE_SESSION_ID`, not + `CODEX_THREAD_ID`. +3. Call `attach_terminal_session_to_leaf` with that qualified leaf key and the claimed role. +4. Rename the session to the seat label the developer expects. +5. Verify the terminal catalog and dashboard row show that exact `(qualified leaf key, seat role)` + binding before continuing. + +If no dashboard terminal catalog session id can be found, record the blocker and ask for the +missing attachment path. Do not claim the seat is attached until the catalog/dashboard row proves +it. After the attachment is verified, continue with the lifecycle selected above. + +## Developer Clarification Triage + +When the developer clarifies a rule, boundary, or desired behavior during an active task, decide +whether it is **current implementation** or **future queue** before writing only a note. Read the +active queue first: the current leaf, parent/master, neighboring leaves, decision log, open +questions, and in-flight branch state. The question is not whether a note is useful; it is whether +the developer is effectively steering the work already in hand. + +Treat it as current implementation when queue context and closeness point at the active change: it +names the same task/leaf/master, resolves a defect exposed by the current work, or improves the +same doctrine or code path. A small change that plainly fits the current diff is a strong signal +for immediate implementation even if the developer phrases it as "maybe" or "we can wrap this in." +In that case, extend the current task surface/decision log enough to make the added scope visible +and implement it now. + +Treat it as future queue when it names a later release, a separate subsystem, a large scope jump, +work whose correctness depends on another unfinished master, or a change that would reorder +already-running leaves. Record the item in the right durable queue or ask the owning seat to plan it +later. If the intent is genuinely ambiguous after reading the queue, ask the developer directly +whether they want immediate implementation or a queued note. Do not silently downgrade a +close/current/small change into future speak, and do not silently expand the active leaf when the +fit is unclear. ## The Role Registry | Role | Seat | Lifecycle file | | --- | --- | --- | -| **orchestrator** | the developer-facing session; first coordination leaf of an orchestrated series | `roles/orchestrator.md` | -| **designer** | a HAT the orchestrator pulls inline (front of the pipeline or mid-flight; separate chair optional) | `roles/designer.md` | -| **strategist** | the sprint planner, SPAWN-FIRST; a strategist run is a **mandatory precondition for any orchestrated run** — its deliverable is the orchestration task (sprint plan + scope); spawn value `strategist` | `roles/strategist.md` | +| **architect** | the developer-facing owner seat; design conversation, decision-item relay, and drawing board | `roles/architect.md` | +| **orchestrator** | spawned backend portfolio/orchestration seat; never developer-facing | `roles/orchestrator.md` | +| **designer** | a HAT the architect pulls inline (front of the pipeline or mid-flight; separate chair optional) | `roles/designer.md` | +| **strategist** | the sprint planner, SPAWN-FIRST when the developer approves the architect's propose-first question; its deliverable is the orchestration task draft (sprint plan + scope); spawn value `strategist` | `roles/strategist.md` | | **manager** | one coordination leaf per master; drives that master's leaf loop | `roles/manager.md` | | **worker** | one leaf worktree, short-lived, fresh session | `roles/worker.md` | +| **curator** | fresh per leaf after builder/reviewer; writes onboarding only from task docs, notes, and code diff | `roles/curator.md` | +| **system-specialist** | backend provider-degradation investigator; report first, fixes only after explicit orchestrator order; spawn value `system-specialist` | `roles/system-specialist.md` | | **adversarial reviewer** | short-lived, spawned at the two seams (master-exit, super-exit) and as any three-party loop's reviewer seat (criteria catalogs bound per review type); spawn value `reviewer` | `roles/reviewer.md` | The **lenses** (bug · feature · triage · research — `lenses.md`) are how the scoping seats -(orchestrator, designer) read a piece of work; a dispatched role never picks a lens — its brief +(architect, designer, backend orchestrator) read a piece of work; a dispatched role never picks a lens — its brief already carries the flavor. +## Role-Seat Immutability (dashboard-owned sessions) + +When the dashboard owns a session, its role is fixed for the session lifetime. Roles expand +**horizontally** by spawning new, individually addressable chats; sub-agents drill **vertically** +inside one seat's context for deeper analysis. A dashboard-owned session that already has a role +refuses a pasted role brief instead of silently rerouting itself; it escalates the mismatch to its +owner via the inbox. Router condition 2 applies only to fresh sessions. Sessions not owned by the +dashboard follow the host harness's ordinary rules. + +Hat-collapse is sanctioned only for the owner/developer-facing architect seat in solo or flat +runs. Spawned role seats never absorb another role brief and never become a different role in +place. + +## Minimal Decision-Item Relay + +The ARCHITECT/ORCHESTRATOR split uses the existing operator inbox now. No full queue schema or +dashboard reform is introduced here. + +- Backend seats post one `messageKind: decision-item` inbox row at a time to the architect. The row + states what is being decided, the options, the consequences, and the durable evidence refs. +- The architect presents one item at the developer's pace, records the ruling in the durable task + surface (`openQuestions` / decision logs, with notes for analysis), and returns one + `messageKind: decision-ruling` inbox row to the backend seat. +- If the item is underspecified, the architect sends a single clarification row back instead of + guessing. The backend does not open a second item until the active item has a durable ruling or + clarification state. + ## The Minimal Frame (the only machinery every session shares) Every session in a managed repo may be a **lifecycle**: six signals — `lifecycle_start` · @@ -72,19 +158,34 @@ handles a lifecycle id** — identity is server-side, anchored in the worktree c Rules: a tool call outside any lifecycle is **dropped, never misattributed**; `paused` is system-owned. **A spawned role that never touches mutating AR tools simply never instantiates a lifecycle — that is correct, not a violation.** A spawned role runs its **own** lifecycle when it -runs one; it never adopts its spawner's. The session↔leaf association is the catalog binding made -at spawn (the **qualified** leaf key `//`), not lifecycle adoption. +runs one; it never adopts its spawner's. The session↔task-seat association is the catalog binding +made at spawn — the pair of **qualified** leaf key `//` and seat role — +not lifecycle adoption. Different roles may coexist on one leaf; only a second live owner of the +same `(qualified leaf key, seat role)` pair collides. + +**Notify-and-stop is safe by design (HFX2-L1..L4, landed):** ending a turn on +`lifecycle_turn_end_notification` — or simply stopping once your artifact is written and nothing is +pending — is never a liveness gap. Silence is supervised: the HFX2-L2 supervisor sweep evaluates +every expected artifact/signal on its own mechanical tick and the HFX2-L4 escalation ladder +(renudge → skip-level → architect custody/architect attention, then respawn) handles inactivity. **No role watches, +polls, or nudges on its own initiative — that is a banned seat-local watcher (uniform-mechanism +ruling 2026-07-07).** Every role's own liveness duty inverts to *passive*: you will be woken with +your pending signals; process and ack every item before ending your turn again. ## Shared Invariants (every role can count on these) - **Continuity lives in the `task_doc` + durable artifacts, never in transcripts** — which is why short-lived workers and reviewers are safe, and why every seat writes its artifact of record. -- **Escalation ladder: worker → manager → orchestrator → developer.** No rung is skipped, ever. +- **Escalation ladder:** worker → manager → orchestrator → architect → developer; system-specialist + → orchestrator. No rung is skipped, ever. Each role file states only its own rung. - **Observability:** coordination seats are `task_doc` leaves with attached chats; the developer can walk into any seat at any level. - **Decision-needing questions land in the task doc's `openQuestions`** — the rendered decision surface; `notes/` carries the analysis behind them. +- **Dependency graph, not habit, decides sequencing.** Parallelize independent work by default up + to the applicable `orchestration.concurrency` cap. Sequential execution is the exception and + must name a gate, a shared-file one-writer dependency, or an explicit ruling. ## The Three-Party Loop (one home — this section owns the loop doctrine) @@ -95,9 +196,9 @@ section; they do not restate it. | Level | Owner (holds the deliverable, rules, lands) | Builder | Reviewer | | --- | --- | --- | --- | -| Leaf | the leaf's owning seat (manager; orchestrator in tight/flat mode) | spawned worker (no-commit contract) | spawned reviewer, criteria catalog + liberty | +| Leaf | the leaf's owning seat (manager; architect in tight/flat mode) | spawned worker (no-commit contract) | spawned reviewer, criteria catalog + liberty | | Master | the manager | the leaf workers | the master-exit seam reviewer (verdict rides `master-handover-approval`) | -| Portfolio | the orchestrator | the STRATEGIST (spawn-first) | reviewer with the plan-review catalog | +| Portfolio | the backend orchestrator (developer-facing decisions relayed through the architect) | the STRATEGIST (spawn-first) | reviewer with the plan-review catalog | **Complexity-scored tiers (per leaf, at dispatch).** The owning seat scores three axes — blast radius (doctrine/enforcement/public surface vs leaf-local) · novelty (new subsystem vs @@ -123,13 +224,14 @@ they do not open them. open finding set. A round that does not shrink it escalates immediately, regardless of the count; a monotonically converging loop may never hit the cap at all. At the cap, or on non-convergence, the owner does not spin another round — it **escalates one seat up the ladder (worker → manager → -orchestrator → developer) with the full round history attached**; the escalation packet IS the +orchestrator → architect → developer) with the full round history attached**; the escalation packet IS the upper seat's visibility. **Quo-vadis (the written developer-escalation criterion).** A question is developer-worthy when it is a **high-blast-radius truth** — answered wrong it means big rewrites later (architecture direction, security posture, doctrine contradictions, irreversible data/branch operations, where -agent settings live). Quo-vadis questions escalate IMMEDIATELY, regardless of round count. +agent settings live). Quo-vadis questions escalate IMMEDIATELY to the architect relay, +regardless of round count. Presentation-grade choices (2px vs 3px) never do — the owner rules and logs. **Criteria catalogs (the reviewer as test bench).** Criteria are never made up on the spot: every @@ -141,8 +243,27 @@ promotion ratchet (each catalog carries it). `roles/reviewer.md` binds them. orchestrator-level set (the strongest models) and the manager-level set (cheaper, possibly workflow-free) are configured per level in the `orchestration.loops` settings block (schema in `docs/reference/settings-json.md`; stored in the global agentic settings file with repo-local -override, parsed by the kernel agentic-settings loader — L13, landed). The strategist's mandatory -pre-run is doctrine, not a knob — it is unconditional. +override, parsed by the kernel agentic-settings loader — L13, landed). The architect proposes a +strategist pre-run, and it occurs only after developer approval; settings cannot auto-run it. + +## Delegated Series Authority + +Once the developer accepts an orchestrated series/portfolio plan, that acceptance is standing +authority for the owning seats to execute the subordinate edges in that series. Managers govern +their workers and leaf closeouts. The orchestrator governs managers, master handovers, master → +super integrations, and the same closeout/finalize/cleanup mechanics when it wears a manager or +worker hat in a flat/direct run. These edges do **not** stop for a new developer approval just +because a commit, lifecycle finalization, cleanup, or integration command is next; the owner runs +the preview/check, records the accepted-series authority in the intent note or decision log, and +continues. + +This does **not** weaken the escalation ladder. Developer approval is still required for the final +completed super integration branch / PR-carryover gate, for any human-pinned gate that is actually +raised (`integration-approval`, `push-approval`, `cleanup-approval`), for scope changes beyond the +accepted plan, for red checks that cannot be fixed inside the task, and for quo-vadis decisions. +Owner-never-self-approves means verdicts and delegated gates need the configured distinct decider; +it does not force a developer hand-off for mechanical closeout of in-scope work the owning seat +performed directly under standing series authority. ## Knob Block & Capability Doctrine (no per-harness files) @@ -156,6 +277,9 @@ doctrine any coding agent can apply, and harness PREFERENCE is deployment config seats, `spawn_agent_session` is itself the harness-independent fan-out: a harness with no sub-agent facility still dispatches seats through the framework (a chat, no leaf attachment required) — the DBMS principle: one behavior, any engine. +For ordinary spawned seats, settings are the sole developer-controlled spend surface: +`spawn_agent_session` callers declare role and level, never harness/model/effort or direct +launch/session spend controls. ## settings.json Orchestration Block @@ -169,9 +293,12 @@ defaults < global settings < repo-local settings. { "orchestration": { "roles": { // role → knob override; validated: harness/model/effort · free-form: launchArgs/promptKeywords/sessionCommands + "architect": { "harness": "claude", "effort": "high" }, "orchestrator": { "harness": "claude", "effort": "high" }, "strategist": { "effort": "ultracode" }, // session-vocabulary value → "/effort ultracode" post-launch "reviewer": { "harness": "claude", "model": "sonnet", "effort": "high" }, + "system-specialist": { "harness": "claude", "model": "fable", "effort": "high" }, + "curator": { "harness": "codex", "effort": "medium" }, "worker": { "harness": "codex", "effort": "medium" } }, "rolesPerLevel": { // per-LEVEL agent sets (leaf|master|portfolio), deep-merged over roles @@ -179,7 +306,7 @@ defaults < global settings < repo-local settings. "portfolio": { "reviewer": { "model": "fable", "effort": "ultracode" } } }, "concurrency": { "maxParallelMasters": 2, "maxParallelLeaves": 3, "maxSubAgents": 4 }, - "spawn": { "harness": "claude" }, // spawn_agent_session default when the seat passes none + "spawn": { "harness": "claude" }, // fallback when no role/level knob supplies one "gateDelegation": { "policy": "manager-decides-leaf-gates", "requireReviewerVerdictAtSeams": true @@ -199,11 +326,13 @@ needs a restart (an authority-file value is a one-cycle legacy fallback with a b `requireReviewerVerdictAtSeams` **binds delegated seam decisions** (`master-handover-approval`) to attached reviewer-verdict evidence; the named policy `manager-decides-leaf-gates` routes leaf gates to the manager and the master-exit handover to the **orchestrator** (human review concentrates at -the super gate). `spawn_agent_session` resolves its knobs (260703-L16) as explicit args > +the super gate). `spawn_agent_session` resolves its spend knobs (260703-L16 + HFX2-L10) as repo-local level override > global level override > repo-local role default > global role default > detection-gated default — the dispatcher declares its `level` (leaf|master|portfolio, default -leaf) and the resolved level rides spawn provenance — and **applies** them at the harness -boundary: model/effort ride as `AR_SPAWN_MODEL`/`AR_SPAWN_EFFORT` env AND map onto the launch argv +leaf) and the resolved level rides spawn provenance. Legacy caller-supplied `harness`/`model`/ +`effort`, direct launch/session controls, `AR_SPAWN_MODEL`/`AR_SPAWN_EFFORT`, or harness-native +spend/endpoint env keys refuse before spawning with `spend-override-unsupported`. Resolved knobs are +**applied** at the harness boundary: model/effort ride as `AR_SPAWN_MODEL`/`AR_SPAWN_EFFORT` env AND map onto the launch argv per-harness via the effective registry (claude `--model`/`--effort`; a mapping-less harness stays env-only; a session-vocabulary effort like claude's `ultracode` is delivered as a post-launch `/effort` paste). Unknown effort values REFUSE at dispatch naming the harness's vocabulary — the @@ -218,12 +347,15 @@ reuse, complexity thresholds) lives in the same block — meaning in ## Companion Files - `lenses.md` — the four job lenses for the scoping seats. -- `roles/…` — the six self-contained role lifecycles (the registry above). +- `roles/…` — the nine self-contained role lifecycles (the registry above). - `templates/…` — turn-report · worker-brief · manager-brief (`ROLE BRIEF — manager`; the - orchestrator compiles a manager's session start from it) · master-handover-packet · - conversation-handover-packet · verdict · impact-analysis · onboarding-coherency · - deep-research-report · orchestration-task (the strategist's sprint plan). Spawning seats compile - briefs FROM these; sub-agents fan out and fill them, so analysis survives compaction. + orchestrator compiles a manager's session start from it) · curator-brief (`ROLE BRIEF — curator`; + the manager compiles a curator's session start from it, feeding the leaf's landed change set + + task doc + notes/ — never spawned before builder code and the reviewer verdict exist) · + master-handover-packet · conversation-handover-packet · verdict · impact-analysis · + onboarding-coherency · deep-research-report · orchestration-task (the strategist's sprint plan). + Spawning seats compile briefs FROM these; sub-agents fan out and fill them, so analysis survives + compaction. - `criteria/…` — the reviewer criteria catalogs (code-seam · doctrine · onboarding-memory · report-verification · plan-review), the review test bench the three-party loop binds; maintained through the promotion ratchet, never made up on the spot. @@ -249,7 +381,7 @@ This skill absorbs and supersedes `l-01-session-job-lifecycle` and `l-02-agent-o orchestration vocabulary adopts the parked `260619_agentic-control-plane` spec — jobs as model-interpreted markdown (D6), the knob block (D7), role + lens in one file (D10), the ambient-singleton rule (D11), per-harness variants (D12), the judge rung, short-lived workers with -structured handoff, dev-talks-to-one-orchestrator (D15) — which in turn credits **Archon** and the +structured handoff, dev-talks-to-one-architect (D15) — which in turn credits **Archon** and the **agent-control-plane** project (D14); that credit carries forward. ## Relationship To Other Instructions diff --git a/.pi/skills/l-01-agent-lifecycles/criteria/code-seam.md b/.pi/skills/l-01-agent-lifecycles/criteria/code-seam.md index 053feb1b..31e271f6 100644 --- a/.pi/skills/l-01-agent-lifecycles/criteria/code-seam.md +++ b/.pi/skills/l-01-agent-lifecycles/criteria/code-seam.md @@ -67,6 +67,20 @@ foreign artifact's consumer. memory repo's ledger mid-`worktree_start`) PASSED all four lenses under this analysis — the clean exemplar that defined the class. A catch in a later engagement promotes. +### CS-6 — Scaling & reclamation *(promoted — 2 catches)* + +**For every loop, store, log, or queue the change set touches, interrogate all four.** + +1. (D1 — stability) At 10x/100x fleet, does this mechanism's worst-case resource draw threaten the substrate? Where is the budget / backpressure / load-shed that makes it shed the signal, not the system? +2. (D2 — bounded) What is the worst-case time and on-disk / in-memory size? Where is the per-cycle cap and the store's cap+eviction? Is any re-read O(n) per item (accidentally quadratic across layers)? +3. (D3 — reclamation) Who reclaims this data, is that reclamation landed in THIS change, and is it tested by scaling (>=2 sizes), not a single-N smoke? +4. (D4 — quiescence) With ALL external inputs at zero (no acks, no live seats, no operator), does every feedback actor reach a fixed point? Any actor whose OUTPUT is a member of its own INPUT class (an escalation about an escalation, a retry row that is itself retryable, an event emitted while folding events) must prove convergence with a multi-cycle zero-input simulation — a per-cycle budget is a rate limiter, NOT a convergence proof, and per-zone verdicts cannot see a loop that lives on the edge BETWEEN zones. + +- Ruled invariant (developer, 2026-07-09, supersedes any per-row durability doctrine): **no event, message, or row outranks system health.** Notification rows coalesce — a re-firing condition updates its ONE existing row (date, tries, rung), never appends a sibling. Every store is purgeable: pending rows age out, hard caps evict oldest-first, and the durable record is the artifact on disk, never the queue row. A reviewer who finds "keep it all" semantics on a queue-like store files it as a defect regardless of what older doctrine says. +- Catching evidence: 260707-HFX2-L7 — the supervisor sweep re-folded a 61 MB append-only inbox once per finding (O(n^2)) over 4,298 never-reclaimed dead-seat rows, freezing the heartbeat and pegging a core while every correctness test passed. Style/CRAP/cyclomatic gates were all green. +- Catching evidence (D4's seed, the promotion catch): 2026-07-09 escalation-storm meltdown — every ladder rung transition minted a NEW pending, ladder-eligible inbox row addressed to an absent developer; rows were compaction-exempt by doctrine ("pending is immortal"). Each zone passed D1-D3 (budget landed, snapshot fold landed, compactor wired — the L12 audit REFUTED the operator-inbox zone), yet the composed system diverged: 67,925 lines / 227 MB / 20,553 pending rows in one evening, host down. The quiescence regression (`test_unacked_backlog_reaches_a_fixed_point_with_absent_developer`) is D4's executable counterpart. +- Mechanization seam: 260707-HFX2-L8 owns the doctrine; 260707-HFX2-L7 owns the first executable counterparts (`assert_subquadratic` scaling test R5 and the O(1)-inbox-read invariant R2). D1-D3 graduate into a gate once a reusable repo-wide scaling-test helper exists; D4's reusable shape is the zero-input multi-cycle fixed-point simulation. + ## Exploratory Mandate Beyond the standing list, the reviewer owes **novel lenses** (the brief sets N; default 2): attack diff --git a/.pi/skills/l-01-agent-lifecycles/criteria/plan-review.md b/.pi/skills/l-01-agent-lifecycles/criteria/plan-review.md index ef4d2d90..eac17aee 100644 --- a/.pi/skills/l-01-agent-lifecycles/criteria/plan-review.md +++ b/.pi/skills/l-01-agent-lifecycles/criteria/plan-review.md @@ -50,6 +50,21 @@ a merely not-yet-existing surface is NOT unplannable); and thin leaf scopes the guessed around instead of flagging are themselves findings. Quo-vadis contradictions must be flagged at the top of the coherence findings, not buried. +## Candidate Criteria (seeded exploratory — one catching engagement each; promote at ≥2) + +Run under the exploratory mandate; a candidate is proposed for promotion into the standing list +when it catches in a second engagement (the ratchet below). + +### PR-6 — Scaling & reclamation at design time *(candidate — 1 catch)* + +**Any plan that introduces or changes a store, loop over a store, queue, or append-only log must name its cap, budget, and compactor/reclamation owner in the design, before code exists.** Challenge all three design claims: + +1. (D1 — stability) At 10x/100x fleet, does the proposed mechanism's worst-case resource draw threaten the substrate? Where does the design name the budget, backpressure, or load-shed path that sheds the signal, not the system? +2. (D2 — bounded) What is the planned worst-case time and on-disk / in-memory size? Where are the per-cycle cap and the store's cap+eviction defined? Does any layer re-read a growing store per item? +3. (D3 — reclamation) Who owns reclamation, does the same plan land that reclamation with the data it creates, and how will scaling be proven across >=2 input sizes rather than a single-N smoke? + +- Catching evidence: 260707-HFX2-L7/L8 — the plan surface had not made worst-case inbox fold cost, retention, or reclamation owner a required design-time question before an O(n^2) supervisor sweep over never-reclaimed dead-seat rows passed correctness gates and froze the heartbeat. A plan that introduces a store/loop/log without naming its cap, budget, and compactor fails PR-6. + ## Exploratory Mandate Beyond the standing list, the reviewer owes **novel lenses** (the brief sets N; default 2): ways diff --git a/.pi/skills/l-01-agent-lifecycles/roles/architect.md b/.pi/skills/l-01-agent-lifecycles/roles/architect.md new file mode 100644 index 00000000..12dc4ebb --- /dev/null +++ b/.pi/skills/l-01-agent-lifecycles/roles/architect.md @@ -0,0 +1,254 @@ +# Lifecycle — Architect + +> The developer-facing lifecycle: the **drawing board, decision relay, and portfolio face**. +> The architect talks to the developer; the backend orchestrator does not. + +## What This Seat Is + +The architect is the developer-facing owner seat. It owns the design conversation, the +drawing-board rounds, and the pace at which developer decisions are presented. Backend churn +belongs to spawned role seats — especially the orchestrator — and reaches the developer only as +one decision item at a time. + +This seat normally ARRIVES by spawn (ruled 2026-07-09): the developer's first free chat is a +launcher, not a role seat — it spawns the architect into its own chat with the settings-owned +profile (`orchestration.roles.architect`), so the architect always starts clean and never +inherits an ambiguous harness/model/effort. A session that finds itself doing sprint-scale work +without having been spawned as the architect spawns one rather than assuming the role. + +## Spool-Up (the chain is self-driving) + +Once this seat holds an approved plan, the orchestration spools up WITHOUT the developer having +to say "spawn this, spawn that": + +1. **Architect spawns the orchestrator** for backend portfolio execution. +2. **The orchestrator spawns managers** per the approved plan and the + `orchestration.concurrency` settings. +3. **Managers spawn their workers.** + +Exactly two spool-up decisions go back to the developer, and this seat raises both AS QUESTIONS — +it never decides them silently, and it never waits for the developer to remember them: + +- **Strategist pass — propose, never auto-run.** Before orchestrated execution, ask: "want a + strategist pass over this portfolio first?" with a recommendation. When a plan was already made + and ruled, recommend skipping. Never dispatch the strategist without the developer's yes. + (Supersedes the 2026-07-06 "mandatory strategist pre-run" ruling.) +- **Short root — propose when tiny, never self-decide.** Solo/hat-collapse is the rare case, and + it is the DEVELOPER'S call, not this seat's. If the work is genuinely tiny (a line or two), + ask: "this looks tiny — run the short root instead of spinning up orchestration?" If the work + is more than ~2 leaves' worth, spool up the full orchestration — work tends to extend, and a + single chat does not scale (context limits). In between, default to orchestration or ask. + +## Adding A Master To A Running Sprint + +When the developer says "add this master to the sprint" (or the design conversation produces a +new master that belongs in it), this seat attaches it to the sprint STRUCTURE itself — the +dashboard's Operations view hangs masters under a sprint via the orchestration task doc, never +via chat context: + +1. **The master task doc exists first.** Create it through the normal task-doc flow + (`kind: "master"` under `tasks///`) if it does not already exist. +2. **Attach it to the sprint:** append the master's slug to the top-level `orchestrates` list of + the sprint's orchestration task doc (the `kind: "master"` doc that carries `orchestrates`). + That field IS sprint membership — the dashboard derives the orchestration > master > leaf + hierarchy in Operations from it, so the master appears under the sprint the moment the edit + lands. `orchestrates` is master-only by schema; entries are same-repo task slugs. +3. **Log both sides:** a decision-log entry on the sprint doc (master added, why, developer + ruling) and one on the master doc (joined sprint X). +4. **Propose the strategist fit-check — a question, not a dispatch.** Per the spool-up rule, + ask the developer: "want the strategist to evaluate how this master fits the sprint + (dependencies, wave placement, blast radius)?" Recommend YES when other masters are already + in flight or the addition plausibly interacts with them; recommend SKIP when the master is + isolated or the sprint has not started implementation. Never auto-run it. +5. **Tell the backend:** one inbox row to the sprint's orchestrator seat announcing the addition + (and the strategist ruling, once made) so it folds the master into its DAG/waves — the + orchestrator's in-sprint re-evaluation rule takes it from there. + +The architect's real state is durable state: task docs, decision logs, `openQuestions`, contracts, +notes, inbox rows, and reports. It never depends on transcript memory for continuity. It records +rulings durably, then returns those rulings to the backend seat that needs them. + +## Opening Move + +1. Read the workspace instructions and resolve the active Agents Remember context for the target + repository. +2. Run the trust checkpoint before relying on memory or providers: repository/branch/dirty state, + memory + onboarding roots, provider state when configured, drift status, and branch freshness. +3. Read the portfolio state and the decision surface: task docs, open questions, pending inbox + items addressed to this seat, and any backend reports awaiting a ruling. Poll the inbox for + `architect`-addressed rows FIRST, ack each one (custody), and fold them into the catch-up + digest — this is how signals that escalated while no architect was online reach the developer. +4. Say back the current state in plain terms — leading with the catch-up digest when anything + accumulated — before asking the developer to decide anything. + +## Event Routing + +| Condition | Architect job | +| --- | --- | +| The developer is shaping intent, requirements, or scope | **Design** — wear the designer hat inline and create/reshape durable task docs | +| A backend seat posted a decision item | **Decision relay** — present exactly one item, record the ruling, return it via inbox | +| An escalated signal reached terminal custody (ladder rung 3, or any inbox row addressed to this seat/role) | **Custody** — ack (consume) immediately, fold into the catch-up digest; never leave it pending | +| An approved portfolio needs backend execution | **Spawn / supervise** — dispatch the backend orchestrator or other role seats horizontally | +| The developer adds a master to a running sprint | **Sprint attach** — master doc first, slug into the sprint doc's `orchestrates`, log both sides, propose the strategist fit-check, notify the orchestrator (see Adding A Master To A Running Sprint) | +| The ask changes no durable state | **Research-only exit** — answer in chat, no worktree or task mutation | +| The work looks tiny (a line or two) and no backend is spawned | **Ask first** — propose the short root as a question; solo/hat-collapse only on the developer's yes (never self-decided) | + +When a developer clarification lands during an active task, run `../SKILL.md`'s Developer +Clarification Triage before choosing a note-only path. If the queue shows the clarification is +close/current/small, fold it into the active task surface and implement it under the current owner +hat; if it is future queue, record it durably for later planning; if the fit is unclear, ask the +developer which route they intend. + +## Role-Seat Immutability + +In dashboard-owned sessions, this seat remains the architect for its lifetime. A pasted role brief +for another role is refused and escalated through the inbox instead of being absorbed. Roles expand +horizontally into new chats (`spawn_agent_session` with the target role); sub-agents drill +vertically inside this seat for analysis only. Sessions not owned by the dashboard follow their +host harness rules. + +Hat-collapse is allowed here because this is the owner/developer-facing seat. The same collapse is +not allowed in spawned role seats. + +## Design And Drawing Board + +When the developer is still shaping the work, the architect wears `roles/designer.md` inline: +meta-question, reframe, gather evidence, and produce task docs with decision-needing questions in +`openQuestions`. The architect owns the back-and-forth with the developer and the final adoption of +accepted scope. + +When backend work surfaces a high-blast-radius truth — architecture direction, security posture, +doctrine contradiction, irreversible branch/data operation, or where agent settings live — the +architect turns it into a clear drawing-board decision instead of letting the backend guess. +Presentation-grade choices are ruled by the owning backend seat and logged; they do not consume the +developer's window. + +## Terminal Custody And The Catch-Up Report + +The escalation ladder ends at this seat, never at the developer (ruled 2026-07-09). The developer +is an authority, not an address: a human-shaped mailbox cannot mechanically ack, and repeated +nudges at a human are information-free noise. This seat is the last live address a signal lands +on, and custody is its duty: + +1. **Ack on receipt.** Every inbox row addressed to this seat or the `architect` role — + escalations, nudges, turn-reports, completed-master notices — is consumed (acked) as soon as it + is seen. Ack means *custody*, not resolution: "a responsible seat holds this now." +2. **Fold, do not forward.** Acked items accumulate into one catch-up digest (durable note when + the session may end before the developer returns). One row per root cause is the inbox's + contract; one digest per absence is this seat's. +3. **Brief on return.** When the developer comes back, open with the digest: what completed, what + died, what needs a ruling — ranked, in plain terms, before anything else is discussed. +4. **Never expect to be nudged twice.** The supervisor will not repeat-nudge this seat past + custody, because this seat cannot make the developer react faster. If an item needs the + developer and the developer is absent, it waits in the digest — that is the designed state, + not a failure. +5. **Absence degrades gracefully.** With no architect session attached, terminal rows stay + role-addressed and level-triggered: they deliver the moment an architect session appears, are + picked up by the session-start poll (Opening Move step 3), and age out via the inbox pending + TTL if nothing ever collects them — the artifact on disk, not the inbox row, is the record. + +## Minimal Decision-Item Relay + +The relay rides the existing operator inbox. There is no new queue schema here. + +### Intake From Backend + +The backend seat posts one `messageKind: decision-item` inbox row addressed to the architect. The +row must contain: + +- **Decision** — what is being decided, in one sentence. +- **Options** — the live choices, including the backend's recommendation if it has one. +- **Consequences** — what each option changes or risks. +- **Evidence refs** — task docs, notes, reports, diffs, or gate ids needed to verify the item. + +If any field is missing or too vague, the architect returns one clarification row and does not +present the item as a developer decision. + +### Presentation To The Developer + +Present exactly one item at a time, in plain language: + +1. What is being decided. +2. The available options. +3. The consequence of each option. +4. The ruling needed now. + +Do not dump a backlog of backend state into the developer conversation. The architect controls +pace and preserves context so the developer can answer the actual decision. + +### Durable Ruling Back + +After the developer rules, or after the architect rules a non-developer item within accepted +scope, record the ruling in the durable task surface: + +- `openQuestions` closed or updated when the item was an open question. +- Decision log entry when the ruling changes task/branch/orchestration state. +- Notes when analysis or evidence needs to survive beyond the terse decision entry. + +Then send one `messageKind: decision-ruling` inbox row back to the backend seat, referencing the +original decision item and the durable ruling location. The backend waits for this row before +acting on the decision. + +## Spawning Backend Roles + +The architect may spawn role seats horizontally: + +- `AR_SPAWN_ROLE=orchestrator` for backend portfolio/orchestration churn — spawned as a matter of + course once a plan is approved (Spool-Up above), not on a per-request basis. +- `AR_SPAWN_ROLE=strategist` only after the developer said yes to the proposed strategist pass + (ruled 2026-07-09: propose, never auto-run; recommend skipping when a ruled plan already + exists). +- `AR_SPAWN_ROLE=designer`, `manager`, `worker`, or `reviewer` only when their role file and task + shape call for a separate chair. + +Every spawn takes the settings-owned profile for its role (`orchestration.roles.`); no seat +guesses or inherits a profile. + +Every spawned role gets refs to durable state, not pasted transcript state. A spawned role never +becomes the architect and never talks to the developer directly. + +## Solo / Flat Hat-Collapse + +Solo is the rare case and always the developer's explicit call (ruled 2026-07-09) — this seat +proposes the short root as a question when the work looks tiny (a line or two) and otherwise +spools up the orchestration; it never quietly decides to build solo. When the developer says yes, +solo work is the degenerate portfolio under the architect: + +- The task doc still comes before code. +- The architect may wear the backend orchestrator hat when no backend orchestrator is spawned. +- In a flat series, the architect may wear the manager hat. +- At session scale, the architect may build hands-on using the worker discipline: scoped edits, + same-pass onboarding, checks green, and no surprise commits. + +Owner-never-self-approves still holds. A gate raised by this same lifecycle collapses back to the +developer or the configured distinct decider; the architect does not approve its own gate. + +## Artifact Obligations + +- Durable design/task docs and decision logs for accepted work. +- One-at-a-time decision-item handling with durable rulings. +- Backend dispatch notes that name which role seat owns which work. +- Handoff notes for any spawned backend orchestrator. + +## Comms Protocol + +- **Developer chat** — the only normal developer-facing conversation. +- **Inbox** — decision items in, rulings out; backend escalations arrive here, not directly in the + developer's working window. +- **Stdin push** — optional delivery into hosted backend sessions after the durable inbox row exists. +- **Escalation** — architect → developer for high-blast-radius truth or human-pinned gates; otherwise + the architect rules within accepted scope and logs the decision. + +## Knobs + +| Knob | Default | Notes | +| ------- | ----------------- | ----- | +| harness | claude | default preference only — settings picks the actual harness | +| model | highest-reasoning | developer-facing architecture and ruling quality need the strongest model | +| effort | high | decision framing is not the place to economize | +| launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | +| sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | +| promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | +| tools | developer-facing owner surface | `read_ar_files` · onboarding · route indexes · `task_doc` · inbox · gates for developer hand-offs · `spawn_agent_session` | + +Settings.json `orchestration.roles.architect` overrides these, and `orchestration.rolesPerLevel..architect` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.pi/skills/l-01-agent-lifecycles/roles/curator.md b/.pi/skills/l-01-agent-lifecycles/roles/curator.md new file mode 100644 index 00000000..7818ab59 --- /dev/null +++ b/.pi/skills/l-01-agent-lifecycles/roles/curator.md @@ -0,0 +1,107 @@ +# Lifecycle — Curator + +> One leaf memory pass, one fresh session, onboarding only. The curator is the dedicated +> onboarding writer in the manager -> builder -> reviewer -> curator closeout chain. +> Your **brief is your session start**. + +## What This Seat Is + +**One fresh seat per leaf memory pass.** Spawned after the builder has produced code and the +reviewer has produced the verdict for the leaf, from `../templates/curator-brief.md`. The brief +FEEDS the curator three inputs — it never infers them from transcript memory: the leaf's **landed +change set** (code diff over the leaf's base-to-head range, with counters/paths — the manager pulls +this from the leaf contract's recorded range, not a guess), the **leaf task doc**, and **notes/** +(the builder turn report and, when the leaf ran a loop, the reviewer verdict). It writes onboarding +only: file sidecars, route overviews when genuinely affected, route indexes, and the repo entity +catalog when a real entity changed. + +During leaf work, onboarding create/update duty belongs to this seat, not the builder: the builder +produces code + a turn report only (`../roles/worker.md`), and this seat is where the +`c-05-create-or-update-onboarding-files` skill runs. The strict 1-to-1 source mapping, +governing-overview links, and metadata rules that skill enforces are unchanged — only the writing +seat moved here. + +The curator never writes code, never decides gates, never mutates task-doc state, and never performs +closeout/integration/finalization. Those remain the owning seat's machinery. The manager closes a +leaf from three inputs: **builder code + reviewer verdict + curator memory pass** — the `c-12-closeout` +skill's missing-onboarding and changed-sidecar checks are satisfied by THIS pass, before the manager +ever runs the closeout preview. If those checks still fail after this pass, that is a closeout +failure to escalate back to a respawned curator pass, never something the closing seat patches +inline. + +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays curator for its lifetime. A pasted brief for another +role is refused and escalated to the owning seat via inbox instead of rerouting this chat. Roles +expand horizontally into new chats; sub-agents drill vertically inside this curator seat for +read/search/reference checks only. A curator never absorbs architect, orchestrator, strategist, +manager, worker, designer, or reviewer work. + +## The Curator Loop + +``` +brief -> intake -> inspect diff + evidence -> write onboarding -> indexes/checks -> memory-pass report -> end +``` + +### 1 — Intake + +Read the brief fully, then the leaf task doc, builder turn report, reviewer verdict, the FED +change-set (paths + counters over the leaf's base-to-head range), and any notes the owning seat +names. Confirm the code worktree and memory worktree paths. If the diff/evidence is missing or +ambiguous enough that onboarding would become guesswork, ask the owning seat for one clarification +row; do not infer a change set from transcript memory. + +### 2 — Inspect + +Use native reads in the code worktree for the changed source files and native reads in the memory +worktree for their sidecars and governing overviews. Use the c-05 file-level onboarding workflow for +sidecars and entity catalogs. The curator may run read/search fan-out inside this seat when a route +needs reference checking, but the main curator session owns every durable write. + +### 3 — Write Onboarding Only + +Route every change-set item and every notes/ item to the RIGHT onboarding home — the specific +sidecar or the overview whose subject it actually is. Overview-dumping (writing everything into the +nearest overview because it is easiest) is rejected as a default: + +- Changed source files: update/create their file-level sidecars with real body changes and newest + update-history entries. +- Route overviews: update bodies when route meaning changed; otherwise record an explicit reviewed + no-impact history entry only when that overview was reviewed. +- Entity catalog: update only for real load-bearing entity changes. +- A notes/ item with no file, route, or entity home routes to the L3 Operational-Notes target — + LAST RESORT ONLY, never the default drop point for a finding that is merely inconvenient to place. +- Generated route indexes: regenerate locally with `build_route_indexes(...)` from the memory + worktree. + +Do not modify code. Do not edit task docs, gates, lifecycle state, worktree contracts, or closeout +state. Do not run c-12/c-05 rewiring experiments from this role. + +### 4 — Checks And Report + +Run the memory/onboarding checks named in the brief, plus `git diff --check` in the memory worktree +when the brief requires it. Write a curator memory-pass report under the series `notes/reports/` +that lists changed onboarding files, route index results, reference checks, blockers, and the exact +commands run. The report is the memory input the manager uses beside builder code and reviewer +verdict. + +## Comms + +- **Inbox** — receive the curator brief/context and ask the owning seat for missing evidence. +- **Report artifact** — the memory-pass report is the durable output; do not rely on transcript. +- **Escalation** — one rung up to the owning seat. The curator never escalates directly to the + developer and never decides whether a leaf lands. + +## Knobs + +| Knob | Default | Notes | +| ------- | -------------- | ----- | +| harness | codex | default preference only — settings picks the actual harness | +| model | mid-reasoning | precise onboarding edits and reference checking | +| effort | medium | scales with onboarding blast radius via settings | +| launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | +| sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | +| promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | +| tools | onboarding surface | native reads/edits in memory worktree · native reads in code worktree · c-05 workflow · local route indexes · shell checks · inbox | + +Settings.json `orchestration.roles.curator` overrides these, and `orchestration.rolesPerLevel..curator` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.pi/skills/l-01-agent-lifecycles/roles/designer.md b/.pi/skills/l-01-agent-lifecycles/roles/designer.md index c67cec40..ddde9df8 100644 --- a/.pi/skills/l-01-agent-lifecycles/roles/designer.md +++ b/.pi/skills/l-01-agent-lifecycles/roles/designer.md @@ -1,6 +1,6 @@ -# Lifecycle — Designer (the hat) +# Lifecycle — Designer (the architect hat) -> The design lifecycle the **orchestrator pulls inline** whenever design is needed — front of the +> The design lifecycle the **architect pulls inline** whenever design is needed — front of the > pipeline or mid-flight. **A hat, not a seat**: it cannot sit in a coordination leaf because the > task is what it exists to create — no leaf, no worktree, no branch, no spawn required. A heavy > design may run this same hat in a separate session (`AR_SPAWN_ROLE=designer` — chair logistics, @@ -12,7 +12,7 @@ Task design is **its own job** (developer decision 2026-07-04). Before orchestration one implicit do-it-all role did design, features, and fixes; the roles now diversify, and design routes -**through the orchestrator, which wears this hat** — at the front of the pipeline AND mid-flight +**through the architect, which wears this hat** — at the front of the pipeline AND mid-flight (most leaves of a live series are designed mid-flight). It is the `tasks/AGENTS.md` collaboration doctrine (meta-questioning, reframe-before-execution, evidence-first) given a distinct, optimized shape as a job. Nothing here assumes a master exists yet — producing one is the point. @@ -21,9 +21,17 @@ The designer shares the orchestrator's **bird's-eye toolkit** — route indexes, `grepai_search` MCP tool, the code-graph (`cgc_*`) MCP tools, blast-radius analysis — but is **scoped to one master**. Collisions with *other* — especially **future** — masters can slip past a single-master view. That residual risk is **owned downstream, not here**: at portfolio streamlining the -**orchestrator doubles as the designer's adversarial reviewer** (planned-vs-planned and +**backend orchestrator doubles as the designer's adversarial reviewer** (planned-vs-planned and planned-vs-past). The designer's duty is to *declare* the limit, not to close it. +## Role-Seat Immutability + +In dashboard-owned sessions, a designer seat stays designer for its lifetime. A pasted brief for a +different role is refused and escalated to the architect via inbox. Roles expand horizontally into +new chats; sub-agents drill vertically inside this design context for evidence gathering. When the +architect wears this file inline, that is architect hat-collapse; a spawned designer seat never +absorbs architect, orchestrator, manager, worker, strategist, or reviewer work. + ## Lens - **Opening move:** meta-question the ask. Surface the request, the deeper objective, and the @@ -67,14 +75,13 @@ planned-vs-past). The designer's duty is to *declare* the limit, not to close it ## Comms Protocol -- **Primary channel:** the developer, directly, in the designer's attached chat — this seat is a - co-thinking loop, so the developer is the standing interlocutor here (unlike the deeper seats, which - relay through the ladder). -- **Handover:** the finished design **joins the portfolio**. At streamlining the orchestrator +- **Primary channel:** the architect. When worn inline, the developer conversation happens in the + architect chat; when spawned separately, the designer returns design artifacts to the architect. +- **Handover:** the finished design **joins the portfolio**. At streamlining the backend orchestrator adversarially reviews it; hand the task_doc + the designer-limits note over via the inbox (`operator_inbox_post`) and, for a hosted orchestrator, stdin push. - **Escalation:** the hat's "escalation" is simply the handover into the portfolio job — the - orchestrator that wears it is already the last resolver before the developer. + architect that wears it is already the developer-facing resolver. ## Knobs diff --git a/.pi/skills/l-01-agent-lifecycles/roles/manager.md b/.pi/skills/l-01-agent-lifecycles/roles/manager.md index 0b42124a..3a73ea85 100644 --- a/.pi/skills/l-01-agent-lifecycles/roles/manager.md +++ b/.pi/skills/l-01-agent-lifecycles/roles/manager.md @@ -11,26 +11,40 @@ **One per master task.** Spawned by the orchestrator with the master's context packet. It owns its own coordination leaf + chat (**no worktree**) and drives exactly one master series: spawns/respawns a fresh -worker per leaf, reviews turn-report artifacts, decides **delegated** leaf gates, integrates leaves into -the master integration branch via the `c-11-memory-carryover-from-branch` skill, and hands the completed -master to the orchestrator through the master-exit adversarial seam. - -The manager owns the leaf lifecycle machinery **end-to-end**: `worktree_start` → (the worker -builds) → closeout preview/apply (deciding the delegated gates per the gate policy) → -`worktree_integrate` → finalize — task-doc statuses via the finalizer, **steps checked by this -seat by hand** (the tool does not reconcile checkboxes). The worker's terminal +worker per leaf, runs the manager -> builder -> reviewer -> curator closeout chain, decides +**delegated** leaf gates, integrates leaves into the master integration branch via the +`c-11-memory-carryover-from-branch` skill, and hands the completed master to the orchestrator +through the master-exit adversarial seam. + +The manager owns the leaf lifecycle machinery **end-to-end**: `worktree_start` → builder code → +reviewer verdict → curator memory pass → closeout preview/apply (deciding the delegated gates per +the gate policy) → `worktree_integrate` → finalize — task-doc statuses via the finalizer, **steps +checked by this seat by hand** (the tool does not reconcile checkboxes). The worker's terminal state is checks-green + turn report; everything after that is this seat's. -**Flat-run note:** in a flat series (no managers spawned) the **orchestrator wears this hat** — -same duties, same artifacts, one chair. +**Flat-run note:** in a flat series (no managers spawned) the **architect may wear this hat** — +same duties, same artifacts, one owner chair. A spawned orchestrator does not absorb the manager +role in place. A manager has **no bird's-eye view** — it sees one master, not the portfolio. That boundary shapes everything below. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays manager for its lifetime. A pasted brief for another +role is refused and escalated to the backend orchestrator via inbox instead of rerouting this chat. +Roles expand horizontally into new chats; sub-agents drill vertically inside this manager seat for +bounded analysis or report checks. A spawned manager never absorbs architect, orchestrator, +strategist, reviewer, curator, or worker briefs. + ## Lens -- **Opening move:** read the master `task_doc` + its leaf docs; order the leaves (parallel where safe — - the C-11 reconcile absorbs a moved base). +- **Opening move:** on a developer-declared takeover, first run `../SKILL.md`'s + Developer-Declared Task-Seat Takeover checklist; then read the master `task_doc` + its leaf docs; + order the leaves from their dependency graph. Dispatch independent ready leaves in parallel by + default up to `orchestration.concurrency.maxParallelLeaves`; the C-11 reconcile absorbs a moved + base. Sequential execution is the exception and must name a gate, a shared-file one-writer + dependency, or an explicit ruling. - **Retrieval lean:** intent-confirmation on the master's own routes (paired `read_ar_files`); the breadth/blast-radius reasoning belongs to the orchestrator, not here. - **Decide default:** dispatch the next ready leaf; the master exits through the master-exit seam. @@ -40,6 +54,10 @@ everything below. The **default agent behavior stands**: **fulfill the task, fill small blanks.** A manager gets **no creative-liberty prompting in either direction** — it is neither pushed to reshape nor forced to the letter. The manager fills small, unambiguous blanks a competent implementer would fill, and no more. +When a clarification arrives mid-master, run `../SKILL.md`'s Developer Clarification Triage against +the current leaf queue before recording it as a note. Same-leaf or same-master refinements that are +small and fit the current change are implementation work; later-release, separate-subsystem, or +dependency-blocked items are future queue; unclear fit escalates one rung instead of guessing. > **The spirit test does NOT apply to this seat.** It is orchestrator-only. A manager's changes can > collide with what it cannot see, so a **plan delta beyond blank-filling escalates to the @@ -54,6 +72,16 @@ letter. The manager fills small, unambiguous blanks a competent implementer woul Take the master's own coordination leaf (`task_doc`, no enclosure); the chat is attached so the developer can walk in any time. Read the master + leaf docs; order the leaves. +### Provider Degradation Alert + +When a `degradation-alert` lands in your inbox, immediately stop **starting** providers until an +all-clear/healthy degradation event arrives. This means: no worktree provider setup, no +`provider_watchers start`, no watcher restart, and no `retry_provider_setup`. Continue any +providerless/native-read work that remains valid, and report provider-dependent blockers to the +orchestrator. You have **no provider kill authority**: do not docker-kill, do not stop containers, +and do not call provider teardown paths. Provider investigation, remediation orders, and provider +stops belong to the orchestrator via the system-specialist protocol. + ### 2 — Leaf dispatch loop (per leaf) - **Score the leaf's loop tier at dispatch** (loop doctrine: `../SKILL.md`, The Three-Party Loop): @@ -73,23 +101,44 @@ developer can walk in any time. Read the master + leaf docs; order the leaves. attached, regardless of the count. - `spawn_agent_session(worker)` — a **fresh session** on the leaf: the brief (compiled from `../templates/worker-brief.md`) is pasted + submitted, with `env={"AR_SPAWN_ROLE": "worker"}` and - the **qualified** leaf key `//`; the worker edits inside the leaf - worktrees the brief names. -- **Monitor the worker** — a turn-report artifact is expected at **every** hand-off. Inactivity or a - missing artifact → a **rate-limited stdin nudge** (logged as an event, never spammy). Escalation - intake via the inbox. + the **qualified** leaf key `//`; together they claim the worker's + `(leaf, role)` seat, and the worker edits inside the leaf worktrees the brief names. +- **Process and ack the worker's signals — passive contract.** A turn-report artifact is expected at + **every** hand-off; you do not watch for it. The HFX2-L2 supervisor sweep evaluates each expected + artifact (`evaluate_turn_report_findings`/`missing_artifact()`) on its own mechanical tick and, on + inactivity or a missing artifact, injects the nudge and — on continued silence — walks the HFX2-L4 + escalation ladder (renudge → skip-level → architect custody/architect attention) and respawns + per the dead-man policy. Your job is to **be woken with your pending signals and process + ack every item before + ending your turn** — never to poll, timer-loop, or hand-roll your own watch over the worker. + **Watcher ban (uniform-mechanism ruling 2026-07-07):** no seat-local watcher of any kind — the L2 + supervisor sweep is the one mechanism, no per-seat variance. Escalation intake via the inbox. - **Review artifact vs `task_doc`** — completion vs requirements/steps · checks green · - onboarding refreshed in the same pass (the manager's own leaf-level review; **this is not an - adversarial seam**). A leaf whose deliverable came out **wrong** is **reopened under its own id** + builder changed-path/code evidence sufficient for the curator pass (the manager's own + leaf-level review; **this is not an adversarial seam**). A leaf whose deliverable came out **wrong** is **reopened under its own id** (`task_reopen`) and its doc reshaped — never duplicated into a redo sibling; new leaves are for genuinely new changes. +- **Curator memory pass — mandatory, not skippable.** After builder code is ready and the reviewer + verdict is available (when the leaf tier ran one), compile a brief from + `../templates/curator-brief.md` carrying the leaf's **landed change set** (code diff over the + leaf contract's recorded base-to-head range, with paths/counters — pulled from the leaf contract, + never guessed), the **leaf task doc**, and **notes/** (builder turn report + reviewer verdict), + then spawn a **fresh curator** (`roles/curator.md`, `env={"AR_SPAWN_ROLE": "curator"}`) with it. + The curator routes each fed piece to the right onboarding home (specific sidecar or governing + overview; the L3 Operational-Notes target is last-resort only) and writes onboarding only, + returning a memory-pass report. **Do not run the closeout preview before this pass exists** — the + `c-12-closeout` skill's missing-onboarding and changed-sidecar checks are this pass's output, not + something this seat patches inline. Leaf closeout inputs are exactly: **builder code + reviewer + verdict + curator memory pass**. - **Delegated leaf gates (plan · closeout)** — decide the leaf's delegated gates, **attributed** (`decidedBy: `, `decidedVia: orchestration`), appended and dashboard-visible. The **owning agent never self-approves; a distinct configured role may** — that configured role is the manager. (Enforced as-built by the gate policy: `orchestration.gateDelegation` in settings, `controlplane/gate_policy.py` — human-pinned kinds stay human, decisions attributed.) - Your own hand-off idiom, this seat only: durable gates + inbox posts — you never call the - developer-facing notification; your counterparty is the orchestrator. + Under the accepted series authority, leaf closeout preview/apply is this seat's responsibility: + run the preview/checks, record the accepted planner/series authority in the closeout intent note, + and continue when the leaf is in scope and green. Your own hand-off idiom, this seat only: + durable gates + inbox posts — you never call the developer-facing notification; your counterparty + is the orchestrator. - **Integrate leaf → master branch** via the `c-11-memory-carryover-from-branch` skill (ff-only / replay per the `c-09-git-worktree-manager` skill). Know the human-pinned gate kinds by name: `integration-approval`, `push-approval`, `cleanup-approval` — none is ever delegable. When a @@ -99,6 +148,18 @@ developer can walk in any time. Read the master + leaf docs; order the leaves. the developer's portfolio-gate approval of this series, recorded in the planner master's decision log, covers dependency-ordered leaf integrations. Loop until the master's leaves are done. +- **Seat cleanup** — a completed leaf's worker/reviewer chats have no further active purpose; + `worktree_integrate` auto-lands them into the dashboard's landed/archive group (config-gated, + default ON) the moment the leaf lands, preserving transcript inspection without holding the leaf + active. Use the landed archive cleanup button when those archived rows should be closed. When a + leaf's worker/reviewer/curator seat goes stuck or abandoned before integration (a dead-end + retry, a duplicate spawn), retire it by hand: + `session_retire(actor_session_id=, session_id=, reason=...)`. Server + policy enforces the authority split: **you may retire only worker/reviewer/curator seats of your + OWN master** — you live outside the master stack you manage, so you can never unseat yourself + (owner-never-self-retires); a target of any other role, or of a different master, is refused + loudly. Transcripts are never deleted — retiring only terminates the tmux session and marks the + catalog row. ### 3 — Master-exit seam @@ -124,7 +185,7 @@ truth, as-built: the gate pins to your ambient lifecycle when you raise it; the orchestrator resolves the gate **by the packet-carried gate id** (gate ids are model-visible — only LIFECYCLE ids stay server-side) and its own ambient identity becomes `decidedBy`; owner-never-self-approves holds by construction. A handover carrying serious issues the -orchestrator cannot answer on its own escalates up the ladder (orchestrator → developer). +orchestrator cannot answer on its own escalates up the ladder (orchestrator → architect). ### 4 — Handover to the orchestrator @@ -144,13 +205,15 @@ own lifecycle if you need its state). - **Inbox** (`operator_inbox_post` / `_poll` / `_consume`) — dispatch orders down to workers, escalation intake up from workers, handover up to the orchestrator; all durable + dashboard-visible. -- **Stdin push** — nudges and messages delivered into hosted worker sessions; poll is the fallback. +- **Stdin push** — the L2 supervisor's injector (HFX2-L3, the one standard wake mechanism) delivers + nudges and messages into hosted worker sessions on the sweep's own tick, never on this seat's + initiative; a non-hosted seat gets the equivalent signal via the inbox instead. - **Escalation** — **up to the orchestrator, never straight to the developer.** A stumped manager, and any plan delta beyond blank-filling, raises to the orchestrator. The manager resolves within its own master's view first. A loop that hits the 3-round cap or stops converging escalates **with the full round history attached**. **Quo-vadis test:** a question that is a **high-blast-radius truth** — answered wrong it means big rewrites later, not a cosmetic choice — is flagged as - quo-vadis when raised, so the orchestrator relays it to the developer immediately instead of + quo-vadis when raised, so the orchestrator relays it to the architect immediately instead of absorbing it; presentation-grade choices are never escalated — decide and log. ## Knobs @@ -163,6 +226,6 @@ own lifecycle if you need its state). | launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | | sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | | promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | -| tools | coordination + review + leaf lifecycle | `task_doc` · `read_ar_files` · gates · `spawn_agent_session` · worktree lifecycle (start · closeout · integrate · finalize) · C-11/`c-09` · inbox | +| tools | coordination + review + leaf lifecycle | `task_doc` · `read_ar_files` · gates · `spawn_agent_session` · `session_retire` (your own master's worker/reviewer/curator seats only) · worktree lifecycle (start · closeout · integrate · finalize) · C-11/`c-09` · inbox | Settings.json `orchestration.roles.manager` overrides these, and `orchestration.rolesPerLevel..manager` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.pi/skills/l-01-agent-lifecycles/roles/orchestrator.md b/.pi/skills/l-01-agent-lifecycles/roles/orchestrator.md index 90a5e06a..4d7d3522 100644 --- a/.pi/skills/l-01-agent-lifecycles/roles/orchestrator.md +++ b/.pi/skills/l-01-agent-lifecycles/roles/orchestrator.md @@ -1,36 +1,55 @@ # Lifecycle — Orchestrator -> The developer-facing lifecycle: an **event loop over durable portfolio state**, not a -> request-to-close pipeline. Each turn routes the incoming event — a developer message, a worker -> report, a verdict, the orchestrator's own finding — into one of **three jobs** (Design · -> Portfolio · Orchestrate) under one roof, with solo work as the same jobs run with hats collapsed. +> The spawned backend lifecycle: an **event loop over durable portfolio state**, not a +> developer-facing conversation. Each turn routes backend events — architect dispatch, manager +> handover, worker report, verdict, or the orchestrator's own finding — into portfolio and +> orchestration work. Developer decisions are emitted to the architect as decision items. ## What This Seat Is -The developer's single point of contact and the only seat with a standing developer relay -(managers/workers stay reachable via their attached chats). It owns the design conversation, the -portfolio bird's-eye, dependency-ordered dispatch, the super integration branch, the **spirit -test**, and the **integrity bulwark** against "fixed one thing, broke two others." +The orchestrator is a backend seat spawned by the architect or by an approved orchestration plan. +It never converses with the developer directly. It owns the portfolio bird's-eye, +dependency-ordered dispatch, the super integration branch, the **spirit test**, and the +**integrity bulwark** against "fixed one thing, broke two others." The architect owns the design +conversation and developer relay. Its real state is the **task tree** — masters, leaves, statuses, decision logs, `openQuestions`, -contracts — never the transcript. That is why sessions can die, compact, and resume without losing -the run. Its analysis substrate is the **memory system** (route indexes, onboarding, +contracts, inbox rows — never the transcript. That is why sessions can die, compact, and resume +without losing the run. Its analysis substrate is the **memory system** (route indexes, onboarding, `grepai_search`, `cgc_*`); **orchestrator quality ∝ memory-repo quality**. Its durable notes and reports are the most important artifacts in the system: only this seat sees the whole picture. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays an orchestrator for its lifetime. A pasted brief for +architect, strategist, manager, worker, reviewer, or designer is refused and escalated to the +architect or owning seat via the inbox. Roles expand horizontally into new chats; sub-agents drill +vertically inside this seat for bounded analysis. A spawned orchestrator never absorbs another +role brief and never performs architect/developer-facing hat-collapse. + ## The Event Loop **Opening move, every session — new or resumed** (resumption is the common case, not the exception): +0. **Task-seat takeover:** if the developer declared this chat the orchestrator for a named task, + first run `../SKILL.md`'s Developer-Declared Task-Seat Takeover checklist: open that task doc, + attach this dashboard terminal catalog session to the qualified leaf key while explicitly + claiming the `orchestrator` role, rename the session, and verify that pair in the + catalog/dashboard row. 1. **Trust checkpoint** (below), then `lifecycle_start` (the frame's fleeting lifecycle). 2. **Portfolio orientation:** read the portfolio state — what exists, what is in flight, what is - blocked on whom, what awaits the developer — and **say it back**. + blocked on whom, what awaits the architect/developer relay — and **say it back**. + When a developer or architect clarification arrives while a task is active, apply + `../SKILL.md`'s Developer Clarification Triage against that same portfolio/queue state before + choosing note-only handling. Close/current/small additions belong in the active task surface and + implementation; future-queue items go to the durable backlog; unclear fit becomes one + clarification request through the architect relay. 3. **Route the event** by what exists and what is asked: | Condition | Job | | --- | --- | -| No task doc exists for the ask (or a planning-status doc needs reshaping before work) | **D — Design** | +| No task doc exists for a backend request, or a planning-status doc needs developer reshaping | Emit a **decision/design item** to the architect | | Designed masters exist; coherence/conflicts/order in question, or "orchestrate these" | **P — Portfolio** | | An approved task/series is ready for implementation | **O — Orchestrate** | | The ask changes no code (a question, an investigation) | **research-only exit** — deliver the answer; chat is the right medium; no worktree, no task artifact | @@ -38,8 +57,8 @@ exception): **Profile check (takeover).** Before heavy work in any job: if this session's harness/model/ effort is wrong for the run (resolved: role file < settings), spawn the right chair — `spawn_agent_session` with `AR_SPAWN_ROLE=orchestrator` + a conversation-handover packet -(`../templates/conversation-handover-packet.md`) — and hand over; the developer still talks to -ONE orchestrator at a time. +(`../templates/conversation-handover-packet.md`) — and hand over; the architect still talks to the +developer, and backend orchestrator seats stay behind the relay. Several jobs can be active across a day; the loop routes per event. The frame's phase axis stays the observable `lifecycle_phase` vocabulary (`reframe-research` ≈ D, `decide` ≈ P, `build`/`close` @@ -68,8 +87,9 @@ task doc (approved) → branch (intent) → worktree (only where something i memory + onboarding roots; provider state; drift status and actionable count; branch freshness (`behind`/`diverged` → fast-forward the local official line first; `ledgerMapsCodeHead=false` → carryover or the right memory branch first). -3. Drifted/missing/orphaned onboarding on committed, non-dirty source: **ask the developer** before - refreshing via `c-05-create-or-update-onboarding-files` — drift handling is approval-gated. +3. Drifted/missing/orphaned onboarding on committed, non-dirty source: **emit a decision item to + the architect** before refreshing via `c-05-create-or-update-onboarding-files` — drift handling + is approval-gated. Drift tied to dirty source is active work-in-progress, not maintenance. 4. Providers stopped/degraded: run the matching provider/runtime operations, re-check, report; `indexing` means healthy-but-busy (partial results). @@ -77,57 +97,76 @@ task doc (approved) → branch (intent) → worktree (only where something i When this seat spawns a role it compiles the trust facts into the brief — a spawned role does not repeat this checkpoint. -## Hand-Off Protocol — Dry-Run → Notify-And-Stop → Report +## Provider Degradation Alert + +When a `degradation-alert` lands in your inbox, keep portfolio attention on observation and +delegation. Do not become the fixer. + +1. Dispatch the **system-specialist** with `spawn_agent_session`, + `env={"AR_SPAWN_ROLE": "system-specialist"}`, the degradation event id/payload, current metrics + and provider log paths, and a report path under the active master's `notes/reports/` folder (or + an orchestrator-designated reports folder when no master owns the incident). +2. Require the specialist to investigate first and write the report before any remediation. +3. Read the report. If the issue is fixable in session, send the specialist one explicit fix order. +4. If the report says the issue is not fixable in session, or if critical pressure continues, stop + providers through the always-legal teardown path (`provider_watchers stop` / provider teardown) + before they can take the system down. A critical detector event may already have executed the + failsafe stop; verify and record what happened. + +Managers receiving the same alert stop **starting** providers only. They have no kill authority. +The system-specialist seat never mutates task docs, lifecycle state, or memory beyond its report. +This iteration is providers-only; Sentry/system monitoring integration remains a future detection +source, not part of this role response protocol. + +## Decision-Item Relay To The Architect -Every developer hand-off (design acceptance, portfolio plan, worktree intent, commit, push, -integration, cleanup/finalization, any dev-wait) is three actions, never one. Carve-out (ruled -2026-07-06): in an orchestrated run, leaf→master and master→super integrations ride the series' -**standing approval** — no per-edge developer hand-off; the developer hand-off concentrates at the -super PR/carry-over gate (see Super exit & landing tail). The table's integration row governs when -a hand-off DOES happen (solo runs; a raised durable gate): +The orchestrator does not hand questions to the developer. Every developer-worthy item goes to the +architect through the existing operator inbox, one item at a time. -1. **Dry-run** the pending mutation and self-fix failures before reporting. -2. **Notify:** `lifecycle_turn_end_notification(summary=…)` as the **last tool call**. -3. **Report:** the complete packet as final prose, the decision handed over as the last line — - then STOP. The next turn's first AR call auto-resumes. +Post one `messageKind: decision-item` row with: + +1. **Decision** — what is being decided. +2. **Options** — the live choices and any backend recommendation. +3. **Consequences** — what each option changes, risks, or blocks. +4. **Evidence refs** — task docs, notes, reports, diffs, or gate ids the architect can verify. + +Then stop acting on that item until the architect returns a `messageKind: decision-ruling` row (or +a clarification request). Do not open a second developer item while the first is unresolved. + +Operational hand-offs that stay inside the backend still use the existing durable gate and inbox +surfaces. Carve-out (ruled 2026-07-06): in an orchestrated run, leaf→master and master→super +integrations ride the series' **standing approval** — no per-edge architect/developer hand-off; the +developer review concentrates at the super PR/carry-over gate through the architect. The table's +integration row governs when a hand-off DOES happen (solo runs; a raised durable gate): | Junction | Parked durable gate `kind` | Hands off via | | --- | --- | --- | -| design acceptance / plan gate | `plan-approval` | this lifecycle | +| design acceptance / plan gate | `plan-approval` | architect decision item | | worktree intent | `worktree-intent` | `c-09-git-worktree-manager` | | commit / closeout | `closeout-approval` | `c-12-closeout` | -| push | `push-approval` | this lifecycle / `c-09` | +| push | `push-approval` | architect decision item / `c-09` | | integration | `integration-approval` | `c-09` / `c-12` | | cleanup / finalization | `cleanup-approval` | `c-09` / `c-12` | -| any other dev-wait | `agent-question` | this lifecycle | +| any other developer-worthy wait | `agent-question` | architect decision item | `closeout-approval` **is** the commit hand-off. The block-and-wait `lifecycle_gate` + `lifecycle_resume` pair remains the parked fallback for a durable, mutation-blocking approval -record; it renders a prompt over your prose, which is exactly why notify-and-stop is the path. +record; when developer attention is needed, the architect is the relay that presents it. -## Job D — Design (pull the designer hat) +## Design Boundary — Ask The Architect -**Entry:** an intent/problem with no task doc — or a planning-status doc that needs reshaping -before work starts. Fires at the front of the pipeline AND mid-flight; most leaves of a live -series are designed mid-flight. +The orchestrator does not own the developer drawing board and does not pull the designer hat. +When an intent/problem has no task doc, or a planning-status doc needs developer-visible +reshaping, emit a decision/design item to the architect with the missing decision, options, +consequences, and evidence refs. The architect wears `roles/designer.md`, discusses with the +developer, and returns a durable ruling or updated task surface. -Run `roles/designer.md` **inline — the designer is a hat, not a seat**: it cannot sit in a -coordination leaf because the task is what it exists to create. No worktree, no branch, no spawn -required; a heavy design may run the same hat in a separate session (chair logistics, not a role -distinction — spawn with `AR_SPAWN_ROLE=designer`). - -- The co-think loop, evidence model, blast-radius-within-the-master, and designer-limits - declaration are the hat's own file. The orchestrator remains accountable for what the hat - produces: **bulwark-check the design against the portfolio and the past before acceptance** - (planned-vs-planned AND planned-vs-past — a designed change that collides with another master's - standing order is caught here or shipped broken). -- **Output:** master/leaf task docs (requirements · steps · code examples), `openQuestions` for - the developer (the rendered decision surface; `notes/` carries the analysis), the limits note. -- **Gate:** the developer accepts the design — or parks it. **No git surface.** +The orchestrator remains accountable for backend portfolio integrity after the architect returns +the design: run the bulwark check against the portfolio and the past before dispatch. ## Job P — Portfolio (streamline + plan) -**Entry:** designed masters exist and coherence/order is the question, or the developer says +**Entry:** designed masters exist and coherence/order is the question, or the architect dispatches "orchestrate these." - **Route-coherence scan** across the set (route indexes · onboarding · grepai · cgc); fan-out @@ -140,25 +179,31 @@ distinction — spawn with `AR_SPAWN_ROLE=designer`). the decision log; numbers freeze when the series lands on main. - **Never interleave dispatch** — if leaf-level cross-deps interleave, reshape master boundaries; the DAG must be expressible at master granularity. -- **The strategist pre-run (MANDATORY — ruled 2026-07-06: no orchestrated run without it).** After - 1..N masters are designed and BEFORE implementation starts on any of them, dispatch the +- **The strategist pre-run (BY DEVELOPER APPROVAL ONLY — ruled 2026-07-09, superseding the + 2026-07-06 "mandatory" rule).** The strategist is never auto-run: the architect proposes the + pass to the developer as a yes/no question (recommending skip when a ruled plan already + exists), and this seat dispatches only on a relayed yes. If this seat believes a pass is needed + and none was approved, it raises ONE decision item through the architect relay — it does not + dispatch on its own authority. When approved: after 1..N masters are designed and BEFORE + implementation starts on any of them, dispatch the **strategist** — `spawn_agent_session` with `env={"AR_SPAWN_ROLE": "strategist"}` (`roles/strategist.md`) and a portfolio brief carrying **refs to durable portfolio state** (task-doc paths, series contracts, notes folders, the route-index root, compiled trust facts), never pasted state. Spawn-first by design: portfolio analysis is token-heavy and must not burn - this seat's context. **Even a single master gets the pass.** The strategist runs its + this seat's context. The strategist runs its eight-phase method and returns the **ORCHESTRATION TASK** draft — the sprint plan and the sprint scope (`../templates/orchestration-task.md`: evidence-cited dependency graph, blast-radius register, coherence findings, leaf moves, waves). This is the portfolio three-party loop (owner = this seat · builder = strategist · reviewer with - `../criteria/plan-review.md`), followed by **drawing-board rounds with the developer** — this - seat relays, multi-round convergence is expected and normal, and quo-vadis items (e.g. two - masters heavily disagreeing) go straight to the developer. On acceptance **this seat adopts the + `../criteria/plan-review.md`), followed by **drawing-board rounds through the architect** — this + seat relays by decision item, multi-round convergence is expected and normal, and quo-vadis + items (e.g. two masters heavily disagreeing) go straight to the architect relay. On acceptance + **this seat adopts the draft into durable task form** (the strategist is a reader, not a mutator) with a decision-log entry. -- **Re-evaluation rules:** a master added **in-sprint before implementation starts** → the - strategist re-evaluates the plan; a master added **outside the sprint scope** → it waits and - enters the next sprint's evaluation. +- **Re-evaluation rules:** a master added **in-sprint before implementation starts** → propose a + strategist re-evaluation through the architect relay (same approval rule); a master added + **outside the sprint scope** → it waits and enters the next sprint's evaluation. - **Output: the planner master task + the adopted orchestration task** — the run's durable home: subTasks = the coordination leaves (orchestrator seat first, one per manager); body = the DAG + dispatch order + conflict decisions + (once Job O starts) the super branch name; decision log = @@ -167,33 +212,54 @@ distinction — spawn with `AR_SPAWN_ROLE=designer`). task doc carrying a top-level `orchestrates` list naming the master tasks it commands — the dashboard derives the orchestration > master > leaf hierarchy (and the rank insignia) from that field, so setting it is part of adoption. -- **Gate:** the portfolio plan gate — one wholesale developer review of the reshaped portfolio + - the orchestration task (sprint scope + DAG + dispatch order). **No git surface** — not even the - super branch exists yet. +- **Gate:** the portfolio plan gate — one wholesale architect/developer review of the reshaped + portfolio + the orchestration task (sprint scope + DAG + dispatch order). **No git surface** — + not even the super branch exists yet. ## Job O — Orchestrate (execute the plan) -**Entry:** an approved planner master — or a single approved master for a flat run. Either way, -**the adopted orchestration task must exist**: the strategist pre-run (Job P) is the -unconditional precondition for any orchestrated run — even one master. It is doctrine, not a -knob. +**Entry:** an approved planner master — or a single approved master dispatched for backend +execution. Either way, **the adopted orchestration task must exist**. When the developer approved +the propose-first strategist pass, this seat adopts Job P's accepted draft. When the developer +sanctioned a strategist skip, this seat authors and adopts the orchestration task from the +developer-ruled plan, recording that source and adoption in the decision log. A skipped Job P +therefore never blocks Job O. **First act — the super-branch intent:** create the super integration branch off `main` so masters can base off it. **A branch, not a worktree** — this seat has nothing to build at creation time. (Interim: until a branch-without-worktree primitive lands, the manual git + contract edge is acceptable and recorded in durable notes.) -**Dispatch loop**, dependency-ordered — for each ready master (dependencies integrated into -super): `spawn_agent_session(manager)` with a brief compiled from +**Dispatch loop**, dependency-ordered — the dependency graph, not habit, decides sequencing. +Dispatch independent ready masters in parallel by default up to +`orchestration.concurrency.maxParallelMasters`. Sequential execution is the exception and must +name a gate, a shared-file one-writer dependency, or an explicit ruling. For each ready master +(dependencies integrated into super), call `spawn_agent_session(manager)` with a brief compiled from `../templates/manager-brief.md` (`env={"AR_SPAWN_ROLE": "manager"}`, the **qualified** leaf key -`//`; the brief carries the load-bearing base fact: master branches -off the **current super**, never off main); -monitor turn-report artifacts, nudges, escalation intake; apply the **spirit test** to escalated +`//`; together the environment role and qualified leaf claim the +manager's `(leaf, role)` seat; the brief carries the load-bearing base fact: master branches off +the **current super**, never off main); +process and ack the pending signals the L2 supervisor sweep wakes you with — turn-report +artifacts, nudges, escalation intake — before ending your turn; you never watch for these yourself +(**watcher ban, uniform-mechanism ruling 2026-07-07:** the supervisor sweep is the one mechanism, +no seat-local polling/monitoring, own duty inverts to processing what lands, not hunting for it). +Then apply the **spirit test** — a model-judgment duty, not a watching one — to escalated deltas. A manager escalation may carry a **loop's full round history** (3-round cap hit, or a round that failed to shrink the finding set — the convergence rule, `../SKILL.md` The Three-Party Loop): this seat either re-runs the loop at ITS level (the orchestrator-level agent set — the -strongest models) or, when the blocker is a quo-vadis truth, takes it to the developer. In a -**flat run, wear the manager hat yourself** (see The Hat-Collapse Rule). +strongest models) or, when the blocker is a quo-vadis truth, emits a decision item to the +architect. This spawned backend seat does not run flat hat-collapse (see The Hat-Collapse Rule). + +**Delegated series authority:** after the developer accepts the orchestration plan, this seat owns +subordinate execution without repeated developer formality. Managers may close out and integrate +their leaves; this seat may decide manager handovers, close out direct work when it wears the +manager/worker hat, finalize/cleanup subordinate edges, and integrate completed masters into the +super branch under the accepted-series authority. Run the preview/checks and record the authority +source in the intent note or decision log; do not stop merely because the next operation creates a +commit, advances a lifecycle, cleans up a spent worktree, or fast-forwards a subordinate branch. +Stop for the developer only when the work reaches the final completed super branch / PR-carryover +gate, a human-pinned gate is actually raised, the plan meaning changes, checks remain red outside +scope, or a quo-vadis truth is in play. **Failed-deliverable rule (reopen-and-reshape):** a leaf whose deliverable came out wrong is **REOPENED under its own id** (`task_reopen`) and its doc reshaped to the intended form — the @@ -212,7 +278,7 @@ policy may require the attached reviewer verdict (`requireReviewerVerdictAtSeams enforces it: `worktree_integrate` refuses while a `master-handover-approval` gate addressed to this master (its `enclosure`) is undecided or policy-invalid. A blocking verdict decomposes into fix leaves dispatched before integration; a -handover you cannot honestly decide escalates to the developer. +handover you cannot honestly decide escalates to the architect as a decision item. **Integration duty (master → super) — the worktree moment.** Per completed master: @@ -226,6 +292,16 @@ handover you cannot honestly decide escalates to the developer. 4. Carry memory + map the ledger (C-11; duplicate memory single-sided; memory quality before the memory edge lands). 5. Record the new super tips in durable notes; mark next masters ready. +6. **Land the completed master's spent seats** — + `lifecycle_finalize_task` auto-lands the master's manager + any master-level reviewer seats into + the dashboard's landed/archive group (config-gated, default ON) the moment the master finalizes + into super. Their transcripts remain inspectable and non-active; use the landed archive cleanup + button when those rows should be closed. You hold the **only** portfolio-wide retire authority + for exceptional stuck/abandoned/duplicate seats: unlike a manager (scoped to its own master's + worker/reviewer seats), you may retire ANY seat in the portfolio, including a completed manager — + `session_retire(actor_session_id=, session_id=, reason=...)`. + Owner-never-self-retires still holds (you can never retire your own seat). Use + this by hand for a stuck/abandoned seat the automation missed; transcripts are never deleted. **The topology (single home — this section owns it):** @@ -243,7 +319,8 @@ Strict stack: super off main; master branches off the **current super** (never o branches off their master. **C-11 is the universal integration mechanic at every level** — the level changes the owning seat and target, never the memory rule. The final super → main landing follows `system/git-workflow.md`: PR to gated main, remote merge, memory carry-over so the ledger -maps the actual merge commit, then push — **push only after the developer approves**. +maps the actual merge commit, then push — **push only after the architect returns the developer's +approval**. **Conflict resolution — exactly two modes:** *Up-front (preferred):* an overlap found during streamlining → extract shared logic into a foundation master implemented first (leaf moves + @@ -255,47 +332,38 @@ owns the final truth; ledger edge mapped once). parallel-master reconcile (T9), the series-branch-without-worktree primitive, and atomic move/renumber — run manually with existing primitives, each manual edge recorded in durable notes. -**Super exit & landing tail — the developer's SINGLE review point (ruled 2026-07-06, resolves +**Super exit & landing tail — the architect-mediated SINGLE review point (ruled 2026-07-06, resolves L8-Q9):** all leaf→master and master→super integrations are **orchestrator-delegated** — on the happy path they proceed under the series' standing approval (the developer's portfolio-gate approval, recorded in the planner master's decision log); a durable `integration-approval` gate, when one is raised, still awaits the developer — the kind stays human-pinned as-built. The -developer reviews ONCE, at the **fully integrated super branch on the PR/carry-over gate**. When +architect presents the developer review ONCE, at the **fully integrated super branch on the +PR/carry-over gate**. When the DAG drains, spawn the super-exit adversarial reviewer (`roles/reviewer.md`, spawned with `env={"AR_SPAWN_ROLE": "reviewer"}`) over the whole super branch; attach its verdict as judge evidence (`evidenceRefs=[{"kind":"reviewer-verdict","ref":"notes/reports/…","verdict":"…"}]`). -The handover to the developer **MUST offer a REVIEWABLE ENVIRONMENT** — for agents-remember: the -dashboard running on the super branch — because the review is **visible-behavior-first** (a +The handover to the architect **MUST offer a REVIEWABLE ENVIRONMENT** — for agents-remember: the +dashboard running on the super branch — because the developer review is **visible-behavior-first** (a broken visual pass fails the handover fast, before anyone reads a diff), code review second. The handover carries **demo notes — "what changed visibly"**: per master, the user-visible behavior -to walk (panels, flows, outputs, how to reach them), so the developer drives the environment +to walk (panels, flows, outputs, how to reach them), so the developer can drive the environment without archaeology. Rejections decompose into fix leaves. On approval: PR + memory carry-over + -push (developer-gated), then finalization +push (architect-mediated developer gate), then finalization (`lifecycle_finalize_task` per edge — statuses via the tool, steps checked by hand), then the **self-improvement close**: proposals for future runs grounded in the run's own ledger ("did x/y/z; hit a/b/c; a and b solved on the spot; c needs this change") — proposals only, never automated self-modification. `lifecycle_end` records the terminal state. -## The Hat-Collapse Rule (solo and flat runs) - -Solo work is **not a fourth route** — it is the same three jobs collapsed: - -- **Design** still happens (however briefly): the task doc exists before anything else. -- **Delegated gates collapse back to the developer when one chair owns both sides** — a gate you - raised from this session's lifecycle cannot be decided by it (owner-never-self-approves). -- **Portfolio** collapses but does not vanish: an ORCHESTRATED run — anything that dispatches - seats, even for a single master — still requires the strategist pre-run (even one master gets - the pass). Only session-scale hands-on work (nothing dispatched; not an orchestrated run) skips - the strategist; the owner's own bulwark check remains. -- **Orchestrate** runs with hats collapsed: in a **flat series** the orchestrator wears the - **manager hat** (`roles/manager.md` duties — dispatch, review, delegated gates, leaf closeout → - integrate → finalize — same duties, same artifacts, one chair). At **session scale** it builds - **hands-on** instead of spawning (when spawn economics don't pay): the build discipline is the - worker's (edit + same-pass `c-05` onboarding + `system/tools.md` checks green + freshness watch - / early `worktree_sync`), the closeout tail is the owner's (see `c-12-closeout`), and - the ladder holds identically: task doc → intent → worktree → build → close. -- Fan-out sub-agents may read/search and **write durable reports**; **every AR state mutation - stays in this seat's main loop** (see Sub-Agent Fan-Out below). +## The Hat-Collapse Rule (spawned backend) + +Hat-collapse is reserved for the owner/developer-facing architect. This spawned backend +orchestrator never wears the architect, designer, manager, worker, strategist, or reviewer hat in +place. + +If a run is small enough for one owner seat, the architect may perform these backend duties under +`roles/architect.md`. If this orchestrator needs another role, it spawns a new role chat +horizontally. Fan-out sub-agents may read/search and **write durable reports**; **every AR state +mutation stays in this seat's main loop** (see Sub-Agent Fan-Out below). ## Sub-Agent Fan-Out (capability doctrine — any harness that has it) @@ -322,7 +390,7 @@ regardless of the engine underneath. ## The Spirit Test — This Seat Only -**Within the spirit** of what the developer accepted → act alone + a decision-log entry (leaf +**Within the spirit** of what the architect/developer accepted → act alone + a decision-log entry (leaf moves and renumbers on planning-status masters, inserted fix leaves, reopened-and-reshaped leaves, mid-series convergence — the integration branch is the safety net). **Against the spirit** → raise it for a joint decision. Only this seat holds the global view to judge a collision; the @@ -339,28 +407,34 @@ task, fill small blanks, escalate real deltas). - **Sub-agent durable reports** (`../templates/impact-analysis.md`, `../templates/onboarding-coherency.md`); sub-agents never call `task_doc`, gates, `spawn_agent_session`, or closeout. -- **The adopted orchestration task** (the strategist drafts; this seat adopts — with the adoption - decision-log entry) before any orchestrated run. +- **The adopted orchestration task** (the strategist drafts when approved; on a sanctioned skip, + this seat authors it from the developer-ruled plan; either way this seat adopts it with the + adoption decision-log entry) before any orchestrated run. - **The super-exit demo notes** ("what changed visibly", per master) + the reviewable environment - offer — the developer handover is visible-behavior-first. + offer — the architect-mediated developer handover is visible-behavior-first. - **The self-improvement report** at close. ## Comms Protocol - **Inbox** (`operator_inbox_post` / `_poll` / `_consume`) — dispatch orders down, escalation intake up; durable + dashboard-visible. -- **Stdin push** — delivery into hosted sessions (echo-confirmed paste); poll is the non-hosted - fallback. -- **Escalation** — this seat is the last resolver before the developer: resolve within the +- **Stdin push** — the L2 supervisor's injector (HFX2-L3, the one standard wake mechanism) delivers + into hosted sessions (echo-confirmed paste) on the sweep's own tick; the inbox is the non-hosted + equivalent, never a hand-rolled poll of this seat's own. +- **Idle is safe** — silence is supervised (the L2 sweep + L4 escalation ladder), so + `lifecycle_turn_end_notification` / ending a turn with nothing pending is the correct move, not a + risk to be covered by watching. **Watcher ban (uniform-mechanism ruling 2026-07-07):** never + build a seat-local watcher of any kind. +- **Escalation** — this seat is the last backend resolver before the architect: resolve within the bird's-eye view first; what goes up is decided by the **quo-vadis test**, not by being stumped — a **high-blast-radius truth** question (answered wrong it means big rewrites later: architecture direction, security posture, doctrine contradictions, irreversible data/branch operations, where - agent settings live) goes to the developer IMMEDIATELY via task-doc `openQuestions`, regardless - of any loop's round count; presentation-grade choices (2px vs 3px) never go up — rule and log. + agent settings live) goes to the architect IMMEDIATELY as a decision item, regardless of any + loop's round count; presentation-grade choices (2px vs 3px) never go up — rule and log. A loop that hits its 3-round cap or stops converging arrives here with its full round history; - re-run it at this level's agent set or take the quo-vadis part to the developer. Developer - rejections arrive here and decompose into fix leaves (or reopens — see the failed-deliverable - rule). + re-run it at this level's agent set or take the quo-vadis part to the architect. Architect or + developer rejections arrive here and decompose into fix leaves (or reopens — see the + failed-deliverable rule). ## Knobs @@ -372,6 +446,6 @@ task, fill small blanks, escalate real deltas). | launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | | sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | | promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste (settings-only; never validated) | -| tools | full bird's-eye + orchestration | route indexes · onboarding · `grepai_search` · `cgc_*` · `read_ar_files` · `task_doc` · gates · `spawn_agent_session` · worktree/C-11 | +| tools | full bird's-eye + orchestration | route indexes · onboarding · `grepai_search` · `cgc_*` · `read_ar_files` · `task_doc` · gates · `spawn_agent_session` · `session_retire` (any seat, portfolio-wide) · worktree/C-11 | Settings.json `orchestration.roles.orchestrator` overrides these, and `orchestration.rolesPerLevel..orchestrator` overrides per dispatch level (role-file defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.pi/skills/l-01-agent-lifecycles/roles/reviewer.md b/.pi/skills/l-01-agent-lifecycles/roles/reviewer.md index a477ee87..fbeab40d 100644 --- a/.pi/skills/l-01-agent-lifecycles/roles/reviewer.md +++ b/.pi/skills/l-01-agent-lifecycles/roles/reviewer.md @@ -13,7 +13,7 @@ reviewer seat (below)** (seams: developer decision 2026-07-03; loop reuse: rulin 1. **Master-exit** — before a **manager** hands its completed master integration branch to the **orchestrator**. 2. **Super-exit** — before the **orchestrator** hands the accumulated super integration branch to the - **developer**. + **architect** for the developer review. Leaf-level review is the manager's own duty — **not** an adversarial seam. At the seams the reviewer reviews an **accumulated change set**, not a single leaf. @@ -30,11 +30,20 @@ loop's 3-round cap** — your delta-verify closes a round, it does not open one. > **Verdicts are evidence, not decisions.** The reviewer never decides a gate. Its verdict attaches to > the handover gate as **judge evidence**; the gate's decider decides — the **orchestrator** at -> master-exit (delegated `master-handover-approval`), the **developer** at super-exit — per the -> gate delegation policy (settings `orchestration.gateDelegation`, `controlplane/gate_policy.py`). +> master-exit (delegated `master-handover-approval`), the **architect carrying the developer +> ruling** at super-exit — per the gate delegation policy (settings `orchestration.gateDelegation`, +> `controlplane/gate_policy.py`). > The policy binds delegated seam decisions to verdict evidence when > `requireReviewerVerdictAtSeams` is set. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays reviewer for its lifetime. A pasted brief for another +role is refused and reported to the seam's decider via inbox instead of rerouting this chat. Roles +expand horizontally into new chats; sub-agents drill vertically inside this reviewer seat for the +three review lenses. A reviewer never absorbs architect, orchestrator, strategist, manager, or +worker work. + ## Lens - **Opening move:** scope the review — the integration branch diff, the relevant task docs @@ -101,10 +110,11 @@ orchestrator. Review the **accumulated master change set**, not a final leaf in master. Each fix leaf names scope, target files/docs, evidence, and done-when. A master-exit block without fix leaves is invalid. -### SUPER-EXIT — Orchestrator Before Developer Handover +### SUPER-EXIT — Orchestrator Before Architect/Developer Handover The orchestrator spawns this reviewer before handing the accumulated super integration branch to the -developer. Review **wholesale branch behavior**: the whole portfolio as integrated on super. +architect for the developer review. Review **wholesale branch behavior**: the whole portfolio as +integrated on super. - **Scope packet:** super integration branch diff against its base (main), portfolio task docs, master task docs, master-handover packets, prior master-exit verdicts, orchestrator decision logs, resolved diff --git a/.pi/skills/l-01-agent-lifecycles/roles/strategist.md b/.pi/skills/l-01-agent-lifecycles/roles/strategist.md index 377e4b70..e4ae81d6 100644 --- a/.pi/skills/l-01-agent-lifecycles/roles/strategist.md +++ b/.pi/skills/l-01-agent-lifecycles/roles/strategist.md @@ -3,8 +3,8 @@ > The sprint planner, **spawn-first** and self-contained: read the whole in-flight portfolio, prove > it coherent, resolve the dependency chains, establish blast radius, shuffle leaves for the best > implementation order, and deliver the **orchestration task** — the sprint plan and the sprint -> scope. A strategist run is a **mandatory precondition for any orchestrated run**. Your **brief is -> your session start**. +> scope. This seat runs only when the developer approves the architect's propose-first strategist +> question; when dispatched, your **brief is your session start**. > > Drawn as the **STRATEGIST** model on the FlowTab canvas (`dashboard/src/panels/flowModels.ts`). @@ -13,8 +13,8 @@ **Spawn-first by design** (developer decision 2026-07-05). Strategist work is token-heavy — it reasons over every master's state, task docs, notes, friction ledger, and gate history — so it runs as its own process with its own harness/model/effort knobs, protecting the orchestrator's context. -The designer precedent explicitly does NOT apply: the designer stays an inline hat because design -is drawing-board-interactive with the developer; the strategist's essence is solitary heavy +The designer precedent explicitly does NOT apply: the designer stays an inline architect hat +because design is drawing-board-interactive; the strategist's essence is solitary heavy analysis. Spawned by the orchestrator via `spawn_agent_session` with `env={"AR_SPAWN_ROLE": "strategist"}`. @@ -36,6 +36,14 @@ it into durable task form. The strategist never edits task docs, never raises ga git. A seat that never touches mutating AR tools never instantiates a lifecycle — that is the designed shape. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays strategist for its lifetime. A pasted brief for +another role is refused and escalated to the orchestrator via inbox instead of rerouting this chat. +Roles expand horizontally into new chats; sub-agents drill vertically inside this strategist seat +for portfolio analysis. A strategist never absorbs architect, orchestrator, manager, reviewer, or +worker work. + ## Lens - **Opening move:** read the brief fully — it carries **refs to durable portfolio state, never @@ -90,7 +98,7 @@ everything else. `roles/manager.md`): the strategist's analysis directly parameterizes the loops. 6. **Coherence & contradiction check** — cross-master sweep: two masters moving one surface in opposite directions, a leaf assuming state another leaf removes, duplicate work, vocabulary - drift. **Directional contradictions are quo-vadis → developer** (via the drawing board; see + drift. **Directional contradictions are quo-vadis → architect** (via the drawing board; see Duties §5). 7. **Ordering** — topological sort over ORDER edges; CONFLICT edges resolved by serialization or **leaf moves (recorded from→to with rationale)**; independent sets become **parallel waves** @@ -134,17 +142,17 @@ mutate nothing yourself. ### 5 — Drawing-board rounds The reviewer (plan-review catalog) passes judgment on the plan; the orchestrator relays the -verdict and the developer's drawing-board feedback back into this session. **Convergence over +verdict and the architect's drawing-board feedback back into this session. **Convergence over rounds is expected and normal** — large, messy portfolios are explicitly NOT expected to be fixed in one shot; the iteration is the feature. Each round must shrink the finding set (the convergence -rule); the loop's hard cap is 3 full rounds, and **the drawing board with the developer IS this +rule); the loop's hard cap is 3 full rounds, and **the drawing board through the architect IS this loop's escalation**. Quo-vadis items — high-blast-radius truths such as two masters heavily -disagreeing on direction — go **straight to the developer** at the drawing board (the orchestrator -carries them; you flag them, unmistakably, at the top of the coherence findings). +disagreeing on direction — go **straight to the architect relay** at the drawing board (the +orchestrator carries them; you flag them, unmistakably, at the top of the coherence findings). ### 6 — Adopted-plan handover -When the developer accepts the plan, the orchestrator adopts it; your seat's work is done. **The +When the architect returns the accepted plan ruling, the orchestrator adopts it; your seat's work is done. **The artifact write is unconditional; the inbox is the delivery channel when the brief wires it** — otherwise your final playback message to the orchestrator carries the artifact ref. Then end. The orchestration task remains the sprint's standing scope: a new master added **in-sprint before implementation starts** re-opens re-evaluation (you @@ -166,8 +174,8 @@ and enters the next sprint's evaluation. dashboard-visible. - **Stdin push** — the orchestrator delivers round feedback into this hosted session; your replies are inbox rows or artifact revisions — never an untracked side channel. -- **Escalation** — to the **orchestrator**, which relays; quo-vadis truths are flagged for the - developer's drawing board. You never edit task docs to reflect a ruling — the orchestrator does. +- **Escalation** — to the **orchestrator**, which relays to the architect; quo-vadis truths are + flagged for the drawing board. You never edit task docs to reflect a ruling — the orchestrator does. ## Tool Surface (positive statement — this is all of it) diff --git a/.pi/skills/l-01-agent-lifecycles/roles/system-specialist.md b/.pi/skills/l-01-agent-lifecycles/roles/system-specialist.md new file mode 100644 index 00000000..54422560 --- /dev/null +++ b/.pi/skills/l-01-agent-lifecycles/roles/system-specialist.md @@ -0,0 +1,102 @@ +# Lifecycle — System Specialist + +> One provider-degradation investigation, one report before any fix. The system specialist is a +> backend operations seat spawned by the orchestrator after a `degradation-alert`; it does not +> replace the orchestrator's portfolio attention. + +## What This Seat Is + +The system specialist investigates provider-only degradation events: provider metrics, provider +current-state files, provider logs, Docker/container state through the existing provider tools, and +the durable degradation event that caused the alert. This iteration is provider-only. Sentry or a +future system monitor may replace or feed the detector later, but the response protocol remains: +detect -> report -> explicit orchestrator order -> fix or stop providers. + +This seat is **investigate-first**. It writes a durable report under the active master's +`notes/reports/` folder (or the orchestrator-designated reports folder when there is no active +master) before attempting any fix. It fixes only after the orchestrator explicitly orders a +specific remediation based on that report. + +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays system-specialist for its lifetime. A pasted brief for +another role is refused and escalated to the orchestrator via inbox. This seat never absorbs +orchestrator, manager, worker, curator, reviewer, strategist, designer, or architect work. + +## Intake + +Read the orchestrator brief and the degradation event first. Required inputs: + +- Degradation event id and event payload or event-log path. +- Current provider metrics/state paths. +- Provider logs or diagnostics paths. +- Report path. +- Whether this is investigation-only or an explicit fix order. + +If the brief lacks the event or report path, ask the orchestrator for one clarification via inbox +and stop. + +## Investigation Report + +Write the report before any fix order is executed. Use this shape: + +```md +# System-Specialist Report — + +## Event +- State transition: +- Affected stacks: +- Critical failsafe already ran: yes | no | n/a + +## Findings +- + +## Root Cause Hypothesis +- + +## Fixable In Session +- Verdict: yes | no | uncertain +- Reason: + +## Recommended Action +- + +## Boundaries +- Provider-only scope honored: yes +- No AR task/memory state mutated beyond this report: yes +``` + +## Fix Mode + +Only after an explicit orchestrator order: + +- Apply the ordered provider remediation with existing provider/runtime tools. +- Do not edit AR task docs, lifecycle state, memory onboarding, ledgers, or code. +- Do not start providers if the order is only to investigate or if managers are paused by a + degradation-alert. +- If the issue is not fixable in-session, report that and recommend `provider_watchers stop`. + +The orchestrator owns the final decision: fixable-in-session -> order a targeted fix; not fixable +-> stop providers before they can take the system down. + +## Comms + +- **Inbox** — receive the orchestrator order, return report/fix completion or escalation. +- **Escalation** — system-specialist -> orchestrator. Never go straight to the architect or + developer. + +## Knobs + +| Knob | Default | Notes | +| ------- | ------- | ----- | +| harness | claude | operational investigation benefits from strong tool/session ergonomics | +| model | fable | system diagnosis and report synthesis | +| effort | high | degradation triage is high-impact | +| launchArgs | — | free-form escape: verbatim harness argv (settings-only; never validated, recorded in spawn provenance) | +| sessionCommands | — | free-form escape: lines pasted + submitted into the fresh session before the brief (settings-only; never validated) | +| promptKeywords | — | free-form escape: prepended as the first line of the dispatch brief paste | +| tools | provider diagnostics + native reads + inbox | provider_status · provider_diagnostics · provider_watchers when explicitly ordered · logs/metrics reads · inbox | + +Settings.json `orchestration.roles.system-specialist` overrides these, and +`orchestration.rolesPerLevel..system-specialist` overrides per dispatch level (role-file +defaults < settings < level override; spawn knobs manual: `docs/reference/harnesses.md`). diff --git a/.pi/skills/l-01-agent-lifecycles/roles/worker.md b/.pi/skills/l-01-agent-lifecycles/roles/worker.md index f5c369b4..aa29e836 100644 --- a/.pi/skills/l-01-agent-lifecycles/roles/worker.md +++ b/.pi/skills/l-01-agent-lifecycles/roles/worker.md @@ -7,7 +7,7 @@ ## What This Seat Is **One per task leaf, short-lived, fresh session.** Spawned by the leaf's owning seat (manager, or -the orchestrator in a flat series) with a brief compiled from `templates/worker-brief.md`. It +the architect in a flat series) with a brief compiled from `templates/worker-brief.md`. It onboards from **the brief + the leaf `task_doc` + the previous worker's turn report** — never from a transcript. Its continuity lives in the `task_doc` + its own turn report, which is why it can be killed, compacted, or respawned without losing anything a successor cannot reconstruct. @@ -16,20 +16,30 @@ The worker builds; it does not manage lifecycle machinery. **Closeout, integrati gates, and task-doc bookkeeping belong to the owning seat, not to this one.** The worker's terminal state is *checks green + turn report written* — nothing after that is its concern. +## Role-Seat Immutability + +In dashboard-owned sessions, this seat stays worker for its lifetime. A pasted brief for another +role is refused and escalated to the owning seat via inbox instead of rerouting this chat. Roles +expand horizontally into new chats; sub-agents drill vertically inside this worker seat for +read/search only. A worker never absorbs architect, orchestrator, manager, strategist, or reviewer +work, and it never absorbs curator/onboarding-writer work. + ## The Worker Loop ``` -brief -> orient -> build (edit + onboarding same-pass) -> checks green -> turn report -> end +brief -> orient -> build code -> checks green -> turn report -> curator memory pass by separate seat | +-- blocked or plan delta beyond blank-filling -> escalate to the owning seat ``` ### 1 — Intake -Read the brief fully, then the leaf spec / `task_doc` it names. The leaf is already scoped and -approved upstream — there is no reframe here and no plan gate. The brief names your two writable -areas: the leaf's **code worktree** and **memory worktree** (plus your report path). You edit -nothing outside them. +On a developer-declared takeover, first run `../SKILL.md`'s Developer-Declared Task-Seat Takeover +checklist so the dashboard chat is attached to this leaf. Then read the brief fully, then the leaf +spec / `task_doc` it names. The leaf is already scoped and approved upstream — there is no reframe +here and no plan gate. The brief names your two writable areas: the leaf's **code worktree** and +your report path. The memory worktree is context for the curator pass unless the brief explicitly +says otherwise. You edit nothing outside your named surfaces. ### 2 — Orient (paired reads before edits) @@ -44,12 +54,10 @@ nothing outside them. - Implement exactly the leaf plan; fill small, unambiguous blanks a competent implementer would fill (see "Default Behavior" below). -- **Refresh the matching onboarding in the same editing pass** per - `c-05-create-or-update-onboarding-files`: a changed source file's sidecar **body** is updated now; - a new file's sidecar is created; route overviews that need a genuine body update get one, and a - no-impact route gets the literal history form `- — No route impact: `. - Regenerate generated route indexes with a **local `build_route_indexes(...)`** invocation from the - memory worktree. +- Produce the builder input the downstream curator needs: changed paths, code-diff summary, tests, + and any route/onboarding observations that would help the memory pass. The curator, not the + worker, writes onboarding in the official manager -> builder -> reviewer -> curator closeout + chain. - **Never `git commit`.** Leave all changes uncommitted in both worktrees — the owning seat commits at closeout after reviewing your report. @@ -63,14 +71,17 @@ the report. A red check you cannot fix inside the leaf's scope is an escalation, Write `templates/turn-report.md` to the path the brief names (convention: `notes/reports/-worker-report.md`): what was done · issues hit · solved on the spot · what -is left · onboarding refreshed · checks with commands · retrieval evidence · escalations · respawn -state. **A missing report gets nudged.** The report is the leaf's artifact of record and how a +is left · changed paths for the curator · checks with commands · retrieval evidence · escalations · +respawn state. The report is the leaf's builder artifact of record and how a respawned successor onboards — write it even when blocked (with the Escalations section filled), -then end your turn. +then end your turn. **A missing report gets nudged by the supervisor sweep (HFX2-L2), never by a +seat-local watcher** — no owning seat, and no worker, hand-rolls its own polling loop over this +artifact; ending your turn once the report is written is safe, not a risk you have to cover for. ## Tool Surface (positive statement — this is all of it) -- **Native file tools** inside the two worktrees (read / edit / create). +- **Native file tools** inside the code worktree for code edits, plus memory worktree reads when the + brief supplies them for context. - **Read-only AR retrieval:** `read_ar_files`, `grepai_search`, `cgc_*`, `context_packet`. - **Shell** for the prescribed checks (use the interpreter paths the brief names — do not assume a `python` shim exists). @@ -85,17 +96,17 @@ lifecycle machinery never instantiates a lifecycle; that is the designed shape, When the harness offers sub-agents, use them for **read/search only**, scoped to the leaf (locate call sites, sweep onboarding): each writes durable notes and returns a compact summary. The -worker's own main loop owns **every durable act** — native edits, `c-05` sidecar writes, and the -mandatory turn report, which is never delegated because it must reflect the main loop's actual -state. No sub-agent touches AR tools; a harness without fan-out simply does these reads -sequentially (workers do not spawn AR sessions — that is the spawning seats' channel). +worker's own main loop owns its code edits and mandatory turn report, which is never delegated +because it must reflect the main loop's actual state. The curator owns onboarding writes. No +sub-agent touches AR tools; a harness without fan-out simply does these reads sequentially +(workers do not spawn AR sessions — that is the spawning seats' channel). ## Loop Position (when the leaf runs as a three-party loop) The owning seat scores each leaf into a tier at dispatch (loop doctrine: `../SKILL.md`, The Three-Party Loop). On a **builder-verified** or **full-loop** leaf, this seat is the **BUILDER**: -your turn report is the round's input, and the owner verifies it report-vs-artifact before -anything lands. Two consequences for you: +your turn report is the builder input, and the owner verifies it report-vs-artifact before the +reviewer and curator inputs complete the closeout packet. Two consequences for you: - **Fix rounds resume THIS session** — the same builder, with its context intact. Your round-2+ report **appends** to your report file rather than rewriting it, so the loop history stays @@ -108,18 +119,25 @@ anything lands. Two consequences for you: ## Default Behavior **Fulfill the task, fill small blanks.** No creative-liberty prompting in either direction. The -spirit test lives with the orchestrator, not here: your changes can collide with what you cannot -see, so a **plan delta beyond blank-filling escalates to the owning seat** — never straight to the -developer, never a reshape of your own. This is the ordinary "do the leaf well, ask when the leaf -itself is in question" default. +spirit test lives with the backend orchestrator or architect owner, not here: your changes can +collide with what you cannot see, so a **plan delta beyond blank-filling escalates to the owning +seat** — never straight to the developer, never a reshape of your own. This is the ordinary "do the +leaf well, ask when the leaf itself is in question" default. ## Comms - **Inbox** — receive dispatch/context; post escalations; agent-to-agent rows carry role metadata and a `messageKind` (`turn-report`, `nudge`, `escalation`, …), durable + dashboard-visible. -- **Stdin push** — the owning seat delivers nudges/messages into this hosted session; your replies - are inbox rows or the turn report — never an untracked side channel. -- **Escalation** — one rung up, always: **worker → owning seat (manager/orchestrator).** +- **Stdin push** — the L2 supervisor sweep's injector (HFX2-L3) delivers nudges/messages into this + hosted session on its own mechanical tick, in the owning seat's name — never the owning seat (or + you) watching/polling by hand. Your replies are inbox rows or the turn report — never an untracked + side channel. +- **Idle is safe** — once your turn report is written, ending your turn is correct; silence is + supervised (HFX2-L2 sweep + HFX2-L4 escalation ladder), not a gap you must cover by lingering or + self-nudging. **Watcher ban (uniform-mechanism ruling 2026-07-07):** never hand-roll your own + watcher — one mechanism, no per-seat variance. +- **Escalation** — one rung up, always: **worker → owning seat (manager/orchestrator/architect in + solo flat mode).** ## Knobs diff --git a/.pi/skills/l-01-agent-lifecycles/templates/curator-brief.md b/.pi/skills/l-01-agent-lifecycles/templates/curator-brief.md new file mode 100644 index 00000000..8217ce34 --- /dev/null +++ b/.pi/skills/l-01-agent-lifecycles/templates/curator-brief.md @@ -0,0 +1,84 @@ +# Template — Curator Brief + +The dispatch packet the **manager** (or the architect in a flat series) compiles for a **curator**, +spawned fresh per leaf after builder code exists and the reviewer verdict is available. **The brief +is the curator's entire session start** — it replaces the front half the spawner already ran. This +is the change-set feeding contract: the curator never infers a change set from transcript memory, +it is FED the landed change set, the leaf task doc, and notes/ as inputs. + +Spawn with `env={"AR_SPAWN_ROLE": "curator"}` and the **qualified** leaf key +`//`; together they claim the curator's `(leaf, role)` seat while the +session-start router and dashboard leaf rail engage. + +--- + +```md +ROLE BRIEF — curator + +# CURATOR BRIEF — · + +You are the CURATOR for leaf `` of master `` (repo: ). Your lifecycle is +`skills/l-01-agent-lifecycles/roles/curator.md`; this brief is your session start. Write the leaf's +memory pass from the inputs below, then stop. + +## Worktrees +- Code: `` (branch ``, base ``) — read-only for you. +- Memory: `` (branch ``) — your only write surface. + +## The landed change set (fed, not inferred) +- Code diff: `..` in the code worktree — . +- Memory diff (if any pre-existing memory-worktree changes carry forward): `..`. +- Counters: `` from the change-set the manager attached — + do not re-derive this from your own guess at "what probably changed." + +## Task inputs +- Leaf task doc: `` (read it first — objective, requirements, decision log). +- notes/: `` — the builder turn report + (`notes/reports/-worker-report.md`), the reviewer verdict when this leaf ran a loop, and + any other task-local notes naming a factual current-state clarification. + +## Routing rule (mgmt-L4 design — apply this before writing anything) +Route each piece of the change set and each notes/ item to the RIGHT onboarding home: +1. A concrete source file's own sidecar, when the change is about that file's behavior. +2. The nearest governing route-local overview, when the change is about route/package shape or + crosses several files in one route. +3. The repo entity catalog, only for a real load-bearing cross-layer entity change. +4. The L3 Operational-Notes target is LAST RESORT ONLY — use it when a finding is real but has no + file, route, or entity home; never as the default drop point for convenience. +Overview-dumping (writing everything into the nearest overview because it is easiest) is rejected. + +## Tool surface +- Native reads in the code worktree; native reads/edits in the memory worktree. +- `c-05-create-or-update-onboarding-files` skill workflows for sidecars and entity catalogs. +- Local `route_index_refresh`-equivalent (`build_route_indexes(...)`) from the memory worktree. +- Inbox for one clarification row back to if the fed change set is missing or + ambiguous — never invent a change set from memory. +- No `worktree_*`, `lifecycle_*`, `task_doc`, `gate_*`, `memory_quality_check`-mutating tools beyond + what your role file names, no code edits. + +## Checks (before you report) +- `git diff --check` in the memory worktree. +- Any onboarding/reference checks the brief or your role file names. + +## Memory-pass report (mandatory, last act) +Write `/-curator-report.md`: changed onboarding files (with which +change-set item or notes/ item each one routes to and why), route index results, reference checks, +blockers, and the exact commands run. This report — together with the builder's code and the +reviewer's verdict — is exactly the manager's three closeout inputs. +``` + +--- + +**Compiler notes for the manager.** + +- Fill every ``; a brief with an unresolved placeholder is not dispatchable. +- Pull the change-set counters/paths from the leaf's actual landed range (the leaf contract's + recorded base commit through the builder's current HEAD/worktree state) — do not hand the curator + a stale or guessed diff. +- Attach the builder turn report and (when the leaf ran a loop) the reviewer verdict as the notes/ + inputs; the curator does not re-request evidence that already exists in `notes/reports/`. +- Deliver as an echo-confirmed paste; only count delivery on a post-boot echo. +- This brief runs strictly AFTER builder code exists and the reviewer verdict (when the leaf tier + requires one) is available — never before, and never in place of either. diff --git a/.pi/skills/l-01-agent-lifecycles/templates/manager-brief.md b/.pi/skills/l-01-agent-lifecycles/templates/manager-brief.md index 10c37922..0d611d1a 100644 --- a/.pi/skills/l-01-agent-lifecycles/templates/manager-brief.md +++ b/.pi/skills/l-01-agent-lifecycles/templates/manager-brief.md @@ -3,7 +3,8 @@ The dispatch packet the orchestrator compiles for a manager taking one master. Like the worker brief, **this brief is the manager's entire session start** — it replaces the front half the orchestrator already ran. Spawn with `env={"AR_SPAWN_ROLE": "manager"}` and the **qualified** leaf -key of the master's coordination leaf (`//`). +key of the master's coordination leaf (`//`); together they claim the +manager's `(leaf, role)` seat. --- @@ -30,8 +31,25 @@ master's leaf loop to the master-exit seam, then hand over. ## Dispatch defaults - Worker spawns: `templates/worker-brief.md`, `env={"AR_SPAWN_ROLE": "worker"}`, qualified leaf - keys; knob overrides: . + keys; the environment role and qualified leaf together claim each worker's `(leaf, role)` seat; + knob overrides: . +- Leaf closeout chain: manager -> builder -> reviewer -> curator. The manager closes a leaf from + builder code + reviewer verdict + curator memory pass — never before the curator pass exists. +- Curator spawns: `../templates/curator-brief.md`, `env={"AR_SPAWN_ROLE": "curator"}`, fresh per + leaf with the qualified leaf key, so the environment role and qualified leaf claim the + curator's `(leaf, role)` seat; dispatch only after builder code and the reviewer verdict are + available. The brief FEEDS the landed change set (leaf contract's base-to-head range) + the leaf + task doc + notes/ — the curator routes each to the right onboarding home (specific sidecar or + governing overview; L3 Operational-Notes last-resort only) and writes onboarding only. - Concurrency: . +- Provider degradation: on `messageKind="degradation-alert"`, do not start provider setup, + provider watchers, watcher restarts, or `retry_provider_setup` until an all-clear. Managers have + no provider kill authority; provider stops and fixes route through the orchestrator and + system-specialist. +- Cleanup: `worktree_integrate` auto-lands a completed leaf's worker/reviewer seats into the + dashboard landed/archive group (config-gated, default ON). Use the landed archive cleanup button + for archived rows; use `session_retire` only for a stuck/abandoned worker/reviewer/curator seat of + YOUR OWN master — server policy refuses any other target. ## The exit - When all leaves have landed on your branch: spawn the master-exit reviewer diff --git a/.pi/skills/l-01-agent-lifecycles/templates/orchestration-task.md b/.pi/skills/l-01-agent-lifecycles/templates/orchestration-task.md index c3d4bb08..c1559030 100644 --- a/.pi/skills/l-01-agent-lifecycles/templates/orchestration-task.md +++ b/.pi/skills/l-01-agent-lifecycles/templates/orchestration-task.md @@ -1,10 +1,12 @@ # Orchestration-Task Template The artifact a **strategist** drafts for the **orchestrator** (`roles/strategist.md`) — the sprint -plan and the sprint scope. A strategist run producing this artifact is a **mandatory precondition -for any orchestrated run**; the orchestrator adopts the accepted draft into durable task form (the -strategist is a reader, not a mutator). It is written under the series/coordination `notes/` path -the brief names and revised across drawing-board rounds. +plan and the sprint scope. The architect proposes the strategist pass, and the strategist drafts +this artifact only after developer approval; the orchestrator adopts the accepted draft into +durable task form (the strategist is a reader, not a mutator). When the developer sanctions a +strategist skip, `roles/orchestrator.md` owns the alternate author-and-adopt path. It is written +under the series/coordination `notes/` path the brief names and revised across drawing-board +rounds. ## Rules diff --git a/.pi/skills/l-01-agent-lifecycles/templates/turn-report.md b/.pi/skills/l-01-agent-lifecycles/templates/turn-report.md index b771ada4..597f40fd 100644 --- a/.pi/skills/l-01-agent-lifecycles/templates/turn-report.md +++ b/.pi/skills/l-01-agent-lifecycles/templates/turn-report.md @@ -2,7 +2,8 @@ The **mandatory** artifact a worker writes at **every** hand-off (`roles/worker.md`). It is how the leaf's work survives the session's death and how a respawned successor onboards from **state, not the -transcript**. A missing turn report is nudged by the manager. +transcript**. A missing turn report is nudged by the HFX2-L2 supervisor sweep, never by a +manager watching for it (uniform-mechanism ruling 2026-07-07). ## Rules diff --git a/.pi/skills/l-01-agent-lifecycles/templates/worker-brief.md b/.pi/skills/l-01-agent-lifecycles/templates/worker-brief.md index db8f44d0..c91e9779 100644 --- a/.pi/skills/l-01-agent-lifecycles/templates/worker-brief.md +++ b/.pi/skills/l-01-agent-lifecycles/templates/worker-brief.md @@ -7,7 +7,8 @@ absorbed a series of real dispatch frictions (route-index leaks, attestation for keying, missing `python` shim), so deviate knowingly or not at all. Spawn with `env={"AR_SPAWN_ROLE": "worker"}` and the **qualified** leaf key -`//` so the session-start router and the dashboard leaf rail both engage. +`//`; together they claim the worker's `(leaf, role)` seat while the +session-start router and dashboard leaf rail engage. --- @@ -18,13 +19,14 @@ ROLE BRIEF — worker You are a WORKER for leaf `` of master `` (repo: ). Your lifecycle is `skills/l-01-agent-lifecycles/roles/worker.md`; this brief is your session start. Execute the leaf -completely, write your turn report, then stop. +code completely, write your builder turn report, then stop. Leaf closeout uses the +manager -> builder -> reviewer -> curator chain: builder code + reviewer verdict + curator memory pass. -## Worktrees (your ONLY writable areas) +## Worktrees (your code write area + memory context) - Code: `` (branch ``, base ``) -- Memory: `` +- Memory: `` (read/context for changed-path notes; the curator writes onboarding) - Plus your turn report at the path below. Nothing else. NEVER `git commit` — the owning seat - closes out after reviewing your report. + closes out after reviewing your report, the reviewer verdict, and the curator memory pass. ## Tool surface - Native file tools inside the two worktrees; shell for the checks below. @@ -46,18 +48,18 @@ files involved, the invariants that must hold, what NOT to touch.> - Full: — must exit 0. - `git diff --check` in both worktrees. -## Onboarding (same editing pass, per c-05) -- Changed source files: update the sidecar BODY now; new files: create the sidecar. -- Route overviews: genuine body update where routes changed; otherwise the newest history entry - uses the LITERAL form `- — No route impact: ` (timestamp first). -- Pin idiom for verification metadata: "Verification metadata pinned until closeout stamps the - commit." +## Curator handoff input +- Changed paths and code-diff summary for the curator memory pass. +- Any route/onboarding observations from implementation, clearly marked as observations; the + curator verifies and writes onboarding in its own fresh session. +- Pin idiom for any metadata note the curator needs: "Verification metadata pinned until closeout + stamps the commit." ## Turn report (mandatory, last act) Write `/-worker-report.md` following `skills/l-01-agent-lifecycles/templates/turn-report.md` — including exact check commands + -outcomes, the retrieval-evidence tally, and the respawn state. If blocked: fill Escalations and -stop — escalate to , never to the developer. +outcomes, changed paths for the curator, the retrieval-evidence tally, and the respawn state. If +blocked: fill Escalations and stop — escalate to , never to the developer. ``` --- diff --git a/.pi/skills/w-02-light-task-workflow/master-template.md b/.pi/skills/w-02-light-task-workflow/master-template.md index 42738c16..a807afd1 100644 --- a/.pi/skills/w-02-light-task-workflow/master-template.md +++ b/.pi/skills/w-02-light-task-workflow/master-template.md @@ -7,7 +7,7 @@ built to grow as the work unfolds. ## When to escalate to a series -The `l-01-agent-lifecycles` orchestrator lifecycle's `decide` step escalates a single task to a series once its size is apparent — the +The `l-01-agent-lifecycles` architect lifecycle's `decide` step escalates a single task to a series once its size is apparent — the implementation plan no longer fits on a single page, or the work splits into distinct slices that each deserve their own checklist and commit. You can also start single and escalate later: drop in the master `task.md` and move the existing plan into the first `NN_.md`. diff --git a/AGENTS.md b/AGENTS.md index 62e05208..2bd02586 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -19,8 +19,8 @@ Sessions route by role through the `l-01-agent-lifecycles` skill — the lifecyc roof this checkout routes into. A **spawned agent** (the `AR_SPAWN_ROLE` env var is set, or the first message is a role brief) follows its brief — the brief is its session start, and the rest of this section is not addressed to it. A -**developer-facing session** is the **orchestrator**: it runs -`skills/l-01-agent-lifecycles/roles/orchestrator.md`, whose phase axis is +**developer-facing session** is the **architect**: it runs +`skills/l-01-agent-lifecycles/roles/architect.md`, whose phase axis is request → trust-checkpoint → reframe-research → decide → build → close. Classify the job (bug / feature / triage / research) as a *lens* during reframe-research — a hint, re-pickable, never a gate. @@ -36,13 +36,13 @@ code change lives under an approved task doc: master + light sub-task series when it outgrows a single-page plan. The task-collaboration doctrine in `tasks/AGENTS.md` applies inside the -orchestrator lifecycle's reframe-research phase, in plain chat, before any task +architect lifecycle's reframe-research phase, in plain chat, before any task file or format is chosen. --- **IMPORTANT:** -Do not change code or documentation without entering the orchestrator lifecycle and clearing its plan gate. +Do not change code or documentation without entering the architect lifecycle and clearing its plan gate. Do not change task plan items without approval. Think before acting. Do not randomly commit. Use the `c-12-closeout` skill instead! @@ -145,3 +145,6 @@ branch, and local command guidance. Use `system/coding-guidelines.md` when present for repo-specific coding rules. Use `system/code-quality-report-template.md` as a template for reporting code quality results after implementation work changes source code. +Before adding or editing any store, loop-over-a-store, queue, or append-only log, the +`system/coding-guidelines.md` "Stability, Bounded Resources, and Reclamation" section is +MUST-READ doctrine. diff --git a/README.md b/README.md index 3718dd54..ce5531ed 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ That is the normal first-run path. `skills_install()` remains available as a maintenance/manual MCP tool, but the starter packages already provide the initial skills and harness files. -After that, normal work runs through the `l-01-agent-lifecycles` skill: a developer-facing session is the orchestrator; spawned agents follow their role briefs. The agent resolves the active context with `c-08-ar-coordination-context-resolver`, checks memory quality with `c-02-memory-quality-control`, reads relevant onboarding beside code, and updates onboarding after approved changes. +After that, normal work runs through the `l-01-agent-lifecycles` skill: a developer-facing session is the architect; spawned backend orchestrators and other role seats follow their role briefs. The agent resolves the active context with `c-08-ar-coordination-context-resolver`, checks memory quality with `c-02-memory-quality-control`, reads relevant onboarding beside code, and updates onboarding after approved changes. ## Run The Dashboard @@ -162,8 +162,8 @@ upgrade is picked up by the next session ([Settings Reference](docs/reference/settings-json.md)). Pinning a version is the debugging/repro path, not the default: `uv tool -install 'agents-remember-mcp==3.0.0rc3'`, or one-shot without installing, -`uvx --from 'agents-remember-mcp==3.0.0rc3' agents-remember dashboard`. +install 'agents-remember-mcp==3.0.0rc4'`, or one-shot without installing, +`uvx --from 'agents-remember-mcp==3.0.0rc4' agents-remember dashboard`. > **Pre-release note (until 3.0.0 final):** the dashboard currently ships in > `3.0.0rcN` pre-releases, which default version resolution skips. Install with @@ -239,7 +239,7 @@ ar-coordination/ ## Status -Agents Remember is at `3.0.0rc3` and actively developed. The core path — by-path onboarding, drift checks, and approval-gated updates — is in real use and stable enough to rely on. The public contracts listed under [Stability](#stability) are held stable across minor releases and change only on a major bump; the internals beneath them and the optional semantic/relationship providers may still evolve, so pin a version and read the notes for your target version in [GitHub Releases](https://github.com/Foxfire1st/agents-remember/releases) — the repository's canonical changelog — before upgrading. The Claude Code path is the most exercised; other harnesses are supported but less battle-tested. +Agents Remember is at `3.0.0rc4` and actively developed. The core path — by-path onboarding, drift checks, and approval-gated updates — is in real use and stable enough to rely on. The public contracts listed under [Stability](#stability) are held stable across minor releases and change only on a major bump; the internals beneath them and the optional semantic/relationship providers may still evolve, so pin a version and read the notes for your target version in [GitHub Releases](https://github.com/Foxfire1st/agents-remember/releases) — the repository's canonical changelog — before upgrading. The Claude Code path is the most exercised; other harnesses are supported but less battle-tested. The 3.0 arc: the working session itself is now observable and steerable — a system-managed agent lifecycle with durable approval gates and an event/projection layer, served as the mission-control browser cockpit directly from the MCP package (`agents-remember dashboard`; [#2](https://github.com/Foxfire1st/agents-remember/issues/2), [#43](https://github.com/Foxfire1st/agents-remember/issues/43)). The `rc` tag means the cockpit surface is still settling toward the final 3.0.0 contract; the architecture beneath it is the one described above. diff --git a/agents-md-files/coordinator/AGENTS.md b/agents-md-files/coordinator/AGENTS.md index f0f280ca..84a50e2e 100644 --- a/agents-md-files/coordinator/AGENTS.md +++ b/agents-md-files/coordinator/AGENTS.md @@ -5,18 +5,23 @@ Sessions route by role through the `l-01-agent-lifecycles` skill. A **spawned agent** (the `AR_SPAWN_ROLE` env var is set, or the first message is a role brief) follows its brief — the brief is its session start, and the rest of this -section is not addressed to it. A **developer-facing session** is the -**orchestrator**: it enters `skills/l-01-agent-lifecycles/roles/orchestrator.md` -before working in any managed repository. +section is not addressed to it. A **developer-facing session** is the **free +chat** — a launcher, not a role seat (ruled 2026-07-09): it answers +research-only questions inline, and for any role-shaped work it spawns the +**architect** into its own chat with the settings-owned profile +(`spawn_agent_session`, `AR_SPAWN_ROLE=architect`) instead of assuming the +role itself. The spawned architect enters +`skills/l-01-agent-lifecycles/roles/architect.md` and owns the developer +conversation from there. During an already-running session, the agent must stay aware of managed-repo boundaries. If a new turn or tool target may cross from outside Agents Remember -scope into a managed repository, enter the orchestrator lifecycle first. +scope into a managed repository, enter the architect lifecycle first. --- **IMPORTANT:** -Do not change code without entering the orchestrator lifecycle and clearing its plan gate. +Do not change code without entering the architect lifecycle and clearing its plan gate. Do not change task plan items without approval. Do not randomly commit. Use the `c-12-closeout` skill instead! @@ -31,7 +36,7 @@ This workspace uses a layered memory system. Make sure to read the below rules b This coordinator file is the workspace entrypoint. Read these installed `AGENTS.md` files when their scope becomes relevant: -- `tasks/AGENTS.md` — task collaboration doctrine (applied up front in the `l-01-agent-lifecycles` orchestrator lifecycle's +- `tasks/AGENTS.md` — task collaboration doctrine (applied up front in the `l-01-agent-lifecycles` architect lifecycle's reframe-research phase; see _Start Here — Route By Role_ above). ### Onboarding Documentation diff --git a/dashboard/src/cockpit/Cockpit.tsx b/dashboard/src/cockpit/Cockpit.tsx index f8142007..c8760591 100644 --- a/dashboard/src/cockpit/Cockpit.tsx +++ b/dashboard/src/cockpit/Cockpit.tsx @@ -590,6 +590,35 @@ function ServingBuildStamp() { ); } +// 260707-HFX2-L2 R5: "the last turtle is the developer's glance" -- the supervisor sweep's own +// heartbeat tick, red past its staleness cutoff. `lastTickAt: null` means the supervisor has +// never ticked in this workspace (dashboard/supervisor autostart is opt-in), which is NOT the +// same as stale -- the badge renders nothing for it rather than a false alarm. +function SupervisorHeartbeatBadge() { + const heartbeat = useDashboard((s) => s.supervisorHeartbeat); + if (!heartbeat || heartbeat.lastTickAt === null) return null; + const ageMinutes = heartbeat.ageSeconds !== null ? heartbeat.ageSeconds / 60 : null; + const label = + ageMinutes !== null + ? `supervisor ${heartbeat.stale ? "stale" : "ok"} ${ageMinutes.toFixed(1)}m` + : "supervisor stale"; + const duration = + heartbeat.lastSweepDurationSeconds !== null + ? `${heartbeat.lastSweepDurationSeconds.toFixed(2)}s` + : "n/a"; + const backlog = `${heartbeat.redeliverableInboxCount}/${heartbeat.pendingInboxCount}`; + return ( + + {heartbeat.stale ? "⚠ " : ""} + {label} · inbox {backlog} · sweep {duration} + + ); +} + function TopBar() { const conn = useDashboard((s) => s.conn); const metrics = useDashboard((s) => s.metrics); @@ -611,6 +640,7 @@ function TopBar() { ) : null} {generatedAt ? @ {generatedAt.slice(11, 19)} : null} + diff --git a/dashboard/src/data/sessionGroups.test.ts b/dashboard/src/data/sessionGroups.test.ts index b8f40964..3b1d54aa 100644 --- a/dashboard/src/data/sessionGroups.test.ts +++ b/dashboard/src/data/sessionGroups.test.ts @@ -91,7 +91,8 @@ describe("groupSessions (L14 G1 command tree)", () => { it("groups command-role provenance onto the deck together with the orchestration-claiming chat", () => { const grouped = groupSessions({ sessions: [ - session({ id: "dev", leafKey: "agents-remember/sprint-02/SPRINT-02" }), + session({ id: "architect", leafKey: "agents-remember/sprint-02/SPRINT-02" }), + session({ id: "orch", spawnRole: "orchestrator" }), session({ id: "strat", spawnRole: "strategist" }), session({ id: "mgr", spawnRole: "manager", leafKey: "agents-remember/260706_management-repo/260706" }), session({ id: "worker", spawnRole: "worker", leafKey: "agents-remember/260706_management-repo/260706-L1" }), @@ -100,19 +101,16 @@ describe("groupSessions (L14 G1 command tree)", () => { enclosures: [LIVE_ENCLOSURE], }); - const deck = grouped.groups.find((group) => group.kind === "command"); - expect(deck).toBeTruthy(); + const deck = grouped.groups.find((group) => group.key === "sprint:agents-remember/sprint-02"); expect(deck?.label).toBe("SPRINT 02 · management-repo rollout · command deck"); expect(deck?.tier).toBe("orchestration"); - // The developer-facing orchestrator chat (the orchestration task's own leaf claim) sits on the - // deck with the spawned command seats; the worker does NOT (role provenance is the gate). - expect(deck?.sessions.map((member) => member.id)).toEqual(["dev", "strat", "mgr"]); - expect(deck?.countLabel).toBe("3 chats · 3 live"); - - const master = grouped.groups.find((group) => group.key === "master:260706_management-repo"); - expect(master?.sessions.map((member) => member.id)).toEqual(["worker"]); - expect(master?.tier).toBe("management"); // commanded by the sprint → purple badge - expect(master?.nested).toBe(true); // + one indent step + expect(deck?.sessions.map((member) => member.id)).toEqual(["architect"]); + + const master = grouped.groups.find((group) => group.key === "sprint:agents-remember/260706_management-repo"); + expect(master?.sessions.map((member) => member.id)).toEqual(["mgr", "worker"]); + expect(master?.tier).toBe("orchestration"); + expect(master?.nested).toBe(true); + expect(grouped.ungrouped.map((member) => member.id)).toEqual(["orch", "strat"]); }); it("groups by leaf claim under the master, unmarked and un-nested when no orchestration commands it", () => { @@ -125,13 +123,31 @@ describe("groupSessions (L14 G1 command tree)", () => { }); expect(grouped.groups).toHaveLength(1); const master = grouped.groups[0]; - expect(master.key).toBe("master:260707_settings-page"); + expect(master.key).toBe("sprint:agents-remember/260707_settings-page"); expect(master.label).toBe("260707 settings page"); expect(master.tier).toBeUndefined(); expect(master.nested).toBe(false); expect(grouped.ungrouped).toEqual([]); }); + it("uses the current binding role rather than stale spawn provenance for command styling", () => { + const grouped = groupSessions({ + sessions: [ + session({ + id: "moved-manager", + spawnRole: "worker", + seatRole: "manager", + leafKey: "agents-remember/260706_management-repo/260706-L1", + }), + ], + taskDocuments: [SPRINT, COMMANDED_MASTER], + enclosures: [LIVE_ENCLOSURE], + }); + + expect(grouped.groups[0]?.kind).toBe("command"); + expect(grouped.groups[0]?.tier).toBe("orchestration"); + }); + it("keeps command-role sessions out of the deck when NO orchestration task exists (D3 flat run)", () => { const grouped = groupSessions({ sessions: [ @@ -145,7 +161,7 @@ describe("groupSessions (L14 G1 command tree)", () => { expect(grouped.ungrouped.map((member) => member.id)).toEqual(["mgr", "plain"]); }); - it("rolls sessions on landed or absent enclosures into one archive group, collapsed by default", () => { + it("rolls only landed rows into the archive group", () => { const landedEnclosure = enclosure({ enclosure: "/contracts/landed", leafId: "260706-l0", @@ -155,33 +171,37 @@ describe("groupSessions (L14 G1 command tree)", () => { }); const grouped = groupSessions({ sessions: [ - session({ id: "landed-chat", leafKey: "agents-remember/260706_management-repo/260706-L0", status: "exited" }), - session({ id: "absent-chat", leafKey: "agents-remember/260799_unknown/260799-L9" }), + session({ id: "landed-chat", leafKey: "agents-remember/260706_management-repo/260706-L0", status: "landed" }), + session({ id: "legacy-exited", leafKey: "agents-remember/260799_unknown/260799-L9", status: "exited" }), + session({ id: "active-absent", leafKey: "agents-remember/260799_unknown/260799-L10" }), ], taskDocuments: [COMMANDED_MASTER], enclosures: [landedEnclosure], }); - expect(grouped.groups).toHaveLength(1); - const archive = grouped.groups[0]; - expect(archive.kind).toBe("landed"); - expect(archive.tier).toBeUndefined(); // unmarked - expect(archive.defaultCollapsed).toBe(true); - expect(archive.sessions.map((member) => member.id)).toEqual(["landed-chat", "absent-chat"]); - expect(archive.countLabel).toBe("2 chats · archived"); + expect(grouped.groups).toHaveLength(2); + const archive = grouped.groups.find((group) => group.kind === "landed"); + expect(archive).toBeTruthy(); + expect(archive?.label).toBe("landed archive"); + expect(archive?.tier).toBeUndefined(); // unmarked + expect(archive?.defaultCollapsed).toBe(true); + expect(archive?.sessions.map((member) => member.id)).toEqual(["landed-chat"]); + expect(archive?.countLabel).toBe("1 chat · archived"); + expect(grouped.groups.find((group) => group.key === "sprint:agents-remember/260799_unknown")?.sessions.map((member) => member.id)) + .toEqual(["legacy-exited", "active-absent"]); }); - it("matches enclosure leaf ids case-insensitively (doc ids are uppercase, enclosure ids slugified)", () => { + it("groups a qualified leaf without consulting enclosure casing or liveness", () => { const grouped = groupSessions({ sessions: [session({ id: "w", leafKey: "agents-remember/260706_management-repo/260706-L1" })], taskDocuments: [COMMANDED_MASTER], enclosures: [LIVE_ENCLOSURE], // leafId "260706-l1" }); - expect(grouped.groups[0]?.key).toBe("master:260706_management-repo"); + expect(grouped.groups[0]?.key).toBe("sprint:agents-remember/260706_management-repo"); }); it("reads at a glance at 30-chat scale: deck + per-master groups + one archive", () => { const sessions: OpenSession[] = [ - session({ id: "orch", spawnRole: "orchestrator" }), + session({ id: "architect", spawnRole: "architect" }), session({ id: "strategist", spawnRole: "strategist", status: "exited" }), session({ id: "m1", spawnRole: "manager" }), session({ id: "m2", spawnRole: "manager", status: "exited" }), @@ -208,7 +228,7 @@ describe("groupSessions (L14 G1 command tree)", () => { } for (let i = 0; i < 13; i += 1) { sessions.push( - session({ id: `old${i}`, leafKey: `agents-remember/260650_landed/260650-L${i}`, status: "exited" }), + session({ id: `old${i}`, leafKey: `agents-remember/260650_landed/260650-L${i}`, status: "landed" }), ); } expect(sessions).toHaveLength(30); @@ -224,20 +244,53 @@ describe("groupSessions (L14 G1 command tree)", () => { }); expect(grouped.groups.map((group) => group.key)).toEqual([ - "command", - "master:260706_management-repo", - "master:260707_settings-page", + "sprint:agents-remember/260706_management-repo", + "sprint:agents-remember/260707_settings-page", "landed", ]); - const [deck, masterA, masterB, archive] = grouped.groups; - expect(deck.sessions).toHaveLength(4); - expect(deck.countLabel).toBe("4 chats · 2 live"); + const [masterA, masterB, archive] = grouped.groups; expect(masterA.sessions).toHaveLength(6); expect(masterA.countLabel).toBe("6 chats · 2 live"); expect(masterB.sessions).toHaveLength(7); expect(masterB.countLabel).toBe("7 chats · 1 live"); expect(archive.sessions).toHaveLength(13); expect(archive.countLabel).toBe("13 chats · archived"); + expect(grouped.ungrouped.map((member) => member.id)).toEqual(["architect", "strategist", "m1", "m2"]); + }); + + it("keeps identical master folders isolated by repository and groups without a live enclosure", () => { + const deviceDoc = doc({ + id: "device-leaf", + repository: "device-management", + title: "device sprint", + docPath: "/tasks/device-management/260706_management-repo/task.json", + }); + const grouped = groupSessions({ + sessions: [ + session({ id: "ar", leafKey: "agents-remember/260706_management-repo/ar-leaf" }), + session({ id: "device", leafKey: "device-management/260706_management-repo/device-leaf" }), + ], + taskDocuments: [SPRINT, COMMANDED_MASTER, deviceDoc], + enclosures: [], + }); + expect(grouped.groups.map((group) => group.key)).toEqual([ + "sprint:device-management/260706_management-repo", + "sprint:agents-remember/260706_management-repo", + ]); + expect(grouped.groups.map((group) => group.sessions[0]?.id)).toEqual(["device", "ar"]); + expect(grouped.groups[0]?.nested).toBe(false); + expect(grouped.groups[1]?.nested).toBe(true); + expect(grouped.ungrouped).toEqual([]); + }); + + it("surfaces malformed leaf claims instead of silently rendering an orphan row", () => { + const grouped = groupSessions({ + sessions: [session({ id: "broken", leafKey: "260706_management-repo" })], + taskDocuments: [COMMANDED_MASTER], + enclosures: [], + }); + expect(grouped.groups[0]?.kind).toBe("error"); + expect(grouped.groups[0]?.sessions.map((member) => member.id)).toEqual(["broken"]); expect(grouped.ungrouped).toEqual([]); }); }); diff --git a/dashboard/src/data/sessionGroups.ts b/dashboard/src/data/sessionGroups.ts index e8c52239..6deb1122 100644 --- a/dashboard/src/data/sessionGroups.ts +++ b/dashboard/src/data/sessionGroups.ts @@ -1,35 +1,18 @@ import type { EnclosureNode, TaskDocNode } from "../types/projection"; -import { hasLiveWorktree } from "./selectors"; -import type { OpenSession } from "./sessions"; -import { - isOrchestrationDoc, - masterCommandNames, - orchestratorParentKey, - pathDir, -} from "./taskHierarchy"; +import { sessionSeatRole, type OpenSession } from "./sessions"; +import { isOrchestrationDoc, pathDir } from "./taskHierarchy"; import { qualifiedLeafKey } from "./taskIdentity"; -// The G1 COMMAND TREE for the Chats pane (L14): the chats sidebar mirrors operations. Membership, -// in precedence order, per session: -// 1. COMMAND DECK (gold, top; exists ONLY when an orchestration task exists — D3): sessions with -// command-role spawn provenance (AR_SPAWN_ROLE orchestrator/strategist/manager recorded on the -// catalog row) plus any session whose leaf claim resolves INTO the orchestration task itself -// (its own qualified leaf, or a leaf in its folder) — the developer-facing orchestrator chat. -// 2. MASTER GROUPS (one per claimed master folder): sessions whose qualified leaf key -// (`repo/master/leaf-id`) resolves to a live enclosure of that master; the group takes the -// purple management badge + one 22px indent step only when that master is commanded by an -// orchestration task — mirroring the tasks-tab grammar exactly. -// 3. LANDED ARCHIVE (one group, collapsed by default, unmarked): sessions attached to a leaf -// whose enclosure is gone or absent — landed work rolls up instead of cluttering the rail. -// 4. UNGROUPED: sessions with no leaf claim keep today's flat placement below the groups. -// Pure derivation — no persistence; collapse state is UI-local in the SessionList. +// L16 derives one rail box per repo-qualified sprint. Sessions with a valid leaf claim stay in +// that sprint regardless of worktree liveness; landed sessions use the existing archive, while +// malformed claims use an explicit error box. Pure derivation; collapse state remains UI-local. /** The l-01 seats whose chats belong on the command deck. */ -const COMMAND_ROLES = new Set(["orchestrator", "strategist", "manager"]); +const COMMAND_ROLES = new Set(["architect", "orchestrator", "strategist", "manager"]); export interface SessionGroup { - key: string; // "command" | `master:${folder}` | "landed" - kind: "command" | "master" | "landed"; + key: string; // `sprint:${repo}/${master}` | "landed" | "error" + kind: "command" | "master" | "landed" | "error"; label: string; /** Rank insignia tier for the group header; unset = unmarked (landed / uncommanded master). */ tier?: "orchestration" | "management"; @@ -71,22 +54,6 @@ function leafKeySegments( return { repo: parts[0], master: parts[1], leafId: parts[parts.length - 1] }; } -// The enclosure a leaf claim resolves to: master folder = basename(taskRoot), leaf ids compared -// case-insensitively (enclosure leaf ids are slugified lowercase, doc ids authored uppercase — -// the same normalization the tasks tab uses), repo checked when the enclosure carries one. -function enclosureForLeafKey( - leafKey: string, - enclosures: EnclosureNode[], -): EnclosureNode | undefined { - const segments = leafKeySegments(leafKey); - if (!segments) return undefined; - return enclosures.find((enclosure) => { - if (folderOf(enclosure.taskRoot) !== segments.master) return false; - if (enclosure.repoName && enclosure.repoName !== segments.repo) return false; - return enclosure.leafId.toLowerCase() === segments.leafId.toLowerCase(); - }); -} - export function groupSessions(input: { sessions: OpenSession[]; taskDocuments: TaskDocNode[]; @@ -95,95 +62,99 @@ export function groupSessions(input: { const docs = input.taskDocuments; const orchestrationDocs = docs.filter(isOrchestrationDoc); const deckExists = orchestrationDocs.length > 0; - const orchestrationFolders = new Set(orchestrationDocs.map((doc) => folderOf(pathDir(doc.docPath)))); - const orchestrationLeafKeys = new Set( + const orchestrationKeys = new Set( orchestrationDocs.map((doc) => qualifiedLeafKey(doc)).filter(Boolean), ); - const masterDocsByFolder = new Map(); + const masterDocsBySprint = new Map(); for (const doc of docs) { if (doc.kind !== "master") continue; const folder = folderOf(pathDir(doc.docPath)); - if (folder && !masterDocsByFolder.has(folder)) masterDocsByFolder.set(folder, doc); + const key = folder && doc.repository ? `${doc.repository}/${folder}` : undefined; + if (key && !masterDocsBySprint.has(key)) masterDocsBySprint.set(key, doc); } - const deck: OpenSession[] = []; const landed: OpenSession[] = []; const ungrouped: OpenSession[] = []; - const byMaster = new Map(); + const invalid: OpenSession[] = []; + const bySprint = new Map(); for (const session of input.sessions) { + if (session.status === "landed") { + landed.push(session); + continue; + } const segments = session.leafKey ? leafKeySegments(session.leafKey) : undefined; - const claimsOrchestration = - (session.leafKey !== undefined && orchestrationLeafKeys.has(session.leafKey)) || - (segments !== undefined && orchestrationFolders.has(segments.master)); - if ( - deckExists && - ((session.spawnRole !== undefined && COMMAND_ROLES.has(session.spawnRole)) || - claimsOrchestration) - ) { - deck.push(session); + if (session.leafKey && !segments) { + invalid.push(session); continue; } if (!segments) { ungrouped.push(session); continue; } - const enclosure = enclosureForLeafKey(session.leafKey as string, input.enclosures); - if (enclosure && hasLiveWorktree(enclosure)) { - const members = byMaster.get(segments.master); - if (members) members.push(session); - else byMaster.set(segments.master, [session]); - } else { - // Landed or absent enclosure: the work left the hangar, the chat rolls into the archive. - landed.push(session); - } + const key = `${segments.repo}/${segments.master}`; + const members = bySprint.get(key); + if (members) members.push(session); + else bySprint.set(key, [session]); } const groups: SessionGroup[] = []; - if (deck.length > 0) { - const sprint = orchestrationDocs[0]; - groups.push({ - key: "command", - kind: "command", - label: sprint?.title ? `${sprint.title} · command deck` : "command deck", - tier: "orchestration", - nested: false, - defaultCollapsed: false, - sessions: deck, - countLabel: countLabel(deck, "command"), + const sprintGroups = [...bySprint.entries()].map(([sprintKey, members]) => { + const [repo, folder] = sprintKey.split("/"); + const masterDoc = masterDocsBySprint.get(sprintKey); + // `orchestrates` contains bare folder names, so it is safe only when the declaring + // orchestration document belongs to this same repository. Never let a same-named folder in + // another repository inherit command styling or indentation. + const commanded = orchestrationDocs.some((doc) => { + const key = qualifiedLeafKey(doc); + return key?.startsWith(`${sprintKey}/`) || + (doc.repository === repo && (doc.orchestrates ?? []).includes(folder)); }); - } - const masterGroups = [...byMaster.entries()].map(([folder, members]) => { - const masterDoc = masterDocsByFolder.get(folder); - const commanded = masterDoc - ? orchestratorParentKey(masterCommandNames(masterDoc), docs, masterDoc.docPath) !== undefined - : orchestrationDocs.some((doc) => (doc.orchestrates ?? []).includes(folder)); + const hasCommand = deckExists && members.some((session) => + COMMAND_ROLES.has(sessionSeatRole(session)) || + (session.leafKey !== undefined && orchestrationKeys.has(session.leafKey)), + ); + const kind = hasCommand ? "command" : "master"; const group: SessionGroup = { - key: `master:${folder}`, - kind: "master", - label: masterDoc?.title || folder, - ...(commanded ? { tier: "management" as const } : {}), + key: `sprint:${sprintKey}`, + kind, + label: hasCommand + ? `${masterDoc?.title || `${repo}/${folder}`} · command deck` + : masterDoc?.title || `${repo}/${folder}`, + ...(hasCommand ? { tier: "orchestration" as const } : commanded ? { tier: "management" as const } : {}), nested: commanded, defaultCollapsed: false, sessions: members, - countLabel: countLabel(members, "master"), + countLabel: countLabel(members, kind), }; - return { order: masterDoc?.createdAt ?? "", folder, group }; + return { order: masterDoc?.createdAt ?? "", sprintKey, group }; }); - masterGroups.sort( - (left, right) => left.order.localeCompare(right.order) || left.folder.localeCompare(right.folder), + sprintGroups.sort( + (left, right) => left.order.localeCompare(right.order) || left.sprintKey.localeCompare(right.sprintKey), ); - for (const entry of masterGroups) groups.push(entry.group); + for (const entry of sprintGroups) groups.push(entry.group); if (landed.length > 0) { groups.push({ key: "landed", kind: "landed", - label: "landed", + label: "landed archive", nested: false, defaultCollapsed: true, sessions: landed, countLabel: countLabel(landed, "landed"), }); } + if (invalid.length > 0) { + groups.push({ + key: "error", + kind: "error", + label: "unresolvable session claims", + tier: "management", + nested: false, + defaultCollapsed: false, + sessions: invalid, + countLabel: `${invalid.length} error${invalid.length === 1 ? "" : "s"}`, + }); + } return { groups, ungrouped }; } diff --git a/dashboard/src/data/sessions.test.ts b/dashboard/src/data/sessions.test.ts index 9ff1a054..f3665bb0 100644 --- a/dashboard/src/data/sessions.test.ts +++ b/dashboard/src/data/sessions.test.ts @@ -149,11 +149,15 @@ describe("sessionStore (6e hardening)", () => { expect(sessionStore.getState().count).toBe(2); }); - it("does not route lifecycle injections to exited or terminated sessions", () => { + it("does not route lifecycle injections to non-live sessions", () => { sessionStore .getState() .hydrate([{ id: "dead", label: "Claude Code 1", lifecycleId: "LC1", status: "exited" }]); expect(findSessionForLifecycle("LC1")).toBeUndefined(); + sessionStore + .getState() + .hydrate([{ id: "landed", label: "Claude Code 1", lifecycleId: "LC1", status: "landed" }]); + expect(findSessionForLifecycle("LC1")).toBeUndefined(); }); it("moves active focus away when the active session exits", () => { @@ -205,12 +209,30 @@ describe("sessionStore (6e hardening)", () => { sessionStore.getState().add("Chat", "seeker"); sessionStore.getState().setLeaf("owner", leaf); - sessionStore.getState().applyLeafAssignment("seeker", leaf); + sessionStore.getState().applyLeafAssignment("seeker", leaf, "chat"); expect(findSessionForLeaf(leaf)?.id).toBe("seeker"); expect(sessionStore.getState().sessions.find((s) => s.id === "owner")?.leafKey).toBeUndefined(); }); + it("keeps different role seats on the same leaf while replacing only the matching role", () => { + const leaf = "repo/master/leaf-1"; + sessionStore.getState().hydrate([ + { id: "worker-old", label: "Worker", leafKey: leaf, seatRole: "worker", status: "running" }, + { id: "reviewer", label: "Reviewer", leafKey: leaf, seatRole: "reviewer", status: "running" }, + { id: "worker-new", label: "Replacement worker", status: "running" }, + ]); + + sessionStore.getState().applyLeafAssignment("worker-new", leaf, "worker"); + + expect(sessionStore.getState().sessions.find((s) => s.id === "worker-old")?.leafKey).toBeUndefined(); + expect(sessionStore.getState().sessions.find((s) => s.id === "reviewer")?.leafKey).toBe(leaf); + expect(sessionStore.getState().sessions.find((s) => s.id === "worker-new")).toMatchObject({ + leafKey: leaf, + seatRole: "worker", + }); + }); + it("frees a leaf so an exited owner no longer blocks a new bind", () => { const leaf = "repo/master/leaf-1"; sessionStore.getState().hydrate([{ id: "dead", label: "Chat 1", leafKey: leaf, status: "exited" }]); @@ -220,6 +242,15 @@ describe("sessionStore (6e hardening)", () => { expect(findSessionForLeaf(leaf)?.id).toBe("fresh"); }); + it("frees a leaf so a landed owner no longer blocks a new bind", () => { + const leaf = "repo/master/leaf-1"; + sessionStore.getState().hydrate([{ id: "landed", label: "Chat 1", leafKey: leaf, status: "landed" }]); + expect(findSessionForLeaf(leaf)).toBeUndefined(); + sessionStore.getState().add("Chat", "fresh"); + sessionStore.getState().setLeaf("fresh", leaf); + expect(findSessionForLeaf(leaf)?.id).toBe("fresh"); + }); + it("clears a leaf binding when set to null", () => { const leaf = "repo/master/leaf-1"; sessionStore.getState().add("Chat", "agent-1"); @@ -237,13 +268,21 @@ describe("sessionStore (6e hardening)", () => { label: "Chat 1", kind: "terminal", leafKey: leaf, + seatRole: "terminal", cwd: "/ws", tmuxName: "ar-s1", createdAt: "2026-06-26T00:00:00Z", lastAttachedAt: "2026-06-26T00:00:00Z", status: "running", }), - ).toEqual({ id: "s1", label: "Chat 1", kind: "terminal", leafKey: leaf, status: "running" }); + ).toEqual({ + id: "s1", + label: "Chat 1", + kind: "terminal", + leafKey: leaf, + seatRole: "terminal", + status: "running", + }); // No leafKey on the row → no leafKey on the session (omitted, not undefined-valued). expect( @@ -284,6 +323,52 @@ describe("sessionStore (6e hardening)", () => { }); }); + it("converts landed catalog metadata into store sessions", () => { + expect( + fromTerminalSessionInfo({ + id: "s1", + label: "Claude Code 2", + kind: "harness", + harness: "claude", + lifecycleId: "LC1", + leafKey: "repo/master/leaf-1", + spawnRole: "worker", + seatRole: "reviewer", + cwd: "/ws", + tmuxName: "ar-s1", + createdAt: "2026-06-26T00:00:00Z", + lastAttachedAt: "2026-06-26T00:00:00Z", + status: "landed", + landedAt: "2026-07-09T00:00:00Z", + landedReason: "leaf integrated", + landedEdge: "leaf-integration", + spawnedBySession: "manager", + spawnedByLifecycle: "LC0", + spawnedLabel: "Worker", + turnState: "turn-ended", + turnStateChangedAt: "2026-07-09T00:01:00Z", + }), + ).toEqual({ + id: "s1", + label: "Claude Code 2", + kind: "harness", + harness: "claude", + lifecycleId: "LC1", + leafKey: "repo/master/leaf-1", + spawnRole: "worker", + seatRole: "reviewer", + status: "landed", + landedAt: "2026-07-09T00:00:00Z", + landedReason: "leaf integrated", + landedEdge: "leaf-integration", + spawnedBySession: "manager", + spawnedByLifecycle: "LC0", + spawnedLabel: "Worker", + turnState: "turn-ended", + turnStateChangedAt: "2026-07-09T00:01:00Z", + }); + }); + it("passes the generated label and lifecycle to the opener before registering the session", async () => { vi.stubGlobal("crypto", { randomUUID: () => "generated-id" }); const fetchMock = vi.fn().mockResolvedValue({ ok: true }); diff --git a/dashboard/src/data/sessions.ts b/dashboard/src/data/sessions.ts index c00bb483..2b743ecc 100644 --- a/dashboard/src/data/sessions.ts +++ b/dashboard/src/data/sessions.ts @@ -29,7 +29,17 @@ export interface OpenSession { leafKey?: string; /** The AR_SPAWN_ROLE recorded at spawn (L14) — the Chats command-tree grouping key. */ spawnRole?: string; + /** The role occupying the leaf binding; authoritative for grouping and seat identity. */ + seatRole?: string; status?: TerminalSessionStatus; + landedAt?: string; + landedReason?: string; + landedEdge?: string; + spawnedBySession?: string; + spawnedByLifecycle?: string; + spawnedLabel?: string; + turnState?: string; + turnStateChangedAt?: string; } type SessionCatalogChangeReason = "create" | "terminate" | "leaf"; @@ -106,15 +116,14 @@ interface SessionState { setLifecycle: (id: string, lifecycleId: string | null) => void; /** * Bind a hosted session to one durable leaf (qualified leaf id), or clear it (`null`). Advisory - * uniqueness: a non-null bind is REJECTED (no-op) if another LIVE session already owns that leaf — - * the server's `409 leaf-taken` is the real arbiter, this just avoids an obvious local double-claim. + * uniqueness is scoped to the session's current role; the server remains the real arbiter. */ setLeaf: (id: string, leafKey: string | null) => void; /** * Apply a server/catalog-authoritative leaf assignment after a successful backend attach or hydrate. * Same-role local owners of the destination leaf are cleared because the catalog result wins. */ - applyLeafAssignment: (id: string, leafKey: string | null) => void; + applyLeafAssignment: (id: string, leafKey: string | null, seatRole: string) => void; } /** A session's leaf-uniqueness role: a plain shell is a TERMINAL, any agent harness is a CHAT. */ @@ -125,6 +134,21 @@ export function sessionRole(session: Pick): SessionRole { return session.kind === "terminal" ? "terminal" : "chat"; } +/** The role occupying a leaf binding; legacy rows fall back to origin provenance, then transport. */ +export function sessionSeatRole( + session: Pick, +): string { + return session.seatRole ?? session.spawnRole ?? sessionRole(session); +} + +/** Preselect only a declared/typed attach role; a legacy generic chat must be chosen explicitly. */ +export function attachSeatRole( + session: Pick, +): string | undefined { + if (session.kind === "terminal") return "terminal"; + return session.spawnRole ?? (session.seatRole && session.seatRole !== "chat" ? session.seatRole : undefined); +} + function clearLifecycle(session: OpenSession): OpenSession { const next = { ...session }; delete next.lifecycleId; @@ -155,7 +179,7 @@ function maxOrdinal(labels: string[]): number { } function isLiveSession(session: OpenSession): boolean { - return session.status !== "exited" && session.status !== "terminated"; + return (session.status ?? "running") === "running"; } function liveLabels(sessions: OpenSession[]): string[] { @@ -276,13 +300,13 @@ export const sessionStore = createStore((set) => ({ // Advisory guard, scoped to the binding session's role (chat vs. terminal): a live session of // the SAME role already owning this leaf wins — the new bind is a no-op. A chat and a terminal // can both bind one leaf, so they never block each other. The server's 409 is the real arbiter. - const role = sessionRole(state.sessions.find((session) => session.id === id) ?? {}); + const role = sessionSeatRole(state.sessions.find((session) => session.id === id) ?? {}); const owner = state.sessions.find( (session) => session.id !== id && session.leafKey === leafKey && isLiveSession(session) && - sessionRole(session) === role, + sessionSeatRole(session) === role, ); if (owner) return state; } @@ -296,21 +320,20 @@ export const sessionStore = createStore((set) => ({ ), }; }), - applyLeafAssignment: (id, leafKey) => + applyLeafAssignment: (id, leafKey, seatRole) => set((state) => { const target = state.sessions.find((session) => session.id === id); if (!target) return state; - const role = sessionRole(target); return { sessions: state.sessions.map((session) => { if (session.id === id) { - return leafKey ? { ...session, leafKey } : clearLeaf(session); + return leafKey ? { ...session, leafKey, seatRole } : clearLeaf(session); } if ( leafKey && session.leafKey === leafKey && isLiveSession(session) && - sessionRole(session) === role + sessionSeatRole(session) === seatRole ) { return clearLeaf(session); } @@ -353,6 +376,15 @@ export function fromTerminalSessionInfo(info: TerminalSessionInfo): OpenSession ...(info.lifecycleId ? { lifecycleId: info.lifecycleId } : {}), ...(info.leafKey ? { leafKey: info.leafKey } : {}), ...(info.spawnRole ? { spawnRole: info.spawnRole } : {}), + ...(info.seatRole ? { seatRole: info.seatRole } : {}), + ...(info.landedAt ? { landedAt: info.landedAt } : {}), + ...(info.landedReason ? { landedReason: info.landedReason } : {}), + ...(info.landedEdge ? { landedEdge: info.landedEdge } : {}), + ...(info.spawnedBySession ? { spawnedBySession: info.spawnedBySession } : {}), + ...(info.spawnedByLifecycle ? { spawnedByLifecycle: info.spawnedByLifecycle } : {}), + ...(info.spawnedLabel ? { spawnedLabel: info.spawnedLabel } : {}), + ...(info.turnState ? { turnState: info.turnState } : {}), + ...(info.turnStateChangedAt ? { turnStateChangedAt: info.turnStateChangedAt } : {}), status: info.status, }; } diff --git a/dashboard/src/data/store.test.ts b/dashboard/src/data/store.test.ts index 3ebdfb5d..ebe26c52 100644 --- a/dashboard/src/data/store.test.ts +++ b/dashboard/src/data/store.test.ts @@ -103,6 +103,56 @@ describe("dashboard store change gate (260703-L15)", () => { expect(after.generatedAt).toBe(before.generatedAt); // ages/stamp coherence: no new content, no new stamp }); + it("applies an idle re-snapshot with an unchanged supervisorHeartbeat (incl. null/null) with zero store writes", () => { + dashboardStore.getState().applySnapshot(projection); // supervisorHeartbeat stays null + const before = dashboardStore.getState(); + expect(before.supervisorHeartbeat).toBeNull(); + let notifications = 0; + const unsubscribe = dashboardStore.subscribe(() => { + notifications += 1; + }); + dashboardStore.getState().applySnapshot(volatileBump(projection, 1)); // still no supervisorHeartbeat + unsubscribe(); + expect(notifications).toBe(0); + expect(dashboardStore.getState()).toBe(before); + expect(dashboardStore.getState().supervisorHeartbeat).toBeNull(); + }); + + it("applies an idle re-snapshot with a genuinely changed supervisorHeartbeat", () => { + const withHeartbeat = { + ...projection, + supervisorHeartbeat: { + lastTickAt: "2026-07-08T00:00:00+00:00", + ageSeconds: 1, + staleCutoffSeconds: 30, + stale: false, + pendingInboxCount: 2, + redeliverableInboxCount: 1, + lastSweepDurationSeconds: 0.2, + }, + } as WorkspaceProjection; + dashboardStore.getState().applySnapshot(withHeartbeat); + const before = dashboardStore.getState(); + let notifications = 0; + const unsubscribe = dashboardStore.subscribe(() => { + notifications += 1; + }); + const advanced = { + ...withHeartbeat, + supervisorHeartbeat: { ...withHeartbeat.supervisorHeartbeat, ageSeconds: 5 }, + } as WorkspaceProjection; + dashboardStore.getState().applySnapshot(advanced); + unsubscribe(); + expect(notifications).toBe(1); // the tick advance IS a store write + const after = dashboardStore.getState(); + expect(after).not.toBe(before); + expect(after.supervisorHeartbeat).toEqual(advanced.supervisorHeartbeat); + // everything else stays identity-stable — only supervisorHeartbeat rode through + expect(after.lifecycles).toBe(before.lifecycles); + expect(after.analytics).toBe(before.analytics); + expect(after.generatedAt).toBe(before.generatedAt); + }); + it("skips a redundant delta (volatile-only node) without a store write", () => { dashboardStore.getState().applySnapshot(projection); const before = dashboardStore.getState(); diff --git a/dashboard/src/data/store.ts b/dashboard/src/data/store.ts index e6e9212c..9c891a99 100644 --- a/dashboard/src/data/store.ts +++ b/dashboard/src/data/store.ts @@ -9,6 +9,7 @@ import type { Metrics, ProviderNode, ServingBuild, + SupervisorHeartbeat, WorkspaceProjection, } from "../types/projection"; import { stableEquals, stampServed } from "./servedAges"; @@ -32,6 +33,9 @@ export interface DashboardState { // The boot-time serving stamp (260703-L15): which build/process is answering. Rides the // snapshot; rendered muted in the top bar so a stale server is visible at a glance. servingBuild: ServingBuild | null; + // The supervisor sweep's self-liveness tick (260707-HFX2-L2 R5). Rendered red in the top bar + // past its staleness cutoff -- "the last turtle is the developer's glance". + supervisorHeartbeat: SupervisorHeartbeat | null; events: ObserverEvent[]; // raw observer feed retained client-side until reset/reload eventsHydrated: boolean; suppressedAttentionIds: Record; @@ -62,6 +66,24 @@ const EVENT_WINDOW = 2000; /** Reuse the current value when the incoming one is stable-equal (keeps identity + anchor). */ const reuse = (current: T, incoming: T): T => (stableEquals(current, incoming) ? current : incoming); +// `stableEquals` deliberately ignores `ageSeconds` (it's in VOLATILE_AGE_FIELDS) -- exactly +// the field the heartbeat tick advances. A field-literal comparison is needed here so a +// genuine tick advance (ageSeconds changing) is still recognized as a change, while a truly +// idle heartbeat (including null/null) still produces zero store writes. +const heartbeatEquals = (a: SupervisorHeartbeat | null, b: SupervisorHeartbeat | null): boolean => { + if (a === b) return true; + if (a === null || b === null) return false; + return ( + a.lastTickAt === b.lastTickAt && + a.ageSeconds === b.ageSeconds && + a.staleCutoffSeconds === b.staleCutoffSeconds && + a.stale === b.stale && + a.pendingInboxCount === b.pendingInboxCount && + a.redeliverableInboxCount === b.redeliverableInboxCount && + a.lastSweepDurationSeconds === b.lastSweepDurationSeconds + ); +}; + /** * Merge an incoming collection into the id-keyed map, reusing every stable-equal node. * Returns the EXISTING map object when nothing changed (added/updated/removed). @@ -211,6 +233,7 @@ export const dashboardStore = createStore((set, get) => ({ metrics: null, analytics: null, servingBuild: null, + supervisorHeartbeat: null, events: [], eventsHydrated: false, suppressedAttentionIds: {}, @@ -230,6 +253,10 @@ export const dashboardStore = createStore((set, get) => ({ const analytics = reuse(state.analytics, projection.analytics); if (analytics !== state.analytics) stampAnalytics(analytics); const servingBuild = reuse(state.servingBuild, projection.servingBuild ?? null); + // 260707-HFX2-L2 R5: deliberately EXCLUDED from the `unchanged` gate below — it is a live tick + // age injected at response time (mirroring the backend's own "volatile ages excluded" posture, + // delta.py), so it is always applied even when nothing else in the snapshot changed. + const supervisorHeartbeat = projection.supervisorHeartbeat ?? null; const suppressedAttentionIds = pruneSuppressedAttention( state.suppressedAttentionIds, analytics, @@ -243,8 +270,15 @@ export const dashboardStore = createStore((set, get) => ({ analytics === state.analytics && servingBuild === state.servingBuild && suppressedAttentionIds === state.suppressedAttentionIds; - // An unchanged snapshot (a reconnect while idle) is a NO-OP: no store write at all. - if (unchanged && state.conn === "live") return; + // An unchanged snapshot (a reconnect while idle) is a NO-OP for content, but the heartbeat + // tick still rides through below -- only when it actually changed, so a truly idle + // reconnect (e.g. null/null) still performs zero store writes. + if (unchanged && state.conn === "live") { + if (!heartbeatEquals(state.supervisorHeartbeat, supervisorHeartbeat)) { + set({ supervisorHeartbeat }); + } + return; + } set({ conn: "live", // `generatedAt` is the stamp of the last APPLIED content (the top bar's "@ hh:mm:ss"), @@ -257,6 +291,7 @@ export const dashboardStore = createStore((set, get) => ({ metrics, analytics, servingBuild, + supervisorHeartbeat, suppressedAttentionIds, }); }, @@ -304,6 +339,7 @@ export const dashboardStore = createStore((set, get) => ({ metrics: null, analytics: null, servingBuild: null, + supervisorHeartbeat: null, events: [], eventsHydrated: false, suppressedAttentionIds: {}, diff --git a/dashboard/src/data/taskDocuments.ts b/dashboard/src/data/taskDocuments.ts new file mode 100644 index 00000000..e556f123 --- /dev/null +++ b/dashboard/src/data/taskDocuments.ts @@ -0,0 +1,9 @@ +import type { TaskDocNode } from "../types/projection"; + +export async function fetchTaskDocument(docPath: string, base = ""): Promise { + const response = await fetch(`${base}/api/task-document?path=${encodeURIComponent(docPath)}`); + if (!response.ok) { + throw new Error(`task document fetch failed: ${response.status}`); + } + return (await response.json()) as TaskDocNode; +} diff --git a/dashboard/src/data/terminal.test.ts b/dashboard/src/data/terminal.test.ts index bdef068c..ef9889c7 100644 --- a/dashboard/src/data/terminal.test.ts +++ b/dashboard/src/data/terminal.test.ts @@ -1,7 +1,9 @@ import { describe, expect, it, vi } from "vitest"; import { + attachSessionToLeaf, bracketedPaste, + cleanupLandedTerminalSessions, connectTerminal, fetchHarnesses, fetchTerminalSessions, @@ -308,6 +310,61 @@ describe("terminateTerminalSession", () => { }); }); +describe("cleanupLandedTerminalSessions", () => { + it("POSTs selected session ids and normalizes the cleanup result", async () => { + const fetchMock = vi.fn().mockResolvedValue({ + ok: true, + json: () => + Promise.resolve({ + closed: 1, + skipped: 1, + closedSessions: ["landed"], + skippedSessions: [{ session: "active", reason: "status:running" }], + }), + }); + vi.stubGlobal("fetch", fetchMock); + await expect(cleanupLandedTerminalSessions(["landed", "active"])).resolves.toEqual({ + closed: 1, + skipped: 1, + closedSessions: ["landed"], + skippedSessions: [{ session: "active", reason: "status:running" }], + }); + expect(fetchMock).toHaveBeenCalledWith( + "/api/terminal/landed-cleanup", + expect.objectContaining({ + method: "POST", + body: JSON.stringify({ sessionIds: ["landed", "active"] }), + }), + ); + vi.unstubAllGlobals(); + }); + + it("returns null on a non-ok response or network error", async () => { + vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ ok: false })); + expect(await cleanupLandedTerminalSessions(["s1"])).toBeNull(); + vi.stubGlobal("fetch", vi.fn().mockRejectedValue(new Error("offline"))); + expect(await cleanupLandedTerminalSessions(["s1"])).toBeNull(); + vi.unstubAllGlobals(); + }); +}); + +describe("attachSessionToLeaf", () => { + it("posts the leaf and explicit seat role as one binding move", async () => { + const fetchMock = vi.fn().mockResolvedValue({ ok: true }); + vi.stubGlobal("fetch", fetchMock); + + await expect(attachSessionToLeaf("s 1", "repo/master/leaf", "curator")).resolves.toBe("ok"); + expect(fetchMock).toHaveBeenCalledWith( + "/api/terminal/s%201/attach-leaf", + expect.objectContaining({ + method: "POST", + body: JSON.stringify({ leafKey: "repo/master/leaf", role: "curator" }), + }), + ); + vi.unstubAllGlobals(); + }); +}); + describe("sanitizeForInjection (6f hardening)", () => { it("strips the Ctrl-Z suspend byte and other C0 controls but keeps \\n and \\t", () => { expect(sanitizeForInjection("a\x1ab")).toBe("ab"); // 0x1a (Ctrl-Z) — the suspend byte diff --git a/dashboard/src/data/terminal.ts b/dashboard/src/data/terminal.ts index 99fd5139..2e892310 100644 --- a/dashboard/src/data/terminal.ts +++ b/dashboard/src/data/terminal.ts @@ -270,7 +270,7 @@ export interface HarnessInfo { detected: boolean; } -export type TerminalSessionStatus = "running" | "exited" | "terminated"; +export type TerminalSessionStatus = "running" | "exited" | "landed" | "terminated"; export interface TerminalSessionInfo { id: string; @@ -281,14 +281,24 @@ export interface TerminalSessionInfo { /** The durable leaf-identity key (qualified leaf id `repo/master/leaf-id`) this chat claims. */ leafKey?: string; /** The AR_SPAWN_ROLE recorded at spawn (L14): the l-01 role this session was dispatched AS - * (orchestrator/strategist/manager/worker/reviewer/designer). Absent on hand-opened sessions. */ + * (architect/orchestrator/strategist/manager/worker/curator/reviewer/designer). Absent on hand-opened sessions. */ spawnRole?: string; + /** The role occupying the leaf binding. Hand-opened sessions gain this at attach time. */ + seatRole?: string; cwd: string; tmuxName: string; createdAt: string; lastAttachedAt: string; status: TerminalSessionStatus; terminatedAt?: string; + landedAt?: string; + landedReason?: string; + landedEdge?: string; + spawnedBySession?: string; + spawnedByLifecycle?: string; + spawnedLabel?: string; + turnState?: string; + turnStateChangedAt?: string; } interface OpenTerminalOptions { @@ -374,17 +384,48 @@ export async function terminateTerminalSession(sessionId: string, base = ""): Pr } } -/** The outcome of a leaf-attach POST: bound, refused (the leaf already has a running chat), or failed. */ +export interface LandedCleanupResult { + closed: number; + skipped: number; + closedSessions: string[]; + skippedSessions: Array<{ session: string; reason: string }>; +} + +export async function cleanupLandedTerminalSessions( + sessionIds: string[], + base = "", +): Promise { + try { + const response = await fetch(`${base}/api/terminal/landed-cleanup`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ sessionIds }), + }); + if (!response.ok) return null; + const body = (await response.json()) as Partial; + return { + closed: typeof body.closed === "number" ? body.closed : 0, + skipped: typeof body.skipped === "number" ? body.skipped : 0, + closedSessions: Array.isArray(body.closedSessions) ? body.closedSessions : [], + skippedSessions: Array.isArray(body.skippedSessions) ? body.skippedSessions : [], + }; + } catch { + return null; + } +} + +/** The outcome of a leaf-attach POST: bound, refused (the pair already has a live owner), or failed. */ export type AttachLeafResult = "ok" | "leaf-taken" | "error"; /** - * Claim a free leaf for an EXISTING session (slice L5): `POST /api/terminal/{id}/attach-leaf {leafKey}`. - * The server is the uniqueness arbiter — `200` binds the leaf, `409` means another running chat already - * owns it (`"leaf-taken"`), any other status / network failure is `"error"`. Enclosure-independent. + * Claim one leaf-role pair for an existing session. The server is the uniqueness arbiter: `200` + * atomically binds the pair, `409` means another live session owns that same pair, and any other + * status or network failure is `"error"`. Enclosure-independent. */ export async function attachSessionToLeaf( sessionId: string, leafKey: string, + role: string, base = "", ): Promise { try { @@ -393,7 +434,7 @@ export async function attachSessionToLeaf( { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ leafKey }), + body: JSON.stringify({ leafKey, role }), }, ); if (response.ok) return "ok"; diff --git a/dashboard/src/panels/Chats.test.tsx b/dashboard/src/panels/Chats.test.tsx index f25915f6..dbbbf40e 100644 --- a/dashboard/src/panels/Chats.test.tsx +++ b/dashboard/src/panels/Chats.test.tsx @@ -30,10 +30,21 @@ function secondLeafDoc(): TaskDocNode { } as unknown as TaskDocNode; } +function pointerEvent(type: string, clientX: number, pointerId = 1): Event { + const event = new Event(type, { bubbles: true }); + Object.defineProperties(event, { + clientX: { value: clientX }, + pointerId: { value: pointerId }, + }); + return event; +} + // Mock the lazy Terminal so opening a session never pulls xterm (a canvas probe) into jsdom; the stub // just marks its sessionId so a test can assert which session terminals stay mounted. vi.mock("./Terminal", () => ({ - Terminal: ({ sessionId }: { sessionId: string }) =>
, + Terminal: ({ sessionId, readOnly }: { sessionId: string; readOnly?: boolean }) => ( +
+ ), })); class FakeBroadcastChannel { @@ -114,6 +125,45 @@ describe("Chats harness launch buttons (6e-2b)", () => { }); }); +describe("Chats sidebar resize", () => { + it("restores the persisted width and exposes the bounded separator value", async () => { + vi.stubGlobal("fetch", vi.fn().mockRejectedValue(new Error("no backend"))); + window.localStorage.setItem("chats.sidebar-width", "420"); + sessionStore.getState().add("Terminal", "s1"); + + const { findByTestId } = render(); + + expect((await findByTestId("chats-sidebar")).style.width).toBe("420px"); + expect((await findByTestId("chats-sidebar-resize")).getAttribute("aria-valuenow")).toBe( + "420", + ); + }); + + it("resizes with pointer drag and keyboard arrows, persisting each width", async () => { + vi.stubGlobal("fetch", vi.fn().mockRejectedValue(new Error("no backend"))); + sessionStore.getState().add("Terminal", "s1"); + + const { findByTestId } = render(); + const sidebar = await findByTestId("chats-sidebar"); + const handle = await findByTestId("chats-sidebar-resize"); + handle.setPointerCapture = vi.fn(); + + fireEvent(handle, pointerEvent("pointerdown", 300)); + fireEvent(window, pointerEvent("pointermove", 360)); + fireEvent(window, pointerEvent("pointerup", 360)); + expect(sidebar.style.width).toBe("316px"); + expect(window.localStorage.getItem("chats.sidebar-width")).toBe("316"); + + fireEvent.keyDown(handle, { key: "ArrowRight" }); + expect(sidebar.style.width).toBe("340px"); + expect(window.localStorage.getItem("chats.sidebar-width")).toBe("340"); + + fireEvent.keyDown(handle, { key: "ArrowLeft" }); + expect(sidebar.style.width).toBe("316px"); + expect(window.localStorage.getItem("chats.sidebar-width")).toBe("316"); + }); +}); + // 6e-4 + task 22: tmux/catalog own refresh persistence. The UI initially attaches only the active // restored terminal; once a row has been selected in this page, it stays mounted while hidden so its // xterm buffer survives tab switches. @@ -323,6 +373,132 @@ describe("Chats session-tab persistence (6e-4)", () => { expect(queryByTestId("term-s1")).toBeNull(); }); + it("renders a landed restored session as a read-only terminal attachment", async () => { + vi.stubGlobal( + "fetch", + vi.fn((input: RequestInfo | URL) => { + const url = String(input); + if (url.endsWith("/api/harnesses")) { + return Promise.resolve({ ok: true, json: () => Promise.resolve({ harnesses: [] }) }); + } + if (url.endsWith("/api/terminal/sessions")) { + return Promise.resolve({ + ok: true, + json: () => + Promise.resolve({ + sessions: [ + { + id: "s1", + label: "Terminal 1", + kind: "terminal", + cwd: "/ws", + tmuxName: "ar-s1", + createdAt: "2026-06-26T00:00:00Z", + lastAttachedAt: "2026-06-26T00:00:00Z", + status: "landed", + landedReason: "leaf integrated", + }, + ], + }), + }); + } + return Promise.reject(new Error(`unexpected url ${url}`)); + }), + ); + + const { findByTestId, queryByTestId } = render(); + + const terminal = await findByTestId("term-s1"); + expect(terminal.getAttribute("data-readonly")).toBe("true"); + expect(queryByTestId("chats-session-status-s1")).toBeNull(); + expect(queryByTestId("chats-composer")).toBeNull(); + }); + + it("cleans up the landed archive group and reports closed/skipped counts", async () => { + vi.stubGlobal("BroadcastChannel", FakeBroadcastChannel); + let catalogSessions = [ + { + id: "landed", + label: "Worker", + kind: "harness", + harness: "claude", + leafKey: LEAF_KEY, + cwd: "/ws", + tmuxName: "ar-landed", + createdAt: "2026-07-02T00:00:00Z", + lastAttachedAt: "2026-07-02T00:00:00Z", + status: "landed", + }, + { + id: "active", + label: "Active", + kind: "harness", + harness: "claude", + leafKey: SECOND_LEAF_KEY, + cwd: "/ws", + tmuxName: "ar-active", + createdAt: "2026-07-02T00:01:00Z", + lastAttachedAt: "2026-07-02T00:01:00Z", + status: "running", + }, + ]; + let cleanupPayload: unknown; + vi.stubGlobal( + "fetch", + vi.fn((input: RequestInfo | URL, init?: RequestInit) => { + const url = String(input); + if (url.endsWith("/api/harnesses")) { + return Promise.resolve({ ok: true, json: () => Promise.resolve({ harnesses: [] }) }); + } + if (url.endsWith("/api/terminal/sessions")) { + return Promise.resolve({ + ok: true, + json: () => Promise.resolve({ sessions: catalogSessions }), + }); + } + if (url.endsWith("/api/terminal/landed-cleanup")) { + cleanupPayload = JSON.parse(String(init?.body ?? "{}")); + catalogSessions = catalogSessions.filter((session) => session.id !== "landed"); + return Promise.resolve({ + ok: true, + json: () => + Promise.resolve({ + closed: 1, + skipped: 0, + closedSessions: ["landed"], + skippedSessions: [], + }), + }); + } + return Promise.reject(new Error(`unexpected url ${url}`)); + }), + ); + sessionStore.getState().hydrate([ + { id: "landed", label: "Worker", leafKey: LEAF_KEY, status: "landed" }, + { id: "active", label: "Active", leafKey: SECOND_LEAF_KEY, status: "running" }, + ]); + + const { findByTestId } = render( + , + ); + + fireEvent.click(await findByTestId("chats-group-cleanup-landed")); + + await waitFor(() => + expect(sessionStore.getState().sessions.map((session) => session.id)).toEqual(["active"]), + ); + expect(cleanupPayload).toEqual({ sessionIds: ["landed"] }); + expect((await findByTestId("chats-landed-cleanup-status")).textContent).toContain( + "1 closed · 0 skipped", + ); + expect(FakeBroadcastChannel.messages).toEqual([ + expect.objectContaining({ + type: "terminal-catalog-changed", + reason: "terminate", + }), + ]); + }); + it("lists projected leaves in the picker and binds the picked leaf on 200 with NO leaf selected", async () => { // The decoupling contract: an unattached chat made anywhere (no `selectedLeafKey`) can still be // attached to ANY projected leaf through the picker — not only the leaf currently being viewed. @@ -342,6 +518,7 @@ describe("Chats session-tab persistence (6e-4)", () => { // Drill-down picker: open it, then pick the leaf (a lone leaf with no master doc shows at top level), // labelled by its task-doc title. fireEvent.click(await findByTestId("chats-attach-leaf-picker")); + fireEvent.click(await findByTestId("chats-attach-leaf-picker-role-worker")); const leaf = await findByTestId("chats-attach-leaf-picker-leaf"); expect(leaf.getAttribute("data-leaf-key")).toBe(LEAF_KEY); expect(leaf.textContent).toContain("Sidebar chat attachment"); @@ -372,6 +549,7 @@ describe("Chats session-tab persistence (6e-4)", () => { expect(await findByTestId("chats-leaf-badge")).not.toBeNull(); fireEvent.click(await findByTestId("chats-attach-leaf-picker")); + fireEvent.click(await findByTestId("chats-attach-leaf-picker-role-worker")); const leaves = await findAllByTestId("chats-attach-leaf-picker-leaf"); const next = leaves.find((leaf) => leaf.getAttribute("data-leaf-key") === SECOND_LEAF_KEY); expect(next).not.toBeUndefined(); @@ -395,10 +573,11 @@ describe("Chats session-tab persistence (6e-4)", () => { const { findByTestId } = render(); fireEvent.click(await findByTestId("chats-attach-leaf-picker")); + fireEvent.click(await findByTestId("chats-attach-leaf-picker-role-worker")); fireEvent.click(await findByTestId("chats-attach-leaf-picker-leaf")); const note = await findByTestId("chats-leaf-attach-error"); - expect(note.textContent).toContain("leaf already has a chat"); + expect(note.textContent).toContain("leaf already has a worker seat"); expect(sessionStore.getState().sessions[0]?.leafKey).toBeUndefined(); }); diff --git a/dashboard/src/panels/Chats.tsx b/dashboard/src/panels/Chats.tsx index 499472d3..dec6d289 100644 --- a/dashboard/src/panels/Chats.tsx +++ b/dashboard/src/panels/Chats.tsx @@ -1,7 +1,15 @@ -import { lazy, Suspense, useEffect, useState } from "react"; +import { + lazy, + Suspense, + useEffect, + useState, + type KeyboardEvent as ReactKeyboardEvent, + type PointerEvent as ReactPointerEvent, +} from "react"; import { css } from "../../styled-system/css"; import { + attachSeatRole, createSession, fromTerminalSessionInfo, notifySessionCatalogChanged, @@ -14,6 +22,7 @@ import { import { attachSessionToLeaf, bracketedPaste, + cleanupLandedTerminalSessions, fetchHarnesses, fetchTerminalSessionsOrNull, sanitizeForInjection, @@ -25,6 +34,7 @@ import { useDashboard } from "../data/store"; import { buildTaskTree, leafIdFromKey, leafTitleForKey } from "../data/taskIdentity"; import type { TaskDocNode } from "../types/projection"; import { EmptyStateBackdrop } from "./EmptyStateBackdrop"; +import { usePersistedNumber } from "./file-viewer/usePersistedFlag"; import { LeafAttachPicker } from "./LeafAttachPicker"; import { SessionComposer } from "./SessionComposer"; import { SessionList } from "./SessionList"; @@ -104,23 +114,38 @@ const attachError = css({ paddingInline: "0.2rem", }); const harnessIcon = css({ flexShrink: 0, display: "block" }); -const body = css({ display: "flex", flex: "1", minHeight: "0", gap: "0.5rem" }); +const body = css({ display: "flex", flex: "1", minHeight: "0", minWidth: "0", gap: "0.5rem" }); const sidebar = css({ + position: "relative", display: "flex", flexDirection: "column", flexShrink: 0, - width: "16rem", minHeight: "0", + minWidth: "0", + maxWidth: "calc(100% - 20rem)", borderRightWidth: "1px", borderRightStyle: "solid", borderRightColor: "grid", paddingRight: "0.4rem", }); +const sidebarResizeHandle = css({ + position: "absolute", + top: "0", + right: "0", + bottom: "0", + width: "7px", + zIndex: "3", + cursor: "col-resize", + background: "transparent", + transition: "background 0.15s ease", + _hover: { background: "amber" }, + _focusVisible: { outline: "1px solid token(colors.amber)", outlineOffset: "-1px" }, +}); const terminalArea = css({ display: "flex", flexDirection: "column", flex: "1", - minWidth: "0", + minWidth: "min(20rem, 100%)", minHeight: "0", gap: "0.4rem", }); @@ -157,6 +182,64 @@ const statusPanel = css({ const LAST_ACTIVE_SESSION_KEY = "ar-dashboard:last-active-chat-session"; const CATALOG_REFRESH_INTERVAL_MS = 2500; +const SIDEBAR_WIDTH_KEY = "chats.sidebar-width"; +const SIDEBAR_DEFAULT_WIDTH = 256; +const SIDEBAR_MIN_WIDTH = 220; +const SIDEBAR_MAX_WIDTH = 560; +const SIDEBAR_KEYBOARD_STEP = 24; + +function clampSidebarWidth(width: number): number { + return Math.max(SIDEBAR_MIN_WIDTH, Math.min(SIDEBAR_MAX_WIDTH, Math.round(width))); +} + +function SidebarResizeHandle({ + width, + onResize, +}: { + width: number; + onResize: (next: number) => void; +}) { + const onPointerDown = (event: ReactPointerEvent) => { + event.preventDefault(); + const startX = event.clientX; + const startWidth = width; + event.currentTarget.setPointerCapture(event.pointerId); + const move = (moveEvent: globalThis.PointerEvent) => { + onResize(clampSidebarWidth(startWidth + moveEvent.clientX - startX)); + }; + const up = () => { + window.removeEventListener("pointermove", move); + window.removeEventListener("pointerup", up); + }; + window.addEventListener("pointermove", move); + window.addEventListener("pointerup", up); + }; + const onKeyDown = (event: ReactKeyboardEvent) => { + const direction = event.key === "ArrowRight" ? 1 : event.key === "ArrowLeft" ? -1 : 0; + if (!direction) return; + event.preventDefault(); + onResize(clampSidebarWidth(width + direction * SIDEBAR_KEYBOARD_STEP)); + }; + return ( +
+ ); +} + +function isInspectableSession(status: string | undefined): boolean { + return (status ?? "running") === "running" || status === "landed"; +} function readLastActiveSessionId(): string | null { try { @@ -223,6 +306,12 @@ export function Chats({ const sessions = useSessions((state) => state.sessions); const activeId = useSessions((state) => state.activeId); const activeSession = sessions.find((session) => session.id === activeId); + const activeSessionIsRunning = (activeSession?.status ?? "running") === "running"; + const [persistedSidebarWidth, setPersistedSidebarWidth] = usePersistedNumber( + SIDEBAR_WIDTH_KEY, + SIDEBAR_DEFAULT_WIDTH, + ); + const sidebarWidth = clampSidebarWidth(persistedSidebarWidth); const [mountedSessionIds, setMountedSessionIds] = useState>(() => new Set()); const [harnesses, setHarnesses] = useState([]); // The attached-leaf name resolver for the session list ("who works on what"): the bound leaf's task-doc @@ -231,6 +320,7 @@ export function Chats({ leafTitleForKey(taskDocuments, leafKey) ?? leafIdFromKey(leafKey); // A transient note for a refused leaf attach (the server's 409 leaf-taken). const [leafAttachError, setLeafAttachError] = useState(null); + const [landedCleanupNote, setLandedCleanupNote] = useState(null); // Detection-driven: the server reports which supported harnesses are installed; a button appears // only for detected ones. `[]` (no backend / failure) just leaves + Terminal alone. @@ -284,7 +374,7 @@ export function Chats({ setMountedSessionIds((current) => { const sessionIds = new Set(sessions.map((session) => session.id)); const next = new Set([...current].filter((id) => sessionIds.has(id))); - if (activeSession && (activeSession.status ?? "running") === "running") { + if (activeSession && isInspectableSession(activeSession.status)) { next.add(activeSession.id); } if (next.size === current.size && [...next].every((id) => current.has(id))) return current; @@ -298,22 +388,22 @@ export function Chats({ : createSession(label, kind, harness); const attachActive = () => { - if (!activeSession || activeSession.lifecycleId || !selectedLifecycleId) return; + if (!activeSession || !activeSessionIsRunning || activeSession.lifecycleId || !selectedLifecycleId) return; sessionStore.getState().setLifecycle(activeSession.id, selectedLifecycleId); }; // Attach or move the active session to ANY chosen leaf via the server (the uniqueness arbiter) — NOT // just the leaf you happen to be viewing. On success bind the leaf locally + broadcast the catalog // change; on 409 the leaf already has a same-role running session, so surface a note instead of binding. - const attachActiveLeaf = async (leafKey: string) => { - if (!activeSession || !leafKey || activeSession.leafKey === leafKey) return; + const attachActiveLeaf = async (leafKey: string, seatRole: string) => { + if (!activeSession || !activeSessionIsRunning || !leafKey || activeSession.leafKey === leafKey) return; setLeafAttachError(null); - const result = await attachSessionToLeaf(activeSession.id, leafKey); + const result = await attachSessionToLeaf(activeSession.id, leafKey, seatRole); if (result === "ok") { - sessionStore.getState().applyLeafAssignment(activeSession.id, leafKey); + sessionStore.getState().applyLeafAssignment(activeSession.id, leafKey, seatRole); notifySessionCatalogChanged("leaf", activeSession.id); } else if (result === "leaf-taken") { - setLeafAttachError("leaf already has a chat"); + setLeafAttachError(`leaf already has a ${seatRole} seat`); } else { setLeafAttachError("could not attach to leaf"); } @@ -334,6 +424,22 @@ export function Chats({ } }; + const cleanupLandedSessions = async (members: { id: string }[]) => { + setLandedCleanupNote(null); + const result = await cleanupLandedTerminalSessions(members.map((member) => member.id)); + if (!result) { + setLandedCleanupNote("cleanup failed"); + return; + } + for (const id of result.closedSessions) { + sessionStore.getState().setStatus(id, "terminated"); + sessionStore.getState().close(id); + } + notifySessionCatalogChanged("terminate"); + void hydrateTerminalSessionsFromCatalog(true, new Set(result.closedSessions)); + setLandedCleanupNote(`${result.closed} closed · ${result.skipped} skipped`); + }; + // The G1 command tree (L14): group the sidebar by claim + spawn-role provenance. Enclosures come // from the projection store (live-vs-landed truth); with no orchestration task and no leaf claims // this derives zero groups and the SessionList renders today's flat list unchanged. @@ -371,7 +477,7 @@ export function Chats({ ))} {selectedLifecycleId && activeSession?.lifecycleId === selectedLifecycleId ? ( task {selectedLifecycleId} - ) : selectedLifecycleId && activeSession && !activeSession.lifecycleId ? ( + ) : selectedLifecycleId && activeSessionIsRunning && activeSession && !activeSession.lifecycleId ? (
+ {bodyUnavailable ? ( +

Full task document details are unavailable; showing the available summary.

+ ) : null} - {doc.steps.length > 0 ? ( -
- -
- ) : null} {doc.objective ? (
{doc.objective} @@ -1247,6 +1344,21 @@ function TaskReader({ ); } +function mergeTaskDocumentBody(summary: TaskDocNode, body: Partial): TaskDocNode { + return { + ...summary, + ...body, + steps: body.steps ?? summary.steps, + requirements: body.requirements ?? summary.requirements, + codeExamples: body.codeExamples ?? summary.codeExamples, + decisions: body.decisions ?? summary.decisions, + openQuestions: body.openQuestions ?? summary.openQuestions, + references: body.references ?? summary.references, + subTasks: body.subTasks ?? summary.subTasks, + sections: body.sections ?? summary.sections, + }; +} + function Section({ title, children }: { title: string; children: ReactNode }) { return (
diff --git a/dashboard/src/panels/FlowTab.test.tsx b/dashboard/src/panels/FlowTab.test.tsx index b77defb0..3423b82c 100644 --- a/dashboard/src/panels/FlowTab.test.tsx +++ b/dashboard/src/panels/FlowTab.test.tsx @@ -21,7 +21,7 @@ describe("FlowTab canvas (unified l-01-agent-lifecycles)", () => { const { getByTestId, getByText } = render(); fireEvent.click(getByTestId("flow-nav-orchestrator")); expect(getByTestId("flow-tab").getAttribute("data-model")).toBe("orchestrator"); - expect(getByText(/the event loop, drawn on its biggest run/)).not.toBeNull(); + expect(getByText(/backend event loop, drawn on its biggest run/)).not.toBeNull(); expect(getByTestId("flow-nav-orchestrator").getAttribute("aria-checked")).toBe("true"); expect(getByTestId("flow-nav-router").getAttribute("aria-checked")).toBe("false"); fireEvent.click(getByTestId("flow-nav-comms")); @@ -61,6 +61,15 @@ describe("FlowTab canvas (unified l-01-agent-lifecycles)", () => { getByText(/task doc \(approved\) → branch \(intent\) → worktree \(only where something is built\)/), ).not.toBeNull(); expect(getByText(/⟁ chat is never a build route — small code work takes the minimal w-02 artifact/)).not.toBeNull(); + expect(getByText(/developer-facing session → roles\/architect.md/)).not.toBeNull(); + }); + + it("draws the architect as the developer-facing owner and decision relay", () => { + const { getByTestId, getByText } = render(); + expect(getByTestId("flow-tab").getAttribute("data-model")).toBe("architect"); + expect(getByText(/developer-facing owner, drawing board, decision relay/)).not.toBeNull(); + expect(getByText(/backend decision-item → present ONE item, record the durable ruling/)).not.toBeNull(); + expect(getByText(/roles expand horizontally into new chats/)).not.toBeNull(); }); it("encodes the agreed orchestration invariants on the drawn models", () => { @@ -69,12 +78,12 @@ describe("FlowTab canvas (unified l-01-agent-lifecycles)", () => { expect(getByText(/reshape master boundaries — NEVER interleave dispatch/)).not.toBeNull(); expect(getByText(/creates a BRANCH off main, nothing more/)).not.toBeNull(); expect( - getByText(/the ORCHESTRATOR decides by the packet-carried gateId \(own ambient identity/), + getByText(/the BACKEND ORCHESTRATOR decides by the packet-carried gateId \(own ambient identity/), ).not.toBeNull(); - // The escalation ladder lives on the comms drawing; the spirit test is ORCHESTRATOR-ONLY. + // The escalation ladder lives on the comms drawing; the spirit test is limited to bird's-eye seats. fireEvent.click(getByTestId("flow-nav-comms")); - expect(getByText(/escalation · worker → manager → orchestrator → developer/)).not.toBeNull(); - expect(getByText(/spirit test — ORCHESTRATOR-ONLY/)).not.toBeNull(); + expect(getByText(/escalation · worker → manager → orchestrator → architect → developer/)).not.toBeNull(); + expect(getByText(/spirit test — BIRD'S-EYE SEAT ONLY: backend orchestrator or architect/)).not.toBeNull(); // Managers escalate plan deltas instead of judging them, and reopen wrong deliverables. fireEvent.click(getByTestId("flow-nav-manager")); expect(getByText(/managers don't reshape plans \(no bird's-eye\)/)).not.toBeNull(); @@ -84,7 +93,7 @@ describe("FlowTab canvas (unified l-01-agent-lifecycles)", () => { getByText(/enclosure="" — the exact address integration enforcement matches the gate by/), ).not.toBeNull(); expect( - getByText(/the returned gateId rides the packet via inbox \+ push · the ORCHESTRATOR decides the gate by that id/), + getByText(/the returned gateId rides the packet via inbox \+ push · the BACKEND ORCHESTRATOR decides the gate by that id/), ).not.toBeNull(); }); @@ -111,7 +120,7 @@ describe("FlowTab canvas (unified l-01-agent-lifecycles)", () => { getByText(/⟁ a leaf naming neither existing surfaces nor a parent anchor → finding: unplannable as scoped — never a silent guess/), ).not.toBeNull(); expect( - getByText(/reader-not-mutator: the strategist drafts; the ORCHESTRATOR adopts it into durable task form \(decision-log entry\)/), + getByText(/reader-not-mutator: the strategist drafts; the BACKEND ORCHESTRATOR adopts it into durable task form \(decision-log entry\)/), ).not.toBeNull(); }); @@ -125,7 +134,7 @@ describe("FlowTab canvas (unified l-01-agent-lifecycles)", () => { ).not.toBeNull(); fireEvent.click(getByTestId("flow-nav-comms")); expect( - getByText(/⟁ quo-vadis — a high-blast-radius TRUTH \(answered wrong = big rewrites later\) goes to the developer IMMEDIATELY; presentation-grade \(2px vs 3px\) never does/), + getByText(/⟁ quo-vadis — a high-blast-radius TRUTH \(answered wrong = big rewrites later\) goes to the architect relay IMMEDIATELY; presentation-grade \(2px vs 3px\) never does/), ).not.toBeNull(); fireEvent.click(getByTestId("flow-nav-reviewer")); expect( @@ -147,11 +156,11 @@ describe("FlowTab canvas (unified l-01-agent-lifecycles)", () => { ).not.toBeNull(); }); - it("draws the designer as the hat the orchestrator pulls", () => { + it("draws the designer as the hat the architect pulls", () => { const { getByTestId, getByText } = render(); expect(getByTestId("flow-tab").getAttribute("data-model")).toBe("designer"); - expect(getByText(/the hat the orchestrator pulls/)).not.toBeNull(); - expect(getByText(/ORCHESTRATOR adversarially reviews the design/)).not.toBeNull(); + expect(getByText(/the hat the architect pulls/)).not.toBeNull(); + expect(getByText(/BACKEND ORCHESTRATOR adversarially reviews the design/)).not.toBeNull(); expect(getByText(/ask — never fill silently/)).not.toBeNull(); }); @@ -161,7 +170,7 @@ describe("FlowTab canvas (unified l-01-agent-lifecycles)", () => { expect( getByText(/verdicts are evidence, not decisions — requireReviewerVerdictAtSeams binds delegated seam decisions/), ).not.toBeNull(); - expect(getByText(/the ORCHESTRATOR at master-exit \(master-handover-approval\)/)).not.toBeNull(); + expect(getByText(/the BACKEND ORCHESTRATOR at master-exit \(master-handover-approval\)/)).not.toBeNull(); expect(getByText(/⟁ block\? → decomposable fix leaves/)).not.toBeNull(); }); }); diff --git a/dashboard/src/panels/LeafAttachPicker.test.tsx b/dashboard/src/panels/LeafAttachPicker.test.tsx index 22d96c2e..0d8e872c 100644 --- a/dashboard/src/panels/LeafAttachPicker.test.tsx +++ b/dashboard/src/panels/LeafAttachPicker.test.tsx @@ -54,6 +54,7 @@ describe("LeafAttachPicker drill-down", () => { const { getByTestId, getAllByTestId } = render(); fireEvent.click(getByTestId(TID)); // open → top level + fireEvent.click(getByTestId(`${TID}-role-worker`)); fireEvent.click(getByTestId(`${TID}-master`)); // drill into Operations Integration const engine = getAllByTestId(`${TID}-master`).find( (node) => node.getAttribute("data-master") === "engine", @@ -61,7 +62,22 @@ describe("LeafAttachPicker drill-down", () => { fireEvent.click(engine as HTMLElement); // drill into the NESTED master fireEvent.click(getByTestId(`${TID}-leaf`)); // its only leaf - expect(onPick).toHaveBeenCalledWith("repo/engine/E1"); + expect(onPick).toHaveBeenCalledWith("repo/engine/E1", "worker"); + }); + + it("requires an explicit role for an untyped hand-opened chat", () => { + const onPick = vi.fn(); + const { getByTestId } = render(); + + fireEvent.click(getByTestId(TID)); + fireEvent.click(getByTestId(`${TID}-master`)); + const leaf = getByTestId(`${TID}-leaf`) as HTMLButtonElement; + expect(leaf.disabled).toBe(true); + + fireEvent.click(getByTestId(`${TID}-role-reviewer`)); + expect(leaf.disabled).toBe(false); + fireEvent.click(leaf); + expect(onPick).toHaveBeenCalledWith("repo/ops/L5", "reviewer"); }); it("pre-drills to the in-context master so its leaves show immediately on open", () => { diff --git a/dashboard/src/panels/LeafAttachPicker.tsx b/dashboard/src/panels/LeafAttachPicker.tsx index fba1bcb9..cd954d0a 100644 --- a/dashboard/src/panels/LeafAttachPicker.tsx +++ b/dashboard/src/panels/LeafAttachPicker.tsx @@ -100,6 +100,48 @@ const row = css({ const rowText = css({ flex: "1", minWidth: "0", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }); const chevron = css({ flexShrink: 0, color: "amber" }); const emptyNote = css({ fontSize: "0.72rem", color: "muted", paddingInline: "0.45rem", paddingBlock: "0.3rem" }); +const rolePicker = css({ + display: "flex", + flexWrap: "wrap", + gap: "0.25rem", + padding: "0.35rem", + borderBottomWidth: "1px", + borderBottomStyle: "solid", + borderBottomColor: "grid", + marginBottom: "0.15rem", +}); +const roleButton = css({ + font: "inherit", + fontSize: "0.62rem", + letterSpacing: "0.05em", + textTransform: "uppercase", + color: "muted", + background: "transparent", + borderWidth: "1px", + borderStyle: "solid", + borderColor: "grid", + borderRadius: "2px", + paddingInline: "0.35rem", + paddingBlock: "0.15rem", + cursor: "pointer", + _selected: { color: "amber", borderColor: "amber" }, + _hover: { color: "ink" }, + _focusVisible: { outline: "1px solid token(colors.amber)", outlineOffset: "1px" }, +}); + +export const ATTACH_SEAT_ROLES = [ + "architect", + "orchestrator", + "strategist", + "designer", + "manager", + "worker", + "reviewer", + "curator", + "system-specialist", + "agent", + "chat", +] as const; export function LeafAttachPicker({ tree, @@ -108,17 +150,22 @@ export function LeafAttachPicker({ label = "Attach to leaf", testId = "attach-leaf-picker", align = "left", + seatRole, + roleOptions = ATTACH_SEAT_ROLES, }: { tree: TaskTreeNode[]; - onPick: (leafKey: string) => void; + onPick: (leafKey: string, seatRole: string) => void; contextMaster?: string; label?: string; testId?: string; align?: "left" | "right"; + seatRole?: string; + roleOptions?: readonly string[]; }) { const [open, setOpen] = useState(false); // The drilled path of master nodes (breadcrumb). Current level = the last node's children, or the roots. const [path, setPath] = useState([]); + const [selectedRole, setSelectedRole] = useState(seatRole); // The fixed-position anchor for the portaled menu, measured from the trigger (null until opened). const [coords, setCoords] = useState<{ top: number; left?: number; right?: number } | null>(null); const triggerRef = useRef(null); @@ -164,6 +211,7 @@ export function LeafAttachPicker({ if (!open) { // Opening pre-drills to the in-context master so its leaves show first ("pre-selection via master"). setPath(contextMaster ? findMasterPath(tree, contextMaster) : []); + setSelectedRole(seatRole); measure(); } setOpen((value) => !value); @@ -173,9 +221,10 @@ export function LeafAttachPicker({ const drillInto = (node: TaskTreeNode) => setPath((current) => [...current, node]); const back = () => setPath((current) => current.slice(0, -1)); const pick = (leafKey: string) => { + if (!selectedRole) return; setOpen(false); setPath([]); - onPick(leafKey); + onPick(leafKey, selectedRole); }; return ( @@ -201,6 +250,21 @@ export function LeafAttachPicker({ aria-label={label} data-testid={`${testId}-menu`} > +
+ {roleOptions.map((role) => ( + + ))} +
{here ? ( + ) : depth === 1 ? - {session.lifecycleId ? {session.lifecycleId} : null} + {session.lifecycleId ? {session.lifecycleId} : null} + {session.turnState ? {session.turnState} : null} {session.status && session.status !== "running" ? ( - {session.status} + {session.status} ) : null} +
+ + {group.kind === "landed" && onCleanupLanded ? ( + + ) : null} +
{open ? (
{gridListFor(group.sessions, `Open sessions — ${group.label}`)}
) : null} diff --git a/dashboard/src/panels/Terminal.tsx b/dashboard/src/panels/Terminal.tsx index 1e3c79ac..173abc22 100644 --- a/dashboard/src/panels/Terminal.tsx +++ b/dashboard/src/panels/Terminal.tsx @@ -85,9 +85,11 @@ function hasViewportScrollback(term: XtermTerminal): boolean { export function Terminal({ sessionId, onConnection, + readOnly = false, }: { sessionId: string; onConnection?: (conn: TerminalConnection | null) => void; + readOnly?: boolean; }) { const hostRef = useRef(null); const socketFactory = useContext(TerminalSocketContext); @@ -141,7 +143,7 @@ export function Terminal({ } else { const [input, nextLineRemainder] = applicationScrollInput(lines, applicationWheelLineRemainder); applicationWheelLineRemainder = nextLineRemainder; - if (input) conn.sendInput(input); + if (input && !readOnly) conn.sendInput(input); } } if (event.cancelable) event.preventDefault(); @@ -153,7 +155,7 @@ export function Terminal({ // wheel-to-arrow-history mapping. node.addEventListener("wheel", handleWheel, { passive: false, capture: true }); - const dataSub = term.onData((data) => conn.sendInput(data)); + const dataSub = readOnly ? null : term.onData((data) => conn.sendInput(data)); // Fit to the host + keep the PTY winsize in lockstep (the one known Mode B2 risk). A single fit // at mount sticks at the wrong size because the flex layout + the mono web font settle *after* // this effect runs — so re-fit on the next frame and once `document.fonts` is ready, on top of @@ -180,11 +182,11 @@ export function Terminal({ onConnRef.current?.(null); node.removeEventListener("wheel", handleWheel, { capture: true }); observer.disconnect(); - dataSub.dispose(); + dataSub?.dispose(); conn.dispose(); term.dispose(); }; - }, [sessionId, socketFactory]); + }, [readOnly, sessionId, socketFactory]); return
; } diff --git a/dashboard/src/panels/flowModels.ts b/dashboard/src/panels/flowModels.ts index 3e24f7e8..b2eee788 100644 --- a/dashboard/src/panels/flowModels.ts +++ b/dashboard/src/panels/flowModels.ts @@ -59,8 +59,8 @@ const ROUTER: FlowModel = { title: "router — one skill, one lifecycle per agent type", takeaway: "l-01-agent-lifecycles routes every session by EXACTLY three conditions: AR_SPAWN_ROLE set → " + - "run that role's lifecycle; else a role brief as first message → that role (the brief IS the " + - "session start); else the session is developer-facing → the ORCHESTRATOR. Edge cases are " + + "run that role's lifecycle; else a fresh-session role brief as first message → that role " + + "(the brief IS the session start); else the session is developer-facing → the ARCHITECT. Edge cases are " + "decided: an unresolvable role value falls through to the brief; a brief that never arrives " + "means announce-and-wait, never improvise. The invariant ladder binds every path: approved " + "task doc → branch (intent) → worktree only where something is built — and chat is never a " + @@ -73,18 +73,18 @@ const ROUTER: FlowModel = { lines: [ { line: "1 · AR_SPAWN_ROLE set → run roles/.md (designer = the same hat in another chair)" }, { line: "⟁ unresolvable value → fall through to condition 2 · no brief arrives → announce on the inbox and WAIT", junction: true }, - { line: "2 · first message is a role brief (templates/*-brief.md shape, or `ROLE BRIEF — `) → that role" }, - { line: "3 · otherwise: developer-facing session → roles/orchestrator.md (solo = the jobs with hats collapsed)" }, + { line: "2 · first message is a role brief in a fresh session → that role" }, + { line: "3 · otherwise: developer-facing session → roles/architect.md (solo = architect-only hat collapse)" }, ], }, - { kind: "divider", label: "— the event loop (orchestrator): route each event into a job —" }, + { kind: "divider", label: "— the owner loop (architect): route each developer event —" }, { kind: "rundown", - title: "jobs — Design · Portfolio · Orchestrate (+ research-only exit)", + title: "jobs — Design · Decision Relay · Spawn/Supervise (+ research-only exit)", lines: [ - { line: "no task doc for the ask → JOB D — pull the designer hat (no git surface)" }, - { line: "docs exist, coherence/order in question → JOB P — bulwark · reshape · the planner master (no git surface)" }, - { line: "approved series ready to implement → JOB O — super-branch INTENT (a branch, not a worktree) → dispatch" }, + { line: "developer shaping intent → Design — architect wears the designer hat (no git surface)" }, + { line: "backend decision item arrives → Decision Relay — one item, durable ruling back" }, + { line: "approved portfolio needs execution → spawn/supervise backend orchestrator or role seats" }, { line: "no code change → research-only exit — chat is the right medium" }, ], }, @@ -94,12 +94,49 @@ const ROUTER: FlowModel = { lines: [ { line: "task doc (approved) → branch (intent) → worktree (only where something is built)" }, { line: "⟁ chat is never a build route — small code work takes the minimal w-02 artifact", junction: true }, - { line: "hat-collapse: flat run → the orchestrator wears the manager hat · session scale → hands-on build" }, + { line: "hat-collapse: flat run → architect may wear backend hats; spawned seats never absorb another role brief" }, ], }, ], }; +// --- architect (developer-facing owner seat) ----------------------------------------------------- + +const ARCHITECT: FlowModel = { + id: "architect", + label: "Architect", + title: "architect — developer-facing owner, drawing board, decision relay", + takeaway: + "The architect is the developer-facing lifecycle. It owns design conversation, drawing-board " + + "rounds, decision pacing, and durable rulings back to backend seats. Backend churn happens in " + + "spawned role chats; developer-worthy backend questions arrive as one decision item at a time " + + "over the existing inbox.", + segments: [ + { kind: "start", label: "▸ developer-facing session — trust checkpoint + portfolio/decision orientation", next: "route", nextStatus: "current" }, + { + kind: "rundown", + title: "route the event", + lines: [ + { line: "intent / requirements / scope → wear roles/designer.md inline and author/reshape task docs" }, + { line: "backend decision-item → present ONE item, record the durable ruling, return decision-ruling" }, + { line: "approved execution → spawn backend orchestrator / strategist / manager / worker / reviewer as separate chats" }, + { line: "small unspawned work → solo/flat hat-collapse under the architect lifecycle" }, + ], + }, + { + kind: "rundown", + title: "decision-item relay — existing inbox, no new queue", + lines: [ + { line: "backend posts: decision · options · consequences · evidence refs" }, + { line: "architect presents one item at the developer's pace; underspecified items get one clarification row" }, + { line: "ruling lands in openQuestions / decision logs / notes, then returns as decision-ruling" }, + ], + }, + { kind: "node", phase: "spawn", tool: "spawn_agent_session", detail: "roles expand horizontally into new chats; sub-agents drill vertically for analysis only", next: "backend acts", nextStatus: "current" }, + { kind: "node", phase: "close", tool: "durable ruling", detail: "the backend waits for the ruling row before acting on the developer-facing decision" }, + ], +}; + // --- strategist (the sprint planner, spawn-first) ------------------------------------------------ const STRATEGIST: FlowModel = { @@ -109,13 +146,13 @@ const STRATEGIST: FlowModel = { takeaway: "A strategist run is a MANDATORY precondition for any orchestrated run — even a single master " + "gets the pass. Spawn-first by design: portfolio analysis is token-heavy, so it runs as its " + - "own process (the designer stays an inline hat; the strategist never is one). The orchestrator " + + "own process (the designer stays an architect inline hat; the strategist never is one). The backend orchestrator " + "dispatches it with a portfolio brief carrying REFS to durable state, never pasted state. It " + "runs an eight-phase method — inventory, two-sided touch surfaces, evidence-cited dependency " + "edges, blast-radius register, coherence sweep, ordering — and delivers the ORCHESTRATION " + "TASK: the sprint plan and the sprint scope. The plan is adversarially reviewed (plan-review " + - "catalog), then converges over drawing-board rounds with the developer. The strategist READS " + - "everything and MUTATES nothing — the orchestrator adopts the draft.", + "catalog), then converges over drawing-board rounds through the architect. The strategist READS " + + "everything and MUTATES nothing — the backend orchestrator adopts the draft.", segments: [ { kind: "start", label: "▸ spawned before an orchestrated run may begin (AR_SPAWN_ROLE=strategist) — portfolio brief = session start", next: "the mandatory pre-run gate", nextStatus: "proposed" }, { @@ -139,27 +176,27 @@ const STRATEGIST: FlowModel = { ], }, { kind: "node", phase: "deliver", tool: "the ORCHESTRATION TASK", detail: "sprint scope · dependency graph with per-edge evidence · blast-radius register · coherence findings · leaf moves · waves · re-evaluation triggers — drafted as a notes artifact", next: "plan review", nextStatus: "proposed" }, - { kind: "node", phase: "loop", tool: "plan review (plan-review catalog)", detail: "the portfolio three-party loop: owner = orchestrator · builder = strategist · reviewer refutes uncited edges, hunts missed shared surfaces, re-derives blast radii", rides: "portfolio loop", ridesNote: "⊘ verdicts are evidence — the orchestrator rules; 3 full rounds max, convergence required", next: "drawing-board rounds", nextStatus: "proposed" }, - { kind: "node", phase: "converge", tool: "drawing-board rounds (developer)", detail: "multi-round convergence EXPECTED — the orchestrator relays · ⟁ quo-vadis (masters heavily disagreeing) → straight to the developer", next: "adoption", nextStatus: "proposed" }, - { kind: "node", phase: "handover", tool: "orchestrator adopts the plan", detail: "reader-not-mutator: the strategist drafts; the ORCHESTRATOR adopts it into durable task form (decision-log entry)", next: "session ends", nextStatus: "proposed" }, + { kind: "node", phase: "loop", tool: "plan review (plan-review catalog)", detail: "the portfolio three-party loop: owner = backend orchestrator · builder = strategist · reviewer refutes uncited edges, hunts missed shared surfaces, re-derives blast radii", rides: "portfolio loop", ridesNote: "⊘ verdicts are evidence — the backend orchestrator rules; 3 full rounds max, convergence required", next: "drawing-board rounds", nextStatus: "proposed" }, + { kind: "node", phase: "converge", tool: "drawing-board rounds (architect/developer)", detail: "multi-round convergence EXPECTED — backend emits decision items · ⟁ quo-vadis (masters heavily disagreeing) → architect relay", next: "adoption", nextStatus: "proposed" }, + { kind: "node", phase: "handover", tool: "backend orchestrator adopts the plan", detail: "reader-not-mutator: the strategist drafts; the BACKEND ORCHESTRATOR adopts it into durable task form (decision-log entry)", next: "session ends", nextStatus: "proposed" }, { kind: "node", phase: "close", tool: "session ends", detail: "the orchestration task remains the sprint's standing scope" }, ], }; -// --- orchestrator (260703-ORCH) ---------------------------------------------------------------- +// --- orchestrator (spawned backend) -------------------------------------------------------------- const ORCHESTRATOR: FlowModel = { id: "orchestrator", label: "Orchestrator", - title: "orchestrator — the event loop, drawn on its biggest run (Job O)", + title: "orchestrator — backend event loop, drawn on its biggest run (Job O)", takeaway: - "The developer-facing lifecycle is an EVENT LOOP over durable portfolio state: every session " + + "The orchestrator is a spawned backend EVENT LOOP over durable portfolio state: every session " + "(resumption is the common case) opens with the trust checkpoint + PORTFOLIO ORIENTATION, then " + - "routes the event — Design (the hat), Portfolio, Orchestrate, or a research-only exit. Below is " + + "routes backend events from the architect, managers, workers, reviewers, or its own findings. Below is " + "the biggest shape, an orchestrated run: streamline → portfolio gate → super-branch INTENT (a " + "branch, not a worktree) → dependency-ordered dispatch → decide each master-handover gate → " + - "integrate per-edge in a transient worktree → super-exit review → developer handover. Human " + - "review concentrates at the SUPER gate; the orchestrator closes with grounded self-improvement " + + "integrate per-edge in a transient worktree → super-exit review → architect-mediated developer handover. Human " + + "review concentrates at the SUPER gate through the architect; the orchestrator closes with grounded self-improvement " + "proposals — never automated self-modification.", segments: [ { kind: "start", label: "▸ event: \"orchestrate these masters\" — after trust checkpoint + portfolio orientation", next: "profile-fit check", nextStatus: "proposed" }, @@ -169,7 +206,7 @@ const ORCHESTRATOR: FlowModel = { lines: [ { line: "profile-fit check — right harness/model/effort for the orchestrator job?" }, { line: "⟁ wrong profile? → spawn_agent_session(orchestrator) + conversation-handover packet → takeover", junction: true }, - { line: "seat = FIRST coordination leaf (task_doc, no enclosure) · chat attached by leaf id" }, + { line: "seat = spawned backend coordination leaf (task_doc, no enclosure) · chat attached by leaf id" }, ], }, { @@ -181,19 +218,19 @@ const ORCHESTRATOR: FlowModel = { { line: "reshape proposals — leaf moves (planning-status only) · foundation-master extraction · mixed masters first-or-last" }, { line: "⟁ interleaved leaf-level cross-deps? → reshape master boundaries — NEVER interleave dispatch", junction: true }, { line: "dependency DAG — must be expressible at MASTER granularity" }, - { line: "STRATEGIST pre-run — spawn-first, MANDATORY before any orchestrated run → the ORCHESTRATION TASK = sprint plan + scope (drawing-board rounds; the orchestrator adopts)" }, + { line: "STRATEGIST pre-run — spawn-first, MANDATORY before any orchestrated run → the ORCHESTRATION TASK = sprint plan + scope (drawing-board rounds through architect; backend orchestrator adopts)" }, ], }, - { kind: "node", phase: "portfolio · gate", tool: "portfolio plan gate", detail: "developer approves the reshaped portfolio + the orchestration task (sprint scope + DAG + dispatch order) — one wholesale review", rides: "plan-approval", ridesNote: "⊘ the streamlining output is a PROPOSAL — no silent rewrites of dev-accepted tasks", next: "create super integration branch", nextStatus: "proposed" }, + { kind: "node", phase: "portfolio · gate", tool: "portfolio plan gate", detail: "architect presents the reshaped portfolio + orchestration task for developer approval — one wholesale review", rides: "plan-approval", ridesNote: "⊘ the streamlining output is a PROPOSAL — no silent rewrites of accepted tasks", next: "create super integration branch", nextStatus: "proposed" }, { kind: "node", phase: "topology", tool: "super-branch INTENT", detail: "creates a BRANCH off main, nothing more — masters base off IT; the orchestrator worktree exists only per integration edge", next: "dispatch loop", nextStatus: "proposed" }, { kind: "divider", label: "↺ dependency-ordered dispatch loop — send out the next READY master's manager ↺" }, { kind: "node", phase: "dispatch", tool: "spawn_agent_session (manager)", detail: "manager-brief.md · AR_SPAWN_ROLE=manager · qualified leaf key · base = the CURRENT super tip", next: "monitor", nextStatus: "proposed" }, { kind: "node", phase: "monitor", tool: "monitor + steer", detail: "turn reports · nudges · escalations · spirit test on deltas · a wrong deliverable REOPENS its leaf (task_reopen) — never a redo sibling", next: "master handover", nextStatus: "proposed" }, - { kind: "node", phase: "handover", tool: "decide master-handover-approval", detail: "the manager RAISED it (wait=false) with the verdict attached — the ORCHESTRATOR decides by the packet-carried gateId (own ambient identity; owner-never-self-approves holds; the policy may require the verdict)", rides: "master-handover-approval", ridesNote: "⊘ seam 1 of 2 — happy path through the orchestrator; a handover it cannot honestly decide escalates to the developer", next: "integrate master → super", nextStatus: "proposed" }, + { kind: "node", phase: "handover", tool: "decide master-handover-approval", detail: "the manager RAISED it (wait=false) with the verdict attached — the BACKEND ORCHESTRATOR decides by the packet-carried gateId (own ambient identity; owner-never-self-approves holds; the policy may require the verdict)", rides: "master-handover-approval", ridesNote: "⊘ seam 1 of 2 — happy path through the backend orchestrator; an undecidable handover escalates to the architect", next: "integrate master → super", nextStatus: "proposed" }, { kind: "node", phase: "integrate", tool: "integrate master → super (C-11)", detail: "orchestrator WORKTREE with super as source · merge/carry-over · memory single-siding · ledger maps every commit", next: "↺ next ready master — until the DAG is drained", nextStatus: "proposed" }, { kind: "divider", label: "↓ DAG drained — the super branch holds the accumulated change set ↓" }, - { kind: "node", phase: "seam 2", tool: "super-exit adversarial review", detail: "wholesale verdict on the super branch: completion vs tasks · quality · onboarding-vs-code", rides: "super-exit seam", ridesNote: "⊘ adversarial review seam 2 of 2 — before the orchestrator hands over to the developer", next: "developer handover", nextStatus: "proposed" }, - { kind: "node", phase: "handover", tool: "developer review — super level", detail: "the developer's SINGLE review point (integrations below are orchestrator-delegated): visible-behavior-first in a REVIEWABLE ENVIRONMENT (the dashboard) with demo notes (what changed visibly), code second · ⟁ rejected? → decompose feedback into fix leaves ↺ reactive dispatch", rides: "integration-approval", next: "super → main PR", nextStatus: "proposed" }, + { kind: "node", phase: "seam 2", tool: "super-exit adversarial review", detail: "wholesale verdict on the super branch: completion vs tasks · quality · onboarding-vs-code", rides: "super-exit seam", ridesNote: "⊘ adversarial review seam 2 of 2 — before the backend orchestrator hands evidence to the architect", next: "architect handover", nextStatus: "proposed" }, + { kind: "node", phase: "handover", tool: "architect-mediated developer review — super level", detail: "the developer's SINGLE review point (integrations below are orchestrator-delegated): visible-behavior-first in a REVIEWABLE ENVIRONMENT (the dashboard) with demo notes (what changed visibly), code second · ⟁ rejected? → decompose feedback into fix leaves ↺ reactive dispatch", rides: "integration-approval", next: "super → main PR", nextStatus: "proposed" }, { kind: "node", phase: "land", tool: "super → main PR + carry-over", detail: "remote merge · memory carried to main-memory · push (git-workflow.md tail)", next: "close + propose", nextStatus: "current" }, { kind: "node", phase: "close", tool: "self-improvement report", detail: "did x/y/z · hit a/b/c · a,b solved on the spot · c PROPOSES this change — grounded in the accumulated backdrop", next: "lifecycle_end", nextStatus: "proposed" }, { kind: "node", phase: "close", tool: "lifecycle_end", detail: "terminal — durable notes/reports remain the record" }, @@ -207,15 +244,15 @@ const MANAGER: FlowModel = { label: "Manager", title: "manager — one master, leaf loop → master-exit handover", takeaway: - "Spawned by the orchestrator with a manager brief (its entire session start). Owns exactly one " + + "Spawned by the backend orchestrator (or architect in a flat run) with a manager brief (its entire session start). Owns exactly one " + "master: dispatches a fresh worker per leaf, reviews turn reports, decides DELEGATED leaf gates " + "(plan · closeout — the owning agent never self-approves), owns the leaf lifecycle end-to-end " + "(worktree_start → closeout → integrate → finalize), and REOPENS a leaf whose deliverable came " + "out wrong. At master exit it spawns the reviewer, then RAISES master-handover-approval with " + - "the verdict attached — the ORCHESTRATOR decides it. In a flat run the orchestrator wears this " + - "hat. Escalation: to the orchestrator, never straight to the developer.", + "the verdict attached — the BACKEND ORCHESTRATOR decides it. In a flat run the architect may wear this " + + "hat. Escalation: to the backend orchestrator, then architect if needed, never straight to the developer.", segments: [ - { kind: "start", label: "▸ spawned by the orchestrator — manager-brief.md pasted + submitted (the brief is the session start)", next: "seat", nextStatus: "proposed" }, + { kind: "start", label: "▸ spawned by backend orchestrator/architect — manager-brief.md pasted + submitted (the brief is the session start)", next: "seat", nextStatus: "proposed" }, { kind: "rundown", title: "seat · intake", @@ -223,7 +260,7 @@ const MANAGER: FlowModel = { { line: "seat = own coordination leaf (task_doc, no enclosure) · chat attached — the dev can walk in any time" }, { line: "read the master task_doc + leaf docs · order leaves (parallel where safe — C-11 reconcile absorbs a moved base)" }, { line: "default behavior stands: fulfill the task, fill small blanks — no extra creative-liberty prompting either way" }, - { line: "⟁ plan delta beyond filling blanks? → escalate to the ORCHESTRATOR — managers don't reshape plans (no bird's-eye)", junction: true }, + { line: "⟁ plan delta beyond filling blanks? → escalate to the BACKEND ORCHESTRATOR — managers don't reshape plans (no bird's-eye)", junction: true }, { line: "score each leaf's loop tier at dispatch — direct · builder-verified · full loop (the strategist's blast-radius register is the input; all-direct = workflow-free manager)" }, { line: "⟁ full-loop leaves: HARD cap 3 full rounds (delta-verifies don't count) · a non-shrinking round escalates NOW with the round history", junction: true }, ], @@ -236,7 +273,7 @@ const MANAGER: FlowModel = { { kind: "node", phase: "integrate", tool: "integrate leaf → master branch (C-11)", detail: "ff-only / replay per c-09 · a durable gate here is integration-approval — HUMAN-pinned · ↺ next leaf until done", next: "master-exit review", nextStatus: "current" }, { kind: "divider", label: "↓ all leaves landed on the master integration branch ↓" }, { kind: "node", phase: "seam 1", tool: "master-exit adversarial review", detail: "spawn reviewer (AR_SPAWN_ROLE=reviewer) · verdict: completion · quality · onboarding-vs-code · ⟁ blocked? → fix leaves ↺", rides: "master-exit seam", next: "handover to orchestrator", nextStatus: "proposed" }, - { kind: "node", phase: "handover", tool: "RAISE master-handover-approval (wait=false) + packet", detail: "gate raised without blocking (wait=false) with enclosure=\"\" — the exact address integration enforcement matches the gate by — and the verdict attached (evidenceRefs) · the returned gateId rides the packet via inbox + push · the ORCHESTRATOR decides the gate by that id", rides: "master-handover-approval", ridesNote: "⊘ delegable, never human-pinned — human review concentrates at the super gate", next: "seat stays reachable", nextStatus: "proposed" }, + { kind: "node", phase: "handover", tool: "RAISE master-handover-approval (wait=false) + packet", detail: "gate raised without blocking (wait=false) with enclosure=\"\" — the exact address integration enforcement matches the gate by — and the verdict attached (evidenceRefs) · the returned gateId rides the packet via inbox + push · the BACKEND ORCHESTRATOR decides the gate by that id", rides: "master-handover-approval", ridesNote: "⊘ delegable, never human-pinned — human review concentrates at the super gate through architect", next: "seat stays reachable", nextStatus: "proposed" }, { kind: "node", phase: "close", tool: "seat remains", detail: "chat + coordination leaf stay reachable until the series retires" }, ], }; @@ -253,7 +290,7 @@ const WORKER: FlowModel = { "same-pass onboarding, gets the checks green, and ends at the MANDATORY turn report. It owns no " + "lifecycle machinery — closeout, integration, finalization, and gates belong to the owning " + "seat — and it never git-commits. A plan delta beyond blank-filling escalates one rung to the " + - "owning seat; the spirit test lives with the orchestrator, not here.", + "owning seat; the spirit test lives with the backend orchestrator or architect owner, not here.", segments: [ { kind: "start", label: "▸ spawned on a leaf — worker-brief.md pasted + submitted (the brief is the session start)", next: "orient", nextStatus: "current" }, { @@ -283,8 +320,8 @@ const COMMS: FlowModel = { "Three channels compose: the inbox is the durable, dashboard-visible QUEUE; stdin push is the " + "DELIVERY for AR-hosted sessions (no poll loops); turn-report artifacts are the REPORTING that " + "survives compaction and session death. Nudges ride trustworthy inactivity signals (the reform " + - "series). Escalation ladders worker → manager → orchestrator → developer with no skipping, and " + - "the spirit test decides autonomy at every level. One handover-packet schema serves master " + + "series). Escalation ladders worker → manager → backend orchestrator → architect → developer with no skipping, and " + + "decision-item relay keeps developer-worthy backend questions moving one at a time. One handover-packet schema serves master " + "handover, role takeover, and worker respawn.", segments: [ { @@ -293,7 +330,7 @@ const COMMS: FlowModel = { lines: [ { line: "inbox — operator_inbox generalized to agent→agent addressing; every message durable + dashboard-visible" }, { line: "stdin push — echo-confirmed PTY injection delivers queued messages to hosted sessions (poll = fallback only)" }, - { line: "turn-report artifacts — templated, durable; the orchestrator's own reports are the most important in the system" }, + { line: "turn-report artifacts — templated, durable; backend orchestrator reports carry the whole-series picture" }, { line: "chats — every seat has a leaf-attached chat; the developer can walk into any conversation at any level" }, ], }, @@ -312,14 +349,14 @@ const COMMS: FlowModel = { { kind: "divider", label: "— the escalation ladder — no level skipped —" }, { kind: "rundown", - title: "escalation · worker → manager → orchestrator → developer", + title: "escalation · worker → manager → orchestrator → architect → developer", lines: [ - { line: "each level resolves within its own view first; what reaches the developer is decided by the quo-vadis test" }, + { line: "each level resolves within its own view first; what reaches the developer is relayed by the architect after the quo-vadis test" }, { line: "workers/managers: fulfill the task, fill small blanks — plan deltas ESCALATE; no spirit judgment below the bird's-eye" }, { line: "loops: HARD cap 3 full rounds (delta-verifies close rounds) · convergence rule — a round that does not shrink the finding set escalates NOW, with the full round history attached" }, - { line: "⟁ quo-vadis — a high-blast-radius TRUTH (answered wrong = big rewrites later) goes to the developer IMMEDIATELY; presentation-grade (2px vs 3px) never does", junction: true }, - { line: "⟁ spirit test — ORCHESTRATOR-ONLY: within the spirit of accepted plans → act + decision-log entry", junction: true }, - { line: "⟁ against the spirit → JOINT decision with the developer (the unanticipated-wrench case)", junction: true }, + { line: "⟁ quo-vadis — a high-blast-radius TRUTH (answered wrong = big rewrites later) goes to the architect relay IMMEDIATELY; presentation-grade (2px vs 3px) never does", junction: true }, + { line: "⟁ spirit test — BIRD'S-EYE SEAT ONLY: backend orchestrator or architect, within the spirit of accepted plans → act + decision-log entry", junction: true }, + { line: "⟁ against the spirit → architect-mediated developer decision (the unanticipated-wrench case)", junction: true }, ], }, { kind: "node", phase: "handover", tool: "handover packet (one schema)", detail: "master-complete handover · role takeover (profile-fit) · worker respawn — request, decisions, constraints, links, open questions", next: "receiver onboards from state, not transcript", nextStatus: "proposed" }, @@ -331,17 +368,17 @@ const COMMS: FlowModel = { const DESIGNER: FlowModel = { id: "designer", label: "Designer", - title: "designer — the hat the orchestrator pulls", + title: "designer — the hat the architect pulls", takeaway: "Task design is its own job, worn as a HAT: it cannot sit in a coordination leaf because the " + - "task is what it exists to create — the orchestrator runs roles/designer.md inline, at the " + + "task is what it exists to create — the architect runs roles/designer.md inline, at the " + "front of the pipeline AND mid-flight, helping the developer think a master through — the tasks/AGENTS.md doctrine (meta-questioning, reframe before execution, " + - "evidence-first) given a distinct, optimized shape. It shares the orchestrator's bird's-eye " + + "evidence-first) given a distinct, optimized shape. It shares the architect's bird's-eye " + "toolkit (route indexes · onboarding · grepai · cgc · blast radius) but is SCOPED to one master — " + "collisions with other or FUTURE masters can slip. That residual risk is owned downstream: at " + - "portfolio streamlining the ORCHESTRATOR doubles as the designer's adversarial reviewer.", + "portfolio streamlining the BACKEND ORCHESTRATOR doubles as the designer's adversarial reviewer.", segments: [ - { kind: "start", label: "▸ developer intent, no task doc yet — the orchestrator pulls the designer hat (Job D)", next: "co-design loop", nextStatus: "current" }, + { kind: "start", label: "▸ developer intent, no task doc yet — the architect pulls the designer hat", next: "co-design loop", nextStatus: "current" }, { kind: "rundown", title: "co-design loop · the tasks/AGENTS.md doctrine, as a job", @@ -355,7 +392,7 @@ const DESIGNER: FlowModel = { { kind: "node", phase: "reframe", tool: "reframe agreement", detail: "the developer agrees the frame before structure exists", rides: "reframe", ridesNote: "⊘ material scope/intent/sequencing changes are played back and WAIT for confirmation (tasks/AGENTS.md)", next: "task_doc authoring", nextStatus: "proposed" }, { kind: "node", phase: "author", tool: "task_doc authoring", detail: "master + leaves · requirements · steps · code examples (w-02 shape) · leaves scoped around routes/areas", next: "declare the limits", nextStatus: "current" }, { kind: "node", phase: "limits", tool: "designer limits note", detail: "master-scoped bird's-eye: cross-master and FUTURE-master collisions can slip — declared on the doc, never hidden", next: "handover → portfolio", nextStatus: "proposed" }, - { kind: "node", phase: "handover", tool: "join the portfolio", detail: "at streamlining the ORCHESTRATOR adversarially reviews the design — planned-vs-planned AND planned-vs-past", next: "(orchestrator · portfolio phase)", nextStatus: "proposed" }, + { kind: "node", phase: "handover", tool: "join the portfolio", detail: "at streamlining the BACKEND ORCHESTRATOR adversarially reviews the design — planned-vs-planned AND planned-vs-past", next: "(backend orchestrator · portfolio phase)", nextStatus: "proposed" }, ], }; @@ -367,12 +404,12 @@ const REVIEWER: FlowModel = { title: "adversarial reviewer — verdicts are evidence, not decisions", takeaway: "Spawned at exactly two seams: MASTER-EXIT (before a manager hands its integration branch to the " + - "orchestrator) and SUPER-EXIT (before the orchestrator hands the super branch to the developer). " + + "orchestrator) and SUPER-EXIT (before the backend orchestrator hands the super branch to the architect/developer). " + "It reviews the accumulated change set through three lenses — completion vs task docs, code " + "quality per tools.md, and onboarding-vs-code — fanning out sub-agents that write durable " + "reports. Its verdict is a templated artifact that attaches to the handover gate as JUDGE " + - "evidence; the decider decides — the ORCHESTRATOR at master-exit (master-handover-approval), " + - "the DEVELOPER at super-exit. A blocking verdict must decompose into fix leaves — findings, " + + "evidence; the decider decides — the BACKEND ORCHESTRATOR at master-exit (master-handover-approval), " + + "the architect/developer review at super-exit. A blocking verdict must decompose into fix leaves — findings, " + "never prose complaints. The same seat serves every three-party loop's review (leaf full-loop, " + "portfolio plan review) with its type's criteria catalog — criteria are never made up on the spot.", segments: [ @@ -397,4 +434,4 @@ const REVIEWER: FlowModel = { ], }; -export const FLOW_MODELS: FlowModel[] = [ROUTER, DESIGNER, STRATEGIST, ORCHESTRATOR, MANAGER, WORKER, REVIEWER, COMMS]; +export const FLOW_MODELS: FlowModel[] = [ROUTER, ARCHITECT, DESIGNER, STRATEGIST, ORCHESTRATOR, MANAGER, WORKER, REVIEWER, COMMS]; diff --git a/dashboard/src/panels/notes-reader/NotesReaderViewer.test.tsx b/dashboard/src/panels/notes-reader/NotesReaderViewer.test.tsx index a39a0168..76982b93 100644 --- a/dashboard/src/panels/notes-reader/NotesReaderViewer.test.tsx +++ b/dashboard/src/panels/notes-reader/NotesReaderViewer.test.tsx @@ -30,6 +30,13 @@ function stubNotesApi(notes: NoteEntry[], contents: Record "fetch", vi.fn(async (url: string) => { const params = new URLSearchParams(url.split("?")[1] ?? ""); + if (url.startsWith("/api/task-document")) { + const docPath = params.get("path") ?? ""; + const doc = + dashboardStore.getState().analytics?.taskDocuments.find((item) => item.docPath === docPath) ?? + masterDoc(); + return { ok: true, status: 200, json: async () => doc } as unknown as Response; + } if (url.startsWith("/api/notes/list")) { const payload = { repo: params.get("repo"), master: params.get("master"), notes, truncated }; return { ok: true, status: 200, json: async () => payload } as unknown as Response; diff --git a/dashboard/src/types/projection.ts b/dashboard/src/types/projection.ts index 76a615c1..d0aea046 100644 --- a/dashboard/src/types/projection.ts +++ b/dashboard/src/types/projection.ts @@ -230,6 +230,7 @@ export interface TaskDocNode { stepsTotal: number; currentStep?: string; docPath: string; + bodyRevision?: string; createdAt?: string; ageSeconds?: number; steps: TaskStepNode[]; @@ -432,6 +433,23 @@ export interface ServingBuild { commit?: string; } +// The supervisor sweep's self-liveness tick (260707-HFX2-L2 R5, serving/supervisor_heartbeat.py): +// "the watcher must be code AND watched" (#15). Injected app-side onto /api/state and the SSE +// snapshot at RESPONSE time (deliberately volatile — never gates the projection's ETag change +// revision), so `lastTickAt`/`ageSeconds` are as-of-request, not as-of-last-projection-change. +// `lastTickAt: null` means the supervisor has never ticked in this workspace (dashboard/supervisor +// autostart is opt-in) — that is NOT the same as `stale: true`, and the header renders nothing for +// it rather than a false alarm. +export interface SupervisorHeartbeat { + lastTickAt: string | null; + ageSeconds: number | null; + staleCutoffSeconds: number; + stale: boolean; + pendingInboxCount: number; + redeliverableInboxCount: number; + lastSweepDurationSeconds: number | null; +} + export interface WorkspaceProjection { version: number; generatedAt: string; @@ -446,4 +464,5 @@ export interface WorkspaceProjection { metrics: Metrics; analytics: Analytics; servingBuild?: ServingBuild; // app-injected on the wire only — see ServingBuild + supervisorHeartbeat?: SupervisorHeartbeat; // app-injected on the wire only — see SupervisorHeartbeat } diff --git a/docs/design/observable-lifecycle.md b/docs/design/observable-lifecycle.md index 2b96ba50..a4851337 100644 --- a/docs/design/observable-lifecycle.md +++ b/docs/design/observable-lifecycle.md @@ -206,6 +206,38 @@ whole log is **pruned** (§1.5). Approval facts that must outlive a click are co the durable work record that consumes them (for example the closeout contract/ledger state); the gate row itself is still interaction data. +### 2.4.1 Operator inbox storm recovery runbook + +Use this only when the dashboard reports a stale supervisor heartbeat and the forward +signal shows a large operator-inbox backlog that is not draining. + +1. Save live work first. Inspect hosted sessions in the terminal catalog and do not stop + a dashboard daemon that owns live agent ptys until their visible work has been saved, + reported, or otherwise handed off. +2. Stop or restart through the normal dashboard daemon control path. Avoid raw process + kills while there are live ptys unless the daemon is already proven wedged and work has + been saved. +3. Quarantine the inbox log, never delete it: + + ```text + mv logs/observer/workspace/operator-inbox.jsonl \ + logs/observer/workspace/operator-inbox.jsonl..bak + ``` + +4. Inspect the `.bak` file offline. Rows whose latest snapshot is already + `ladder-resolved` are terminal and may stay only in the backup. Rows for seats that + are still live must be re-posted or allowed to be consumed by that live agent; do not + rewrite them as terminal. +5. For rows at the terminal ladder rung whose target seat is retired or absent from the + terminal catalog, park them as ladder-resolved in a reconstructed inbox log or leave + them quarantined if no live consumer remains. Keep the `.bak` as the audit trail. +6. Start the dashboard cleanly. Confirm `/api/state` shows a fresh + `supervisorHeartbeat`, bounded `pendingInboxCount`/`redeliverableInboxCount`, and a + recent `lastSweepDurationSeconds`. +7. Compact through the normal inbox compaction path after recovery. Do not hand-edit + away pending live-seat rows; pending/unacked live rows are protected until acked or + ladder-resolved. + ### 2.5 The observer and its projections `_tool_payload` (observed) · signal tools (declared) · gate actions (approved) · diff --git a/docs/features.md b/docs/features.md index f865dbd8..5b45250a 100644 --- a/docs/features.md +++ b/docs/features.md @@ -295,7 +295,7 @@ worktree and thrown away with it. Memory is only as good as the discipline that keeps it honest, and that discipline is the second half of the product. Sessions route by role through one skill (`l-01-agent-lifecycles`): a spawned agent follows the role brief -that spawned it, and a developer-facing session is the **orchestrator**, whose +that spawned it, and a developer-facing session is the **architect**, whose lifecycle runs ```text diff --git a/docs/getting-started.md b/docs/getting-started.md index dec6b5da..10cbfdf9 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -190,7 +190,7 @@ Providers are optional — memory, onboarding, drift, and task workflows all wor ## Start Working -Sessions route by role through the `l-01-agent-lifecycles` skill: a developer-facing session is the **orchestrator** and runs the orchestrator lifecycle (request → trust-checkpoint → reframe-research → decide → build → close); a spawned agent follows the role brief that spawned it. For normal tasks the agent should: +Sessions route by role through the `l-01-agent-lifecycles` skill: a developer-facing session is the **architect** and runs the architect lifecycle (request → trust-checkpoint → reframe-research → decide → build → close); spawned backend orchestrators and other role seats follow the role briefs that spawned them. For normal tasks the agent should: 1. resolve the repository context with `c-08-ar-coordination-context-resolver` 2. run `c-02-memory-quality-control` before planning against onboarding diff --git a/docs/install/claude-code.md b/docs/install/claude-code.md index 7a81e387..110b09cb 100644 --- a/docs/install/claude-code.md +++ b/docs/install/claude-code.md @@ -73,15 +73,15 @@ If `AR_SPAWN_ROLE` is set, or your first user message is a role brief from an orchestrating agent: **ignore this notice entirely — your brief is your session start.** -Otherwise you are the developer-facing session, i.e. the **orchestrator**: read +Otherwise you are the developer-facing session, i.e. the **architect**: read `ar-coordination/AGENTS.md`, then run your lifecycle at -`skills/l-01-agent-lifecycles/roles/orchestrator.md` — trust checkpoint before +`skills/l-01-agent-lifecycles/roles/architect.md` — trust checkpoint before relying on memory, `read_ar_files` (paired source+onboarding) until the build decision, retrieval-strategy tally as evidence, notify-and-stop at every developer hand-off. ``` -The directive is orchestrator-exclusive by design: a spawned role's session +The directive is architect-exclusive by design: a spawned role's session start is the role brief its orchestrating agent compiled, so the hook tells spawned sessions to ignore it in one sentence and addresses only the developer-facing session. diff --git a/docs/llms.txt b/docs/llms.txt index 13a1bbec..d49f7e0c 100644 --- a/docs/llms.txt +++ b/docs/llms.txt @@ -297,7 +297,7 @@ worktree and thrown away with it. Memory is only as good as the discipline that keeps it honest, and that discipline is the second half of the product. Sessions route by role through one skill (`l-01-agent-lifecycles`): a spawned agent follows the role brief -that spawned it, and a developer-facing session is the **orchestrator**, whose +that spawned it, and a developer-facing session is the **architect**, whose lifecycle runs ```text diff --git a/docs/reference/harnesses.md b/docs/reference/harnesses.md index b491ade6..946c5165 100644 --- a/docs/reference/harnesses.md +++ b/docs/reference/harnesses.md @@ -18,7 +18,7 @@ Each harness is described by an entry with: | Field | Meaning | | --- | --- | -| `id` | The stable identifier callers use (`harness="claude"`). Never a command. | +| `id` | The stable identifier settings and dashboard launches use (`"claude"`). Never a command. | | `name` | Display name (dashboard buttons). | | `command` | The executable probed on `PATH` for detection. | | `argv` | The exact launch command array, e.g. `["claude"]`. Fixed server-side. | @@ -33,10 +33,10 @@ The curated defaults live in `mcp/src/agents_remember/serving/harnesses.py`: | id | argv | modelFlag | effortFlag (values) | session effort (command) | | --- | --- | --- | --- | --- | | `claude` (Claude Code) | `["claude"]` | `--model` | `--effort` (`low`, `medium`, `high`, `xhigh`, `max`) | `ultracode` → `/effort ultracode` | -| `codex` (Codex) | `["codex"]` | — (env-only) | — (env-only) | — | +| `codex` (Codex) | `["codex"]` | `--model` | `--config model_reasoning_effort={value}` (`none`, `minimal`, `low`, `medium`, `high`, `xhigh`) | — | | `pi` (Pi.dev) | `["pi"]` | — (env-only) | — (env-only) | — | -**Env-only** means: the model/effort knobs still ride the spawn env as +**Env-only** (currently Pi.dev) means: the model/effort knobs still ride the spawn env as `AR_SPAWN_MODEL`/`AR_SPAWN_EFFORT` (session-start visibility), but nothing is put on the command line and no effort vocabulary is enforced. Growing a mapping for a builtin is a one-line registry edit — or a settings override, @@ -75,9 +75,11 @@ dispatch, naming the known set and pointing here — never a crash. ## The Role Knobs, Parameter By Parameter Role knobs live in `orchestration.roles.` (flat defaults) and -`orchestration.rolesPerLevel..` (per-level overrides); a spawning -seat may also pass any of them explicitly to `spawn_agent_session`. Three -layers with distinct validation postures: +`orchestration.rolesPerLevel..` (per-level overrides). For ordinary +agent-driven spawns, these settings are the sole developer-controlled spend +surface: `spawn_agent_session` callers declare `env.AR_SPAWN_ROLE` and `level`, +not `harness`/`model`/`effort` or direct launch/session spend controls. Three +settings layers have distinct validation postures: ### Layer 1 — validated enum knobs @@ -85,7 +87,7 @@ layers with distinct validation postures: | --- | --- | --- | | `harness` | Selects the harness entry (argv comes from it). | Must be a known id: builtin or `orchestration.harnesses`-defined. Checked at settings load AND at dispatch. | | `model` | The harness's `modelFlag` on the launch argv, e.g. `--model opus`; also rides env as `AR_SPAWN_MODEL`. | Model names are NOT enum-validated (they evolve faster than any registry). A settings-defined harness with no `modelFlag` refuses the knob with guidance (`model-invalid`). | -| `effort` | Per value: a flag value rides the harness's `effortFlag` on the argv; a session value rides a post-launch session command; either way it rides env as `AR_SPAWN_EFFORT`. | Validated at DISPATCH against the harness's vocabulary (flag values ∪ session values). Unknown values refuse (`effort-invalid`) naming the harness and BOTH sets. Mapping-less builtins (codex, pi) accept anything env-only; a mapping-less settings-defined harness refuses with guidance. | +| `effort` | Per value: a flag value rides the harness's `effortFlag` on the argv; a session value rides a post-launch session command; either way it rides env as `AR_SPAWN_EFFORT`. | Validated at DISPATCH against the harness's vocabulary (flag values ∪ session values). Unknown values refuse (`effort-invalid`) naming the harness and BOTH sets. Codex accepts exactly `none|minimal|low|medium|high|xhigh`; Pi.dev remains env-only. | Why dispatch-time effort validation exists: the installed claude CLI accepts `--effort low|medium|high|xhigh|max` and **warns-then-silently-degrades** on @@ -106,17 +108,24 @@ tool payload). Example: `["--dangerously-skip-permissions"]`. ### Layer 3 — session free-form: `sessionCommands` and `promptKeywords` - `sessionCommands`: a list of lines, each pasted into the freshly spawned - session as its OWN echo-confirmed paste and submitted, BEFORE the dispatch + session as its OWN entry and submitted, BEFORE the dispatch brief — the vehicle for any session-level harness feature (a session-vocabulary effort like claude's `ultracode` is delivered this way - automatically, ahead of your own session commands). Never validated; - recorded in spawn provenance. + automatically, ahead of configured session commands). After the id-bearing brief binds the + spawn-cwd harness JSONL, every command requires its command record plus non-error stdout; + a missing/errored command alone is re-issued and re-checked. Never caller-validated; recorded + in spawn provenance. - `promptKeywords`: a list of keywords prepended as the first line of the dispatch-brief paste (session modes the model interprets, e.g. a prompt keyword like `ultracode`). Never validated; recorded in spawn provenance. -Delivery order at spawn: **launch argv → session commands (effort vehicle -first, then yours) → the brief paste (keywords first)**. +Delivery order at spawn: **launch argv → settings session commands (effort +vehicle first, then configured commands) → the brief paste (keywords first)**. +The brief's unique id binds the harness session log; that id+path are catalog provenance and all +submitted delivery acceptance comes from the bound log, never terminal-screen vocabulary. +After a calibrated log-absence window, Enter may be re-pressed once. A re-paste is allowed only +after a bounded pane check verifies that the prior payload is absent; visible payload/chip evidence +is cleared and verified absent before replacement, or the delivery fails without appending. ### The dispatch level: `level` and `orchestration.rolesPerLevel` @@ -140,10 +149,21 @@ makes that expressible (ruling 2026-07-07T08:15): manager dispatching leaf seats passes `leaf`, the master-seam reviewer `master`, portfolio/end-to-end seats `portfolio`. The level override deep-merges over the flat default at leaf-key granularity (unset fields -inherit; lists replace). Full resolution chain: - -**explicit args > repo-local level override > global level override > -repo-local role default > global role default > spawn preference/detection.** +inherit; lists replace). Full spend resolution chain: + +**repo-local level override > global level override > repo-local role default > +global role default > spawn preference/detection.** + +Legacy caller-supplied `harness`, `model`, `effort`, `launch_args`, +`prompt_keywords`, `session_commands`, `env.AR_SPAWN_MODEL`, or +`env.AR_SPAWN_EFFORT` values return `spend-override-unsupported` before any +session is spawned. Harness-native spend/endpoint env keys for the built-in +Claude/Anthropic and Codex/OpenAI families, such as `ANTHROPIC_MODEL`, +`ANTHROPIC_BASE_URL`, `OPENAI_MODEL`, `OPENAI_BASE_URL`, and API key/project +selectors, refuse the same way because caller `env` is otherwise seeded into +the spawned harness process. Move those choices into `orchestration.roles`, +`orchestration.rolesPerLevel`, `orchestration.spawn`, or +`orchestration.harnesses`. The resolution walk for the reviewer economics above (role riding the spawn env as `AR_SPAWN_ROLE=reviewer`): @@ -162,9 +182,9 @@ payload). A harness is launchable only when its `command` resolves on `PATH` (`shutil.which`). `GET /api/harnesses` reports the effective set with -per-harness detection; harness resolution order when `spawn_agent_session` -gets no explicit id: role knobs (level-merged) > `orchestration.spawn.harness` -preference > the first detected effective-registry harness. +per-harness detection; `spawn_agent_session` harness resolution order is role +knobs (level-merged) > `orchestration.spawn.harness` preference > the first +detected effective-registry harness. ## Refusals (never crashes) @@ -176,17 +196,19 @@ All pre-spawn, nothing mutated: | `harness-not-detected` | known id whose command is not on `PATH` | the id (and the settings source when a configured preference caused it) | | `effort-invalid` | effort outside the harness's vocabulary, or any effort for a mapping-less settings-defined harness | the harness, BOTH value sets (flag + session), and the launchArgs/sessionCommands guidance | | `model-invalid` | model knob for a settings-defined harness with no `modelFlag` | the harness and the declare-or-launchArgs guidance | +| `spend-override-unsupported` | ordinary caller supplied removed spend fields (`harness`, `model`, `effort`, launch/session controls, `AR_SPAWN_MODEL`/`AR_SPAWN_EFFORT`, or harness-native spend/endpoint env keys such as `ANTHROPIC_MODEL` / `OPENAI_BASE_URL`) | the removed fields and the settings families that own them | | `level-invalid` | dispatch level outside `leaf|master|portfolio` | the value and the valid set | | `leaf-taken` | the target leaf already has a running same-role session | the owning session (server-arbitrated, never overridden) | ## Worked Example: Teaching The System `hermes` -Suppose you use a TUI agent called `hermes` that nobody registered. A bare -`spawn_agent_session(harness="hermes")` refuses: +Suppose you use a TUI agent called `hermes` that nobody registered. A role +configured with `"harness": "hermes"` refuses at settings load until the harness +is declared: -> unknown harness: 'hermes'; known harnesses: [claude, codex, pi]. Define a -> new one under orchestration.harnesses in the agentic settings (see -> docs/reference/harnesses.md). +> orchestration.roles.worker.harness must be a harness registry id or an +> orchestration.harnesses-defined id (claude, codex, pi), got 'hermes' +> (see docs/reference/harnesses.md). Teach it in the GLOBAL agentic settings (`/system/settings.json`): @@ -215,8 +237,8 @@ Now `spawn_agent_session(env={"AR_SPAWN_ROLE": "worker"})` launches `hermes --tui --model h-1 --reasoning high` (knobs also riding the env), and `effort: "turbo"` refuses naming `[low, high]`. Had you declared NO `effortFlag`/vocabulary, the effort knob itself would refuse with guidance — -declare the mapping or pass the value through `launchArgs` — explicit over -guessing a flag that might mean something else. A repo-local +declare the mapping or carry a deliberate raw argv through settings-owned +`launchArgs` — explicit over guessing a flag that might mean something else. A repo-local `/system/settings.json` may override any leaf of the entry (e.g. a different `argv`) for that repo's dispatches. diff --git a/docs/reference/settings-json.md b/docs/reference/settings-json.md index 059c8c62..0e35a245 100644 --- a/docs/reference/settings-json.md +++ b/docs/reference/settings-json.md @@ -132,6 +132,16 @@ watch settings internally. "dashboard": { "autoStart": false, "port": 8765 + }, + "providerDegradation": { + "enabled": true, + "failSafeEnabled": true, + "memoryDegradedRatio": 0.8, + "memoryCriticalRatio": 0.92 + }, + "retirement": { + "autoLandOnIntegration": true, + "autoLandOnFinalize": true } } ``` @@ -211,6 +221,30 @@ next session's boot. Daemon state and logs live under `--stop` manage the same daemon from the CLI. Unknown `dashboard` keys are rejected. +`providerDegradation` (optional) configures the provider-only degradation +detector that runs over the central provider metrics log. Defaults enable the +detector and the critical fail-safe. The detector evaluates memory pressure, +restart-loop signals, watcher/index lag, probe latency when a metrics row +carries it, and setup-failure streak rows when present. State transitions write +durable degradation state/events under `/logs/observer/providers/` +and post `degradation-alert` inbox rows to the orchestrator and active managers. +At `critical`, `failSafeEnabled: true` runs the always-legal `provider_watchers +stop` path. Threshold keys are `memoryDegradedRatio`, `memoryCriticalRatio`, +`degradedSamples`, `criticalSamples`, `healthySamples`, +`watcherLagDegradedCommits`, `watcherLagCriticalCommits`, +`watcherLagDegradedMinutes`, `watcherLagCriticalMinutes`, `probeDegradedMs`, +`probeCriticalMs`, `setupFailureDegradedStreak`, +`setupFailureCriticalStreak`, and `recentSampleLimit`. Unknown +`providerDegradation` keys are rejected. + +`retirement` (optional) configures the auto-land hooks for worktree-backed tmux +seats. `autoLandOnIntegration` and `autoLandOnFinalize` (both default `true`) +gate whether integrating a leaf or finalizing a master marks spent hosted seats +as landed/archive. Landing leaves transcripts inspectable and non-active; it +does not close tmux. The legacy `autoRetireOnIntegration` and +`autoRetireOnFinalize` keys are accepted as aliases for existing settings files. +Unknown `retirement` keys are rejected. + `orchestration` in the authority file is LEGACY territory (260703-L13): the agentic family moved to the global agentic settings file documented below. For one migration cycle the authority file may still carry @@ -287,8 +321,12 @@ Read cadence above). ### orchestration.roles, orchestration.rolesPerLevel `orchestration.roles.` overrides a role file's knob block per role -(`orchestrator`, `designer`, `strategist`, `manager`, `worker`, `reviewer`). -Precedence: role-file defaults < global settings < repo-local settings. The +(`architect`, `orchestrator`, `designer`, `strategist`, `manager`, `worker`, `curator`, +`system-specialist`, `reviewer`). +Precedence: role-file defaults < global settings < repo-local settings. These +settings are the sole developer-controlled spend surface for ordinary spawned +seats; `spawn_agent_session` callers declare role and level, not +`harness`/`model`/`effort` or direct launch/session spend controls. The knobs come in a THREE-LAYER model (260703-L16; the full spawn-surface manual with every parameter, vocabulary, and refusal is **`docs/reference/harnesses.md`**): @@ -297,13 +335,15 @@ with every parameter, vocabulary, and refusal is `claude`/`codex`/`pi` or an `orchestration.harnesses`-defined one), `model`, `effort`. The spawn path seeds `model`/`effort` into the spawn env (`AR_SPAWN_MODEL`/`AR_SPAWN_EFFORT`) AND applies them onto the harness - launch argv per-harness (claude: `--model`/`--effort`; a mapping-less - harness stays env-only). `effort` is validated per-harness at DISPATCH: + launch argv per-harness (claude: `--model`/`--effort`; codex: `--model` plus + `--config model_reasoning_effort=`; only mapping-less harnesses stay env-only). + `effort` is validated per-harness at DISPATCH: unknown values refuse loudly naming the harness and its valid sets. 2. **`launchArgs`** (list of strings) — appended VERBATIM to the harness launch argv. Never validated; recorded in spawn provenance. 3. **`sessionCommands`** (list of strings; each line pasted + submitted into - the fresh session BEFORE the brief) and **`promptKeywords`** (list of + the fresh session BEFORE the brief, then verified by command entry + stdout in the log bound by + the brief's unique id) and **`promptKeywords`** (list of strings prepended as the first line of the dispatch-brief paste). Never validated; recorded in spawn provenance. @@ -326,10 +366,15 @@ the per-LEVEL agent sets the L12 doctrine promises: `leaf` | `master` | knob-override shape. A level override deep-merges over the flat `orchestration.roles` default at leaf-key granularity (harness inherited unless overridden; arrays replace). The dispatcher declares its level via -`spawn_agent_session(level=...)`, default `leaf`. Full resolution chain: -explicit args > repo-local level override > global level override > -repo-local role default > global role default > detection-gated default. The -resolved level rides spawn provenance (`spawnLevel`/`spawnLevelSource`). +`spawn_agent_session(level=...)`, default `leaf`. Full spend resolution chain: +repo-local level override > global level override > repo-local role default > +global role default > detection-gated default. The resolved level rides spawn +provenance (`spawnLevel`/`spawnLevelSource`). Legacy caller-supplied +`harness`/`model`/`effort`, direct `launch_args`/`prompt_keywords`/ +`session_commands`, `env.AR_SPAWN_MODEL`/`env.AR_SPAWN_EFFORT`, or +harness-native spend/endpoint env keys for the built-in Claude/Anthropic and +Codex/OpenAI families refuse with `spend-override-unsupported` before +spawning; move those choices into these settings families. ### orchestration.harnesses @@ -344,6 +389,26 @@ Detection still gates dispatch; an id known nowhere refuses loudly pointing at the manual. Schema, semantics, and a worked add-`hermes` example: `docs/reference/harnesses.md`. +### orchestration.supervisor + +`orchestration.supervisor` configures the deterministic supervisor sweep. All +fields are optional; an empty block keeps the safe defaults. + +| Field | Default | Notes | +| --- | --- | --- | +| `enabled` | `true` | Turns the sweep loop on or off. | +| `intervalSeconds` | `10` | Sweep cadence. | +| `staleCutoffSeconds` | `60` | Age after which the supervisor heartbeat is reported stale. | +| `redeliverRateLimitSeconds` | store default (`900`) | Per-row floor between redelivery attempts. Values below `900` seconds are refused. | +| `signalCooldownSeconds` | `900` | Minimum interval between repeated pane/seat-liveness owner signals for the same target, leaf, finding kind, and detail. Values below `900` seconds are refused. | +| `redeliverBudget` | `1` | Maximum inbox redelivery attempts per sweep. Harness-log confirmation is synchronous and bounded per input, so backlogs drain across sweeps without multiplying that wait inside one heartbeat tick. | +| `escalationBudget` | `250` | Maximum escalation-rung emissions per sweep. A large backlog of rung-due rows is spread across sweeps (rung readiness is level-triggered, so deferred rows re-fire on the next sweep) rather than doing O(backlog) synchronous owner pastes + `escalation.rung` event appends in one sweep. Positive integer. | + +`enabled: false` is the emergency kill switch for the supervisor loop. During the +2026-07-09 redelivery-cadence incident the global coordinator settings disabled +the supervisor until the 15-minute redelivery and signal-cooldown fix landed and +passed smoke. + ### orchestration.concurrency, orchestration.spawn `orchestration.concurrency` caps parallel orchestration fan-out: @@ -351,9 +416,9 @@ at the manual. Schema, semantics, and a worked add-`hermes` example: omitted means uncapped). The caps are doctrine input for the spawning seats. `orchestration.spawn.harness` names the default harness `spawn_agent_session` -uses when the caller passes none and no role knob supplies one. Resolution -order at the spawn seam: explicit argument > role knobs (level-merged) > -repo-local settings > global settings > detection-gated default (the first +uses when no role/level knob supplies one. Resolution order at the spawn seam: +role knobs (level-merged) > repo-local settings > global settings > +detection-gated default (the first effective-registry harness found on PATH; the repo-local layer is selected by the qualified leaf key's repository segment). Values are validated against the effective harness ids (builtin + `orchestration.harnesses`) and gated by @@ -363,9 +428,12 @@ argv is definable only in the explicit `orchestration.harnesses` family. ```jsonc "orchestration": { "roles": { + "architect": { "harness": "claude", "effort": "high" }, "orchestrator": { "harness": "claude", "effort": "high" }, "strategist": { "effort": "ultracode" }, // session-vocabulary value → "/effort ultracode" post-launch "reviewer": { "harness": "claude", "model": "sonnet", "effort": "high" }, + "system-specialist": { "harness": "claude", "model": "fable", "effort": "high" }, + "curator": { "harness": "codex", "effort": "medium" }, "worker": { "harness": "codex", "effort": "medium" } }, "rolesPerLevel": { diff --git a/docs/reference/skills.md b/docs/reference/skills.md index 451c7bef..2fa3c6c8 100644 --- a/docs/reference/skills.md +++ b/docs/reference/skills.md @@ -26,7 +26,7 @@ package skill folder. The pre-push hook runs | Skill | Purpose | | --- | --- | -| `l-01-agent-lifecycles` | The agent lifecycles, one per agent type under one roof. Routes every session by exactly three conditions (spawn-role env → role brief → otherwise orchestrator), carries the minimal lifecycle frame (the six lifecycle signals every session shares), and houses the self-contained per-role lifecycles (orchestrator · designer · strategist · manager · worker · adversarial reviewer) plus the report-template library and the reviewer criteria catalogs. The orchestrator lifecycle (request → trust-checkpoint → reframe-research → decide → build → close) owns the research-only exit and the build-mode decision; a developer-facing session IS the orchestrator. | +| `l-01-agent-lifecycles` | The agent lifecycles, one per agent type under one roof. Routes every session by exactly three conditions (spawn-role env → fresh role brief → otherwise architect), carries the minimal lifecycle frame (the six lifecycle signals every session shares), and houses the self-contained per-role lifecycles (architect · backend orchestrator · designer · strategist · manager · worker · curator · system-specialist · adversarial reviewer) plus the report-template library and the reviewer criteria catalogs. The architect lifecycle (request → trust-checkpoint → reframe-research → decide → build → close) owns the developer-facing research-only exit and build decision; backend orchestrators run as spawned seats. | | `w-02-light-task-workflow` | Durable one-page task plan with approval gate and live checklist; escalates to a master + light sub-task series for larger work. | ## Core Skills diff --git a/docs/workflows.md b/docs/workflows.md index 45657af7..34a8ba1b 100644 --- a/docs/workflows.md +++ b/docs/workflows.md @@ -2,11 +2,11 @@ Sessions route by role through the **`l-01-agent-lifecycles`** skill — one lifecycle per agent type, selected by exactly three conditions: a spawn-role environment variable runs that role's -lifecycle, otherwise a role brief as the first message runs that role, otherwise the session is -developer-facing and runs the **orchestrator** lifecycle. The orchestrator's phase axis is +lifecycle, otherwise a fresh-session role brief as the first message runs that role, otherwise the +session is developer-facing and runs the **architect** lifecycle. The architect's phase axis is request → trust-checkpoint → reframe-research → decide → build → close. The job type (bug / feature / triage / research) is a lens during reframe-research, not a gate. -The only task-format decision is the orchestrator lifecycle's build-mode step. +The only task-format decision is the architect lifecycle's build-mode step. ## Shared Discipline @@ -14,13 +14,13 @@ Every build keeps these rules: 1. Resolve the active context with `c-08-ar-coordination-context-resolver`. 2. Run drift detection before planning against onboarding. -3. Wait for developer approval before implementation (the orchestrator lifecycle's plan gate). +3. Wait for developer approval before implementation (the architect lifecycle's plan gate). 4. Update onboarding only after approved changes, live per completed plan-section. 5. Run the checks listed in the resolved memory layer's `system/tools.md` when available. ## Build Modes -At `decide`, the orchestrator lifecycle picks one of: +At `decide`, the architect lifecycle picks one of: ### Research-only exit diff --git a/examples/mcp/settings.example.json b/examples/mcp/settings.example.json index 87883c0c..c681be0c 100644 --- a/examples/mcp/settings.example.json +++ b/examples/mcp/settings.example.json @@ -20,5 +20,15 @@ "dashboard": { "autoStart": false, "port": 8765 + }, + "providerDegradation": { + "enabled": true, + "failSafeEnabled": true, + "memoryDegradedRatio": 0.8, + "memoryCriticalRatio": 0.92 + }, + "retirement": { + "autoLandOnIntegration": true, + "autoLandOnFinalize": true } } diff --git a/mcp/pyproject.toml b/mcp/pyproject.toml index 58fa1f8b..af3f30f9 100644 --- a/mcp/pyproject.toml +++ b/mcp/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "agents-remember-mcp" -version = "3.0.0rc3" +version = "3.0.0rc4" description = "Model Context Protocol server for Agents Remember." readme = "README.md" requires-python = ">=3.11" diff --git a/mcp/src/agents_remember/benchmarks/runner_modules/execution.py b/mcp/src/agents_remember/benchmarks/runner_modules/execution.py index 5bb39888..bca2b9d8 100644 --- a/mcp/src/agents_remember/benchmarks/runner_modules/execution.py +++ b/mcp/src/agents_remember/benchmarks/runner_modules/execution.py @@ -288,6 +288,7 @@ def maybe_prepare_case( skill_exposure_mode: str, force_clone: bool, provider_timeout: int, + allowed_provider_ids: tuple[str, ...] | None = None, ) -> None: if skip_prepare: return @@ -299,6 +300,7 @@ def maybe_prepare_case( force_clone=force_clone, provider_timeout=provider_timeout, provider_ids=selected_provider_ids(case, prompt_id=prompt_id, variant_id=variant_id), + allowed_provider_ids=allowed_provider_ids, ) @@ -443,6 +445,7 @@ def run_case( force_clone: bool, provider_timeout: int, codex_sandbox: str = CODEX_BENCHMARK_SANDBOX, + allowed_provider_ids: tuple[str, ...] | None = None, ) -> Path: maybe_prepare_case( benchmarks_root, @@ -454,6 +457,7 @@ def run_case( skill_exposure_mode=skill_exposure_mode, force_clone=force_clone, provider_timeout=provider_timeout, + allowed_provider_ids=allowed_provider_ids, ) output_root = create_output_root(benchmarks_root, case, dry_run) task_batches, default_jobs = benchmark_task_batches( diff --git a/mcp/src/agents_remember/benchmarks/runner_modules/mcp_registration.py b/mcp/src/agents_remember/benchmarks/runner_modules/mcp_registration.py index 524be19b..0999c783 100644 --- a/mcp/src/agents_remember/benchmarks/runner_modules/mcp_registration.py +++ b/mcp/src/agents_remember/benchmarks/runner_modules/mcp_registration.py @@ -36,6 +36,51 @@ def benchmark_agents_config_path(workspace_root: Path) -> Path: return workspace_root / CODEX_HARNESS_DIR / "config.toml" +def disarm_stale_benchmark_registrations( + benchmarks_root: Path, allowed_provider_ids: tuple[str, ...] | None +) -> list[str]: + """Narrow persisted benchmark MCP settings to the live authority set (R1, review B3). + + The registration written at prepare time persists in the workspace and acts + as the AUTHORITY file for every session later booted there — the one place + the fleet kill-switch cannot reach, because those servers re-read *this* + file. Any prepare/run pass therefore sweeps ALL workspace registrations and + strips providers the live authority no longer enables. ``None`` (no + authority context, direct script use) leaves files untouched. Returns the + rewritten paths. + """ + if allowed_provider_ids is None: + return [] + allowed = set(allowed_provider_ids) + base = benchmarks_root / "workspaces" + candidates = sorted( + set(base.glob(f"*/{CODEX_HARNESS_DIR}/mcp/{BENCHMARK_MCP_SETTINGS_NAME}")) + | set(base.glob(f"*/*/{CODEX_HARNESS_DIR}/mcp/{BENCHMARK_MCP_SETTINGS_NAME}")) + ) + rewritten: list[str] = [] + for settings_path in candidates: + try: + data = json.loads(settings_path.read_text(encoding="utf-8")) + except (OSError, json.JSONDecodeError): + continue + providers = data.get("providers") + if not isinstance(providers, dict): + continue + kept = {pid: cfg for pid, cfg in providers.items() if pid in allowed} + if set(kept) == set(providers): + continue + data["providers"] = kept + settings_path.write_text( + json.dumps(data, indent=2, sort_keys=True) + "\n", encoding="utf-8" + ) + rewritten.append(settings_path.as_posix()) + print( + f"Disarmed stale benchmark registration {settings_path}: providers narrowed " + f"to {sorted(kept) or '(none)'} per the live MCP authority (containment R1)" + ) + return rewritten + + def benchmark_mcp_source_path() -> Path | None: value = os.environ.get(BENCHMARK_MCP_SOURCE_ENV) if not value: @@ -272,6 +317,11 @@ def prepare_configured_providers( settings_path=settings_path, timeout=provider_timeout, dry_run=dry_run, + # Hermetic-cold benchmarks NEED the synchronous timeout-bounded + # graph build: with the fallback default off (260707-HFX-L2), + # `cgc watch` would self-index asynchronously and agents would + # query a half-built graph errorlessly (review L2/B4). + cgc_refresh_fallback=True, ) ) finally: diff --git a/mcp/src/agents_remember/benchmarks/runner_modules/models.py b/mcp/src/agents_remember/benchmarks/runner_modules/models.py index 5f2be46f..14a7faf2 100644 --- a/mcp/src/agents_remember/benchmarks/runner_modules/models.py +++ b/mcp/src/agents_remember/benchmarks/runner_modules/models.py @@ -46,6 +46,13 @@ class BenchmarkPrepareRequest: skill_exposure_mode: str = "copy" force_clone: bool = False provider_timeout: int = 1800 + # Containment R1 (260707-HFX-L1): the live MCP authority's provider ids. The + # MCP controllers always pass this; manifest-requested providers outside the + # set are skipped (and reported), never armed or launched. None = no + # authority context (direct script use) and is FAIL-CLOSED by the consumer + # (workspace.filter_benchmark_provider_ids) unless + # AR_BENCHMARK_ALLOW_UNFILTERED_PROVIDERS=1 — the explicit developer act. + allowed_provider_ids: tuple[str, ...] | None = None @dataclass(frozen=True) @@ -63,3 +70,5 @@ class BenchmarkRunRequest: force_clone: bool = False provider_timeout: int = 1800 codex_sandbox: str = CODEX_BENCHMARK_SANDBOX + # See BenchmarkPrepareRequest.allowed_provider_ids (containment R1). + allowed_provider_ids: tuple[str, ...] | None = None diff --git a/mcp/src/agents_remember/benchmarks/runner_modules/services.py b/mcp/src/agents_remember/benchmarks/runner_modules/services.py index 5a004897..cfee095b 100644 --- a/mcp/src/agents_remember/benchmarks/runner_modules/services.py +++ b/mcp/src/agents_remember/benchmarks/runner_modules/services.py @@ -16,6 +16,9 @@ select_cases, selected_provider_ids, ) +from agents_remember.benchmarks.runner_modules.mcp_registration import ( + disarm_stale_benchmark_registrations, +) from agents_remember.benchmarks.runner_modules.models import ( BenchmarkPrepareRequest, BenchmarkRunRequest, @@ -36,6 +39,9 @@ def prepare_benchmarks(request: BenchmarkPrepareRequest) -> dict[str, Any]: cases = select_cases(load_cases(benchmarks_root), request.target, request.case_id) def run_prepare() -> None: + # Review B3: stale workspace registrations are the one authority file the + # fleet kill-switch cannot reach — every touch of the benchmarks sweeps them. + disarm_stale_benchmark_registrations(benchmarks_root, request.allowed_provider_ids) for case in cases: prepare_case( benchmarks_root, @@ -45,6 +51,7 @@ def run_prepare() -> None: force_clone=request.force_clone, provider_timeout=request.provider_timeout, provider_ids=selected_provider_ids(case), + allowed_provider_ids=request.allowed_provider_ids, ) _, messages = _capture_messages(run_prepare) @@ -84,6 +91,7 @@ def run_selected_cases( cases: list[Any], ) -> list[Path]: output_roots: list[Path] = [] + disarm_stale_benchmark_registrations(benchmarks_root, request.allowed_provider_ids) for case in cases: output_roots.append( run_case( @@ -99,6 +107,7 @@ def run_selected_cases( force_clone=request.force_clone, provider_timeout=request.provider_timeout, codex_sandbox=request.codex_sandbox, + allowed_provider_ids=request.allowed_provider_ids, ) ) return output_roots diff --git a/mcp/src/agents_remember/benchmarks/runner_modules/workspace.py b/mcp/src/agents_remember/benchmarks/runner_modules/workspace.py index fab8a46b..8b1d4dba 100644 --- a/mcp/src/agents_remember/benchmarks/runner_modules/workspace.py +++ b/mcp/src/agents_remember/benchmarks/runner_modules/workspace.py @@ -1,5 +1,6 @@ from __future__ import annotations +import os from pathlib import Path from typing import Any @@ -181,6 +182,45 @@ def prepare_memory_repo( return memory_repo +UNFILTERED_PROVIDERS_ENV = "AR_BENCHMARK_ALLOW_UNFILTERED_PROVIDERS" + + +def filter_benchmark_provider_ids( + case_id: str, + provider_ids: tuple[str, ...], + allowed_provider_ids: tuple[str, ...] | None, +) -> tuple[str, ...]: + """Containment R1 (260707-HFX-L1): the case manifest is not launch authority. + + Providers outside the live MCP authority set are neither persisted into the + workspace registration (which arms every later session booted there) nor + launched by prepare_configured_providers. ``None`` (no authority context, + i.e. direct script use below the MCP layer) is FAIL-CLOSED too — review + finding B4: an implicit default must not be the bypass. The explicit + developer act is the ``AR_BENCHMARK_ALLOW_UNFILTERED_PROVIDERS=1`` env var. + """ + if allowed_provider_ids is None: + if os.environ.get(UNFILTERED_PROVIDERS_ENV) == "1": + return provider_ids + if provider_ids: + print( + f"Skipping benchmark providers {list(provider_ids)} for {case_id}: no MCP " + "authority context (containment R1); direct script runs must set " + f"{UNFILTERED_PROVIDERS_ENV}=1 to arm providers without an authority filter" + ) + return () + allowed = set(allowed_provider_ids) + skipped = tuple(p for p in provider_ids if p not in allowed) + kept = tuple(p for p in provider_ids if p in allowed) + if skipped: + print( + f"Skipping benchmark providers {list(skipped)} for {case_id}: not enabled " + "in the live MCP authority settings (containment R1); the workspace registration " + "carries only authority-enabled providers" + ) + return kept + + def prepare_case( benchmarks_root: Path, case: BenchmarkCase, @@ -189,7 +229,11 @@ def prepare_case( force_clone: bool = False, provider_timeout: int = 1800, provider_ids: tuple[str, ...] = (), + allowed_provider_ids: tuple[str, ...] | None = None, ) -> None: + provider_ids = filter_benchmark_provider_ids( + case.case_id, provider_ids, allowed_provider_ids + ) repository = case.repository root = workspace_root(benchmarks_root, case) source_only_root = source_only_workspace_root(benchmarks_root, case) diff --git a/mcp/src/agents_remember/controllers/benchmark_tools.py b/mcp/src/agents_remember/controllers/benchmark_tools.py index 9ae009d1..2f080658 100644 --- a/mcp/src/agents_remember/controllers/benchmark_tools.py +++ b/mcp/src/agents_remember/controllers/benchmark_tools.py @@ -10,7 +10,7 @@ from agents_remember.benchmarks import runner as benchmark_runner from agents_remember.controllers._guards import require_within_coordination from agents_remember.install.assets import packaged_source_root -from agents_remember.mcp.config import McpRuntimeConfig +from agents_remember.mcp.config import McpRuntimeConfig, reload_provider_authority def codex_benchmark_prepare_tool( @@ -36,6 +36,7 @@ def codex_benchmark_prepare_tool( skill_exposure_mode=skill_exposure_mode, force_clone=force_clone, provider_timeout=provider_timeout, + allowed_provider_ids=_live_provider_ids(config), ) ) @@ -92,6 +93,7 @@ def codex_benchmark_run_tool( force_clone=force_clone, provider_timeout=provider_timeout, codex_sandbox=codex_sandbox, + allowed_provider_ids=_live_provider_ids(config), ) ) result["codexExecutable"] = codex_executable @@ -99,6 +101,16 @@ def codex_benchmark_run_tool( return result +def _live_provider_ids(config: McpRuntimeConfig) -> tuple[str, ...]: + """The live on-disk authority's provider ids (containment R1, 260707-HFX-L1). + + Benchmark provider synthesis is filtered to this set; a fail-closed read + error yields an empty set, so no manifest can arm providers the developer + has disabled on disk. + """ + return tuple(sorted(reload_provider_authority(config).providers)) + + @contextmanager def _benchmark_root_context(config: McpRuntimeConfig, value: str | None) -> Iterator[Path]: if value: diff --git a/mcp/src/agents_remember/controllers/provider_tools.py b/mcp/src/agents_remember/controllers/provider_tools.py index 23bc354e..e9733662 100644 --- a/mcp/src/agents_remember/controllers/provider_tools.py +++ b/mcp/src/agents_remember/controllers/provider_tools.py @@ -11,7 +11,9 @@ from agents_remember.controllers._guards import require_repo from agents_remember.mcp.config import ( DEFAULT_DOCKER_CONTROL_SECONDS, + ConfigError, McpRuntimeConfig, + require_provider_launch_authority, ) from agents_remember.providers import lifecycle_service from agents_remember.providers.current_state import write_current_provider_state @@ -60,6 +62,12 @@ def provider_watchers_tool( raise ValueError( "action must be status, start, stop, restart, invalidate-indexes, or shutdown-all" ) + if action in {"start", "restart", "invalidate-indexes"}: + # Containment R1 (260707-HFX-L1): launching (and rebuilding, which + # launches indexers) runs on the LIVE on-disk authority, never the boot + # snapshot. Disabled-on-disk refuses loudly; stop/status/shutdown-all + # stay available — stopping is always legal. + config = require_provider_launch_authority(config, operation=f"provider_watchers {action}") if action == "restart": # Stop then start only. 'start' re-attaches the watchers and lets them pick up changes # via their incremental scan; it never passes --force, so indexes are preserved. @@ -212,6 +220,8 @@ def grepai_search_tool( operation="grepai_search", dry_run=dry_run, timeout=timeout, + launch_capable=True, + launch_capable_provider="grepai-memory", settings_path_override=_worktree_settings_override(target), run=lambda service_config: lifecycle_service.run_grepai_lifecycle( service_config, @@ -266,6 +276,8 @@ def grepai_trace_tool( operation="grepai_trace", dry_run=dry_run, timeout=timeout, + launch_capable=True, + launch_capable_provider="grepai-memory", settings_path_override=_worktree_settings_override(target), run=lambda service_config: lifecycle_service.run_grepai_lifecycle( service_config, @@ -399,6 +411,8 @@ def cgc_visualize_tool( operation="cgc_visualize", dry_run=dry_run, timeout=timeout, + launch_capable=True, + launch_capable_provider="codegraphcontext-code", settings_path_override=_worktree_settings_override(target), run=lambda service_config: lifecycle_service.run_cgc_lifecycle( service_config, @@ -427,6 +441,8 @@ def _cgc_run_tool( operation=operation, dry_run=dry_run, timeout=timeout, + launch_capable=True, + launch_capable_provider="codegraphcontext-code", settings_path_override=_worktree_settings_override(target), run=lambda service_config: lifecycle_service.run_cgc_lifecycle( service_config, @@ -697,7 +713,24 @@ def _provider_operation_result( timeout: int | None = None, run: ProviderLifecycleRunner, settings_path_override: Path | None = None, + launch_capable: bool = False, + launch_capable_provider: str | None = None, ) -> dict[str, Any]: + if launch_capable: + # Containment R1 (260707-HFX-L1): query/run ops spin one-shot runner + # containers, and a worktree's persisted settings file is stamped + # enabled:true forever — neither is launch authority. The live on-disk + # providers map gates both; with an override the worktree's own stack + # settings still drive the run, but only under an armed authority. + # Review note: the SPECIFIC provider must be armed, not just any. + live = require_provider_launch_authority(config, operation=operation) + if launch_capable_provider and launch_capable_provider not in live.providers: + raise ConfigError( + f"{operation} refused: provider {launch_capable_provider!r} is not enabled " + f"in the on-disk authority settings ({config.config_path}) (containment R1)" + ) + if settings_path_override is None: + config = live # When a worktree target resolves, run against its already-persisted lifecycle # settings (do NOT delete that file). Otherwise write a temp workspace settings # file and clean it up. diff --git a/mcp/src/agents_remember/controllers/worktree_tools.py b/mcp/src/agents_remember/controllers/worktree_tools.py index 4a1c23b7..4a45b677 100644 --- a/mcp/src/agents_remember/controllers/worktree_tools.py +++ b/mcp/src/agents_remember/controllers/worktree_tools.py @@ -2,6 +2,7 @@ from __future__ import annotations +from pathlib import Path from typing import Any from agents_remember.controllers._guards import require_repo, require_within_coordination @@ -9,12 +10,18 @@ DEFAULT_PROVIDER_SETUP_SECONDS, McpRuntimeConfig, RepositoryScope, + reload_provider_authority, ) from agents_remember.observer.ambient import AmbientLifecycle, ambient +from agents_remember.observer.events import now_iso from agents_remember.observer.save_gate import coerce_save_decision from agents_remember.observer.ulid import new_ulid from agents_remember.providers.settings import write_lifecycle_settings +from agents_remember.serving.landing import land_seats_for_leaf +from agents_remember.serving.seat_events import log_landed_event +from agents_remember.serving.terminal_catalog import TerminalCatalog, terminal_catalog_path from agents_remember.worktrees import git_worktree_manager +from agents_remember.worktrees.worktree_contract import load_contract def worktree_start_tool( @@ -40,7 +47,16 @@ def worktree_start_tool( # worktree_start promotes the active lifecycle to persistent (design §1.3); with # no active lifecycle, mint a fresh anchor so the contract always carries one. lifecycle_id = amb.current.id if amb is not None and amb.current is not None else new_ulid() - settings_path = None if skip_provider_setup else write_lifecycle_settings(config) + # Containment R1 (260707-HFX-L1): the on-disk authority file — not the boot + # snapshot — decides whether provider setup may launch. An empty (or + # unreadable: fail-closed) live providers map skips setup outright; the + # worktree itself is still created. Launch runs on the LIVE providers map. + authority = None if skip_provider_setup else reload_provider_authority(config) + settings_path = ( + write_lifecycle_settings(authority.apply(config)) + if authority is not None and authority.providers and authority.error is None + else None + ) provider_setup_config = ( None if settings_path is None @@ -82,6 +98,18 @@ def worktree_start_tool( result: dict[str, Any] | None = None try: result = _worktree_result("worktree_start", git_worktree_manager.start_result(args)) + if authority is not None and ( + authority.error is not None or (config.providers and not authority.providers) + ): + # Surface the veto so a stale-snapshot session sees WHY setup was + # skipped instead of silently diverging from its boot config. + veto: dict[str, Any] = { + "source": str(authority.source_path), + "bootSnapshotProviders": sorted(config.providers), + } + if authority.error is not None: + veto["error"] = authority.error + result["providersAuthority"] = veto _attribute_start(amb, result, repo_id) return result finally: @@ -252,8 +280,9 @@ def worktree_integrate_tool( ledger_commit_message: str = "", dry_run: bool = False, ) -> dict[str, Any]: + confined_contract = require_within_coordination(config, contract_path, "contract_path") args = git_worktree_manager.WorktreeArgs( - contract_path=require_within_coordination(config, contract_path, "contract_path"), + contract_path=confined_contract, strategy=strategy, approved=not dry_run, ledger_commit_message=ledger_commit_message, @@ -263,7 +292,16 @@ def worktree_integrate_tool( # exact delegated approval the master-handover channel produces. gate_policy=config.orchestration.gate_policy, ) - return _worktree_result("worktree_integrate", git_worktree_manager.integrate_result(args)) + result = _worktree_result("worktree_integrate", git_worktree_manager.integrate_result(args)) + if result["ok"] and not dry_run and config.retirement.auto_land_on_integration: + result["autoLandedSeats"] = _auto_land_completed_seats( + config, + confined_contract, + roles=frozenset({"worker", "reviewer"}), + reason="leaf integrated into master", + edge="leaf-integration", + ) + return result def worktree_cleanup_tool( @@ -337,7 +375,51 @@ def lifecycle_finalize_task_tool( dry_run=dry_run, teardown_providers=teardown_providers, ) - return _worktree_result("lifecycle_finalize_task", git_worktree_manager.finalize_result(args)) + result = _worktree_result( + "lifecycle_finalize_task", git_worktree_manager.finalize_result(args) + ) + if result["ok"] and not dry_run and config.retirement.auto_land_on_finalize: + result["autoLandedSeats"] = _auto_land_completed_seats( + config, + confined_contract, + roles=frozenset({"manager", "reviewer"}), + reason="master finalized into super", + edge="master-finalization", + ) + return result + + +def _auto_land_completed_seats( + config: McpRuntimeConfig, + contract_path: Path, + *, + roles: frozenset[str], + reason: str, + edge: str, +) -> list[str]: + """The completion-edge auto-land hook: resolve the contract's qualified leaf key and archive seats. + + Best-effort, END TO END: an unreadable/already-archived contract, a catalog read/write failure, + or any other failure in the landing body skips the auto-land rather than failing the completion + edge itself -- landing is a cleanup courtesy, never a gate on the edge it rides. The whole body + is guarded, not just ``load_contract``: ``land_seats_for_leaf`` + does catalog file I/O that can raise just as easily as a missing contract file, and a raise from + there must never propagate out of an already-succeeded ``worktree_integrate`` / + ``lifecycle_finalize_task`` call. + """ + try: + contract = load_contract(contract_path) + leaf_key = f"{contract.repo_name}/{contract.task_root.name}/{contract.task_id}" + catalog = TerminalCatalog(terminal_catalog_path(config.coordination_root)) + at = now_iso() + landed = land_seats_for_leaf( + catalog, leaf_key=leaf_key, roles=roles, reason=reason, edge=edge, at=at + ) + for entry in landed: + log_landed_event(config, entry) + return [entry.id for entry in landed] + except Exception: + return [] def _worktree_namespace( diff --git a/mcp/src/agents_remember/controlplane/attention_dismissals.py b/mcp/src/agents_remember/controlplane/attention_dismissals.py index 055f607d..3c8fda57 100644 --- a/mcp/src/agents_remember/controlplane/attention_dismissals.py +++ b/mcp/src/agents_remember/controlplane/attention_dismissals.py @@ -12,7 +12,7 @@ import os from pathlib import Path -from pydantic import BaseModel, ConfigDict, Field +from pydantic import BaseModel, ConfigDict, Field, ValidationError ATTENTION_DISMISSAL_SCHEMA = "ar-attention-dismissal/v1" @@ -57,15 +57,23 @@ def dismiss(self, record: AttentionDismissalRecord) -> None: self._replace(list(records.values())) def read(self) -> list[AttentionDismissalRecord]: - """Read current acknowledgement rows (empty when absent).""" + """Read current acknowledgement rows (empty when absent). + + 260707-HFX2-L12 F12: a torn/legacy line is skipped, not raised — this store is read on the + dashboard projection/ASGI path, and a single malformed row must not 500 the endpoint or + freeze a tick (these are disposable UI facts, not an audit trail).""" path = self.log_path() if not path.exists(): return [] - return [ - AttentionDismissalRecord.model_validate_json(line) - for line in path.read_text(encoding="utf-8").splitlines() - if line.strip() - ] + records: list[AttentionDismissalRecord] = [] + for line in path.read_text(encoding="utf-8").splitlines(): + if not line.strip(): + continue + try: + records.append(AttentionDismissalRecord.model_validate_json(line)) + except ValidationError: + continue + return records def current(self) -> dict[str, AttentionDismissalRecord]: """Fold by ``itemId``; legacy duplicate rows collapse to the newest row read.""" diff --git a/mcp/src/agents_remember/controlplane/escalation_ladder.py b/mcp/src/agents_remember/controlplane/escalation_ladder.py new file mode 100644 index 00000000..3082843b --- /dev/null +++ b/mcp/src/agents_remember/controlplane/escalation_ladder.py @@ -0,0 +1,187 @@ +"""P-15 tier 3 (260707-HFX2-L4): the escalation ladder walker. + +"No signal dies with a silent seat." An unacked ``OperatorInboxEntry`` climbs the spawn edges +deterministically -- rung 1 (renudge the original addressee) -> rung 2 (skip-level: re-address to +the owner's owner, ``signal_routing.derive_skip_level_owner``, which already walks past any dead +intermediate) -> rung 3 (architect custody, terminal). Every transition is pure here: this module +decides WHAT should happen and WHO the next addressee is; ``serving/supervisor.py`` (the only +caller, mirroring how it already owns every other predicate/action pairing) is what reads the +stores, calls this, and performs the delivery + durable row update. + +Ruled terminal (developer, 2026-07-09): the ladder ends at the ARCHITECT -- the live seat the +human actually talks to -- never at a "developer" mailbox. The human is an authority, not an +address: a human-shaped mailbox can never mechanically ack, which is exactly how the 2026-07-09 +storm's rows became immortal. The architect seat acks CUSTODY (consume) and briefs the human with +a catch-up report when they return; it is never renudged repeatedly past that, because the +architect can do nothing to make the human react faster. With no architect seat attached the row +stays role-addressed and level-triggered -- it lands the moment an architect session appears +(delivery or poll-at-session-start) and ages out via the inbox pending TTL if none ever does. + +Doctrine (R4, with HFX-L6's capture hardening): a spawned seat NEVER absorbs its dead owner's +role -- it continues its own brief and escalates. This module never reassigns a role; it only ever +finds the next LIVE address to hand a signal to. + +Ruled invariant (developer, 2026-07-09): "no signal dies with a silent seat" is subordinate to +"no signal outranks system health". The ladder climbs by mutating ONE row -- re-anchor, re-address, +redeliver, increment tries -- and never mints a sibling row for a transition. Rows the developer +never acks age out via the inbox pending TTL; the artifact on disk, not the inbox row, is the +durable record. The 2026-07-09 escalation storm (each transition posting a new ladder-eligible +pending row, exempt from compaction, addressed to an absent human) is the incident this rule +retires. +""" + +from __future__ import annotations + +from dataclasses import dataclass +from datetime import datetime +from typing import Literal + +from agents_remember.controlplane.operator_inbox_records import OperatorInboxEntry +from agents_remember.controlplane.signal_routing import ( + RoutedOwner, + derive_architect_owner, + derive_skip_level_owner, + is_seat_dead, +) +from agents_remember.serving.terminal_catalog import TerminalCatalog + +MAX_RUNG = 3 +MIN_RUNG_DWELL_SECONDS = 5 * 60.0 +LadderAction = Literal["renudge", "skip-level", "architect-attention"] + +_ACTION_BY_RUNG: dict[int, LadderAction] = { + 1: "renudge", + 2: "skip-level", + 3: "architect-attention", +} + + +@dataclass(frozen=True) +class LadderStep: + """One rung transition this walker recommends for a pending, unacked entry.""" + + rung: int + action: LadderAction + owner: RoutedOwner + + +def _dwell_anchor(entry: OperatorInboxEntry) -> str: + """What the current rung's dwell time is measured from: the last rung transition, or the + row's own creation when it has never been escalated (rung 0 -> 1).""" + return entry.escalatedAt or entry.createdAt + + +def _parse_anchor(value: str) -> datetime | None: + try: + return datetime.fromisoformat(value) + except ValueError: + return None + + +def _minimum_dwell_anchor(entry: OperatorInboxEntry, escalated_at: datetime) -> datetime: + """Redundant hard-floor anchor for later rung transitions. + + ``advance_rung`` stamps both ``escalatedAt`` and the dedicated ``rungTransitionAt``. The normal + per-rung dwell continues to use ``escalatedAt``; the second stamp independently enforces the + ruled five-minute safety floor. This redundancy is deliberate: the live 2026-07-09 cascade + showed that one stale escalation anchor can collapse several rungs into seconds. General row + ``ts`` is intentionally excluded because delivery and renewal also change it. + """ + transition_at = _parse_anchor(entry.rungTransitionAt or "") + return max(escalated_at, transition_at) if transition_at is not None else escalated_at + + +def rung_due( + entry: OperatorInboxEntry, + *, + now: datetime, + sla_seconds: float, + rung_seconds: float, +) -> bool: + """Whether ``entry`` (still pending, never consumed=acked) is due for its NEXT rung. + + Rung 0 -> 1 uses ``sla_seconds`` (the per-``message_kind`` ack SLA, R1); every later + transition uses ``rung_seconds`` (that rung's own dwell time, re-anchored at each transition). + A row already at :data:`MAX_RUNG` (architect custody) never advances further (R5: no + auto-action past the architect -- and no repeated nudging of a seat that cannot make the + human react faster). + """ + if entry.state != "pending" or entry.rung >= MAX_RUNG: + return False + anchor = _parse_anchor(_dwell_anchor(entry)) + if anchor is None: + return False + threshold = sla_seconds if entry.rung == 0 else rung_seconds + if (now - anchor).total_seconds() < threshold: + return False + if entry.rung == 0: + return True + floor_anchor = _minimum_dwell_anchor(entry, anchor) + return (now - floor_anchor).total_seconds() >= MIN_RUNG_DWELL_SECONDS + + +def next_step( + catalog: TerminalCatalog, + entry: OperatorInboxEntry, +) -> LadderStep: + """The next rung + addressee for ``entry``. The addressee that "silence" is measured against + is the row's own mailbox key (``agentId``/``lifecycleId``/``recipientRole``) -- the seat (or + role) that has not acked; rung 2 re-addresses to THAT seat's owner's owner.""" + rung = min(entry.rung + 1, MAX_RUNG) + action = _ACTION_BY_RUNG[rung] + if action == "renudge": + owner = RoutedOwner( + role=entry.recipientRole, agent_id=entry.agentId, lifecycle_id=entry.lifecycleId + ) + elif action == "skip-level": + owner = derive_skip_level_owner( + catalog, sender_agent_id=entry.agentId, message_kind=entry.messageKind + ) + if owner.agent_id is None and owner.role is None: + # The hierarchy ceiling: a manager-addressed row has only one level above it + # (the orchestrator), so "the owner's owner" resolves to nothing -- there is no + # further seat to skip to below the architect. Jump straight to rung 3 rather than + # stalling rung 2 forever (doctrine: no signal dies with a silent seat). + return LadderStep( + rung=MAX_RUNG, + action="architect-attention", + owner=derive_architect_owner(catalog), + ) + else: + owner = derive_architect_owner(catalog) + return LadderStep(rung=rung, action=action, owner=owner) + + +def seat_is_suspect( + catalog: TerminalCatalog, + agent_id: str | None, + *, + now: datetime, + stale_seconds: float, +) -> bool: + """R3: a seat past rung ``respawn_after_rung`` is "suspect" when it shows no liveness signal + of its own -- dead outright, or a catalog turn-state that has sat ``stale`` past + ``stale_seconds`` (the same cutoff the L2 seat-liveness predicate already uses). A row can be + unacked for reasons that are not the seat's fault (recipient_role-only mailbox, no catalog + trace); this predicate only ever fires for a seat this module can actually observe as dead or + stalled, never inferred from silence alone. + + An ARCHITECT seat is never suspect (ruled 2026-07-09): it is the human's own session, so its + silence means the human is away -- retiring/respawning it cannot produce the human and would + kill the very seat that holds custody of their catch-up report. + """ + if agent_id is None: + return False + live = catalog.get(agent_id) + if live is not None and live.binding_role == "architect": + return False + if is_seat_dead(catalog, agent_id): + return True + entry = catalog.get(agent_id) + if entry is None or entry.turn_state != "stale" or entry.turn_state_changed_at is None: + return False + try: + changed_at = datetime.fromisoformat(entry.turn_state_changed_at) + except ValueError: + return False + return (now - changed_at).total_seconds() >= stale_seconds diff --git a/mcp/src/agents_remember/controlplane/expectation_rows.py b/mcp/src/agents_remember/controlplane/expectation_rows.py new file mode 100644 index 00000000..3737e2b7 --- /dev/null +++ b/mcp/src/agents_remember/controlplane/expectation_rows.py @@ -0,0 +1,287 @@ +"""R2 (260707-HFX2-L1): durable what-must-happen-by-when rows, written atomically at dispatch. + +Every dispatch surface -- ``spawn_agent_session`` (briefed-by, and turn-report-by when the spawn +claims a leaf), a gate opening (verdict-by), and an operator-inbox post (ack-by) -- writes one +durable :class:`ExpectationRow` in the SAME call that performs the dispatch, never as a +follow-up step a caller could forget. Deadlines are ROWS an L2 sweep scans, never in-memory +timers -- the Restate durable-timer lesson (R2): a row survives a daemon/MCP restart; a timer +does not. + +``ExpectationKind`` must be kept in sync with ``KNOWN_EXPECTATION_KINDS`` in +``kernel/agentic_settings.py`` (duplicated there to avoid a kernel<->controlplane import cycle). +""" + +from __future__ import annotations + +import os +from datetime import datetime, timedelta +from pathlib import Path +from typing import Literal + +from pydantic import BaseModel, ConfigDict, Field + +EXPECTATION_ROW_SCHEMA = "ar-expectation-row/v1" + +# 260707-HFX2-L12 F4: how long a met/missed row is kept for dashboard/provenance before the sweep +# reclaims it. Pending rows are always kept. Terminal rows older than this can no longer drive a +# finding (overdue/find_by_source read pending only), so dropping them is safe. +EXPECTATION_RETENTION_SECONDS = 3600.0 + +ExpectationKind = Literal["briefed-by", "turn-report-by", "verdict-by", "ack-by"] +ExpectationState = Literal["pending", "met", "missed"] + + +class ExpectationRow(BaseModel): + """One append-only ``ar-expectation-row/v1`` snapshot: what must happen, by when.""" + + model_config = ConfigDict(extra="forbid") + + schema_version: str = Field(default=EXPECTATION_ROW_SCHEMA, alias="schema") + id: str + ts: str + kind: ExpectationKind + state: ExpectationState + createdAt: str + dueAt: str + # The dispatch surface's own id this row rides beside -- a spawned session id (briefed-by / + # turn-report-by), a gate id (verdict-by), or an operator-inbox entry id (ack-by). Lets a + # sweep or a dashboard resolve straight back to the thing it is a deadline FOR. + sourceId: str + subjectAgentId: str | None = None + subjectLifecycleId: str | None = None + leafKey: str | None = None + seatRole: str | None = None + note: str | None = None + metAt: str | None = None + missedAt: str | None = None + + +def create_expectation_row( + *, + row_id: str, + now: str, + kind: ExpectationKind, + due_at: str, + source_id: str, + subject_agent_id: str | None = None, + subject_lifecycle_id: str | None = None, + leaf_key: str | None = None, + seat_role: str | None = None, + note: str | None = None, +) -> ExpectationRow: + """Create a pending expectation row. Pure: the caller mints ``row_id``/``now``/``due_at``.""" + return ExpectationRow( + id=row_id, + ts=now, + kind=kind, + state="pending", + createdAt=now, + dueAt=due_at, + sourceId=source_id, + subjectAgentId=subject_agent_id, + subjectLifecycleId=subject_lifecycle_id, + leafKey=leaf_key, + seatRole=seat_role, + note=note, + ) + + +def due_at_from_sla(*, now: datetime, sla_seconds: float) -> str: + """The durable ``dueAt`` timestamp: ``now`` + the kind's configured SLA.""" + return (now + timedelta(seconds=sla_seconds)).isoformat() + + +def mark_met(row: ExpectationRow, *, now: str) -> ExpectationRow: + """Return a snapshot marking the expectation fulfilled (idempotent past the first call).""" + if row.state != "pending": + return row + return row.model_copy(update={"ts": now, "state": "met", "metAt": now}) + + +def mark_missed(row: ExpectationRow, *, now: str) -> ExpectationRow: + """Return a snapshot marking the expectation missed (idempotent past the first call). + + This leaf only reserves the transition -- the L2 sweep is the actual caller. + """ + if row.state != "pending": + return row + return row.model_copy(update={"ts": now, "state": "missed", "missedAt": now}) + + +def _terminal_time(row: ExpectationRow) -> datetime | None: + """Best terminal timestamp for a met/missed row (metAt/missedAt, else ts), tz-safe or None.""" + stamp = row.metAt or row.missedAt or row.ts + try: + return datetime.fromisoformat(stamp) + except (ValueError, TypeError): + return None + + +class ExpectationRowStore: + """Append-only expectation-row log, folded by id -- same shape as ``OperatorInboxStore``.""" + + def __init__(self, observer_root: Path) -> None: + self._root = observer_root + + def log_path(self) -> Path: + return self._root / "workspace" / "expectation-rows.jsonl" + + def append(self, row: ExpectationRow) -> None: + path = self.log_path() + path.parent.mkdir(parents=True, exist_ok=True) + with path.open("a", encoding="utf-8") as handle: + handle.write(row.model_dump_json(by_alias=True, exclude_none=True) + "\n") + + def read(self) -> list[ExpectationRow]: + path = self.log_path() + if not path.exists(): + return [] + return [ + ExpectationRow.model_validate_json(line) + for line in path.read_text(encoding="utf-8").splitlines() + if line.strip() + ] + + def current(self) -> dict[str, ExpectationRow]: + """Fold the log by row id, last-wins.""" + latest: dict[str, ExpectationRow] = {} + for row in self.read(): + latest[row.id] = row + return latest + + def pending(self) -> list[ExpectationRow]: + return sorted( + (row for row in self.current().values() if row.state == "pending"), + key=lambda row: row.dueAt, + ) + + def find_by_source(self, source_id: str, *, kind: ExpectationKind | None = None) -> ExpectationRow | None: + """The most recent pending row for ``source_id`` (optionally kind-filtered) -- the + write-once-consume-once lookup a dispatch surface's fulfillment path uses.""" + candidates = [ + row + for row in self.pending() + if row.sourceId == source_id and (kind is None or row.kind == kind) + ] + return max(candidates, key=lambda row: row.createdAt, default=None) + + def overdue(self, *, now: datetime) -> list[ExpectationRow]: + """Pending rows whose ``dueAt`` has already passed -- the L2 sweep's predicate input.""" + due: list[ExpectationRow] = [] + for row in self.pending(): + try: + due_at = datetime.fromisoformat(row.dueAt) + except ValueError: + continue + if now >= due_at: + due.append(row) + return due + + def mark_met(self, row_id: str, *, now: str) -> ExpectationRow: + current = self.current().get(row_id) + if current is None: + raise KeyError(f"no expectation row {row_id!r}") + met = mark_met(current, now=now) + if met is not current: + self.append(met) + return met + + def mark_missed( + self, + row_id: str, + *, + now: str, + current: dict[str, ExpectationRow] | None = None, + ) -> ExpectationRow: + """Mark an overdue row missed (idempotent). Pass ``current`` (the sweep's one-read + snapshot) so the supervisor's per-finding marks stay O(1) instead of re-folding the whole + log each call (CS-6 D2, 260707-HFX2-L12); ``None`` reads fresh for the standalone path.""" + entries = self.current() if current is None else current + row = entries.get(row_id) + if row is None: + raise KeyError(f"no expectation row {row_id!r}") + missed = mark_missed(row, now=now) + if missed is not row: + self.append(missed) + return missed + + def compact( + self, *, now: datetime, retain_seconds: float = EXPECTATION_RETENTION_SECONDS + ) -> tuple[int, dict[str, ExpectationRow]]: + """Reclaim the log to `pending + recent-terminal`, returning `(removed, kept_by_id)`. + + 260707-HFX2-L12 F4/CS-6 D3: the append-only log grew unbounded over daemon lifetime (a new + row per mark). This folds by id (drops superseded appends) and drops met/missed rows whose + terminal timestamp is older than `retain_seconds`; pending and unparseable-ts rows are always + kept. The returned folded dict is the sweep's one-read expectation snapshot, so the supervisor + reads + reclaims the log in a single pass (mirrors the signal-cooldown compactor).""" + records = self.read() + if not records: + return 0, {} + folded: dict[str, ExpectationRow] = {} + for row in records: + folded[row.id] = row + cutoff = now - timedelta(seconds=retain_seconds) + kept: dict[str, ExpectationRow] = {} + for row_id, row in folded.items(): + if row.state == "pending": + kept[row_id] = row + continue + terminal = _terminal_time(row) + try: + stale = terminal is not None and terminal < cutoff + except TypeError: + stale = False # tz-naive vs tz-aware legacy row: keep rather than misjudge + if not stale: + kept[row_id] = row + removed = len(records) - len(kept) + if removed: + self._replace(list(kept.values())) + return removed, kept + + def _replace(self, rows: list[ExpectationRow]) -> None: + path = self.log_path() + if not rows: + path.unlink(missing_ok=True) + return + path.parent.mkdir(parents=True, exist_ok=True) + tmp = path.with_name(f"{path.name}.{os.getpid()}.tmp") + tmp.write_text( + "\n".join(row.model_dump_json(by_alias=True, exclude_none=True) for row in rows) + "\n", + encoding="utf-8", + ) + os.replace(tmp, path) + + +def write_expectation_row( + store: ExpectationRowStore, + *, + row_id: str, + now: datetime, + kind: ExpectationKind, + sla_seconds: float, + source_id: str, + subject_agent_id: str | None = None, + subject_lifecycle_id: str | None = None, + leaf_key: str | None = None, + seat_role: str | None = None, + note: str | None = None, +) -> ExpectationRow: + """Create + append one expectation row in one call -- the atomic-write-at-dispatch helper + every dispatch surface calls (spawn / gate-open / inbox-post), so the row is never a + forgettable follow-up step.""" + now_text = now.isoformat() + row = create_expectation_row( + row_id=row_id, + now=now_text, + kind=kind, + due_at=due_at_from_sla(now=now, sla_seconds=sla_seconds), + source_id=source_id, + subject_agent_id=subject_agent_id, + subject_lifecycle_id=subject_lifecycle_id, + leaf_key=leaf_key, + seat_role=seat_role, + note=note, + ) + store.append(row) + return row diff --git a/mcp/src/agents_remember/controlplane/inbox_backoff.py b/mcp/src/agents_remember/controlplane/inbox_backoff.py new file mode 100644 index 00000000..e46d4f70 --- /dev/null +++ b/mcp/src/agents_remember/controlplane/inbox_backoff.py @@ -0,0 +1,129 @@ +"""Redelivery backoff + per-target rate limiting for pending unacked operator inbox entries. + +R3 (260707-HFX2-L1): a pending/unacked row (``state == "pending"``; ``deliveryState`` never +reaches a terminal state on its own -- only ``consume`` does) carries a ``nextAttemptAt`` +backoff schedule. L2 (the supervisor sweep, a sibling leaf) is the actual driver that walks +``OperatorInboxStore.list_redeliverable`` on a cadence and calls ``deliver_inbox_entry`` again; +this module is the pure math + the per-target rate-limit gate the sweep consults before +re-attempting, mirroring the ``OrchestrationNudgeStore.record`` rate-limit pattern +(``orchestration_nudges.py:81-99``: compare elapsed time since the last attempt against a +rate-limit floor) rather than a fixed interval -- a slow target backs off, a healthy one is not +throttled below the schedule. +""" + +from __future__ import annotations + +from datetime import datetime, timedelta + +from agents_remember.controlplane.operator_inbox_records import OperatorInboxEntry + +# Exponential-ish backoff ladder, seconds; the last entry is the steady-state ceiling so a +# long-pending row does not retry every few seconds forever (26898-style thundering redelivery). +BACKOFF_SCHEDULE_SECONDS: tuple[float, ...] = (30.0, 60.0, 300.0, 900.0, 3600.0, 21600.0) + +# The effective floor: a delivered/queued/unconfirmed/no-hosted-session row may still be in the +# harness queue or under model processing, so no retry path may run faster than this. +MIN_REDELIVERY_INTERVAL_SECONDS = 900.0 +DEFAULT_RATE_LIMIT_SECONDS = MIN_REDELIVERY_INTERVAL_SECONDS + +_REDELIVERABLE_DELIVERY_STATES = frozenset({"queued", "no-hosted-session", "delivered", "unconfirmed"}) +"""Every deliveryState is redeliverable while the row is pending -- 'delivered' is never terminal +(R1: pasted != perceived), so a delivered-but-unacked row still schedules a nudge.""" + + +def backoff_seconds_for_attempt(attempt_count: int) -> float: + """The wait before attempt ``attempt_count + 1``, from the ladder (clamped to the ceiling).""" + index = max(0, min(attempt_count, len(BACKOFF_SCHEDULE_SECONDS) - 1)) + return BACKOFF_SCHEDULE_SECONDS[index] + + +def require_redelivery_floor_seconds( + rate_limit_seconds: float | None, + *, + owner: str = "redelivery interval", +) -> float: + """Return the effective redelivery floor, refusing any sub-15-minute value.""" + if rate_limit_seconds is None: + return DEFAULT_RATE_LIMIT_SECONDS + if rate_limit_seconds < MIN_REDELIVERY_INTERVAL_SECONDS: + raise ValueError( + f"{owner} must be at least {MIN_REDELIVERY_INTERVAL_SECONDS:g} seconds" + ) + return rate_limit_seconds + + +def next_attempt_at( + *, + now: datetime, + attempt_count: int, + redelivery_floor_seconds: float | None = None, +) -> str: + """The durable ``nextAttemptAt`` timestamp stamped after one delivery attempt. + + A durable ROW, never an in-memory timer -- it is written atomically alongside the delivery + snapshot, so it survives a daemon/MCP restart (the Restate durable-timer lesson R2 cites). + """ + effective_seconds = max( + backoff_seconds_for_attempt(attempt_count), + require_redelivery_floor_seconds( + redelivery_floor_seconds, owner="nextAttemptAt redelivery floor" + ), + ) + return (now + timedelta(seconds=effective_seconds)).isoformat() + + +def is_due(entry: OperatorInboxEntry, *, now: datetime) -> bool: + """Whether ``entry`` is a pending row whose backoff window has elapsed.""" + if is_ladder_resolved(entry): + return False + if entry.state != "pending": + return False + if entry.deliveryState not in _REDELIVERABLE_DELIVERY_STATES: + return False + if entry.nextAttemptAt is None: + return True + try: + due_at = datetime.fromisoformat(entry.nextAttemptAt) + except ValueError: + return True + return now >= due_at + + +def is_rate_limited( + entry: OperatorInboxEntry, + *, + now: datetime, + rate_limit_seconds: float = DEFAULT_RATE_LIMIT_SECONDS, +) -> bool: + """Whether the per-target floor still holds since the last attempt on this entry.""" + floor_seconds = require_redelivery_floor_seconds( + rate_limit_seconds, owner="redelivery rate limit" + ) + if entry.lastAttemptAt is None: + return False + try: + last_attempt = datetime.fromisoformat(entry.lastAttemptAt) + except ValueError: + return False + return (now - last_attempt).total_seconds() < floor_seconds + + +def redeliverable( + entries: list[OperatorInboxEntry], + *, + now: datetime, + rate_limit_seconds: float = DEFAULT_RATE_LIMIT_SECONDS, +) -> list[OperatorInboxEntry]: + """Pending rows both past their backoff window and clear of the per-target rate limit.""" + return [ + entry + for entry in entries + if not is_ladder_resolved(entry) + and is_due(entry, now=now) + and not is_rate_limited(entry, now=now, rate_limit_seconds=rate_limit_seconds) + ] + + +def is_ladder_resolved(entry: OperatorInboxEntry) -> bool: + """Whether the escalation ladder has terminally resolved this row without an ack.""" + return entry.state == "ladder-resolved" diff --git a/mcp/src/agents_remember/controlplane/interaction_retention.py b/mcp/src/agents_remember/controlplane/interaction_retention.py index e49f52a8..da9971fe 100644 --- a/mcp/src/agents_remember/controlplane/interaction_retention.py +++ b/mcp/src/agents_remember/controlplane/interaction_retention.py @@ -4,7 +4,10 @@ from datetime import datetime -from agents_remember.controlplane.operator_inbox_records import OperatorInboxEntry +from agents_remember.controlplane.operator_inbox_records import ( + OperatorInboxEntry, + fold_operator_inbox_entries, +) from agents_remember.controlplane.records import GateKind, GateRecord from agents_remember.observer.timeutil import age_seconds @@ -13,6 +16,20 @@ INTERACTION_RECORD_TTL_SECONDS = 24 * 60 * 60.0 AGENT_PICKUP_TTL_SECONDS = 300.0 +INBOX_PENDING_TTL_SECONDS = 48 * 60 * 60.0 +"""Ruled invariant (developer, 2026-07-09): no row outranks system health -- pending/unacked rows +age out too. The inbox is a notification surface, not the record; the durable artifact (turn +report, task doc, gate) lives on disk and survives the purge. A nudge nobody consumed within this +window is stale noise; if its condition still holds, the supervisor recreates one fresh row. +Supersedes the HFX2-L1 R1 immortal-pending rule, which let the 2026-07-09 escalation storm grow a +227 MB / 20k-pending-row inbox that took the host down.""" + +INBOX_MAX_CURRENT_ROWS = 500 +"""Hard health cap on folded inbox rows: past this, compaction keeps the newest rows and evicts +the oldest regardless of state. A correctly coalescing system sits orders of magnitude below this +(one row per distinct root cause); the cap is the backstop that bounds the store even when a +producer misbehaves.""" + MUTATING_TOOL_GATE_KINDS: frozenset[GateKind] = frozenset( { "worktree-intent", @@ -47,16 +64,19 @@ def inbox_keep_ids( *, now: datetime, ttl_seconds: float = INTERACTION_RECORD_TTL_SECONDS, + max_rows: int = INBOX_MAX_CURRENT_ROWS, ) -> set[str]: """Inbox ids whose current snapshot still belongs in the compacted log.""" - latest: dict[str, OperatorInboxEntry] = {} - for entry in entries: - latest[entry.id] = entry - return { - entry.id + latest = fold_operator_inbox_entries(entries) + kept = [ + entry for entry in latest.values() if _keep_inbox_entry(entry, now=now, ttl_seconds=ttl_seconds) - } + ] + if len(kept) > max_rows: + kept.sort(key=lambda entry: entry.createdAt, reverse=True) + kept = kept[:max_rows] + return {entry.id for entry in kept} def delete_after_wait(gate: GateRecord) -> bool: @@ -86,7 +106,15 @@ def _keep_gate(gate: GateRecord, *, now: datetime, ttl_seconds: float) -> bool: def _keep_inbox_entry( entry: OperatorInboxEntry, *, now: datetime, ttl_seconds: float ) -> bool: - if entry.state == "consumed": + """Ruled invariant (developer, 2026-07-09): system health outranks every row, pending + included. A pending/unacked row is kept only within :data:`INBOX_PENDING_TTL_SECONDS`; + consumed rows keep the shorter audit window; ladder-resolved rows drop immediately. This + supersedes HFX2-L1 R1's immortal-pending rule -- the durable record is the artifact on disk, + never the inbox row, so purging an old nudge loses nothing the supervisor cannot recreate + (as one fresh row) while its condition persists.""" + if entry.state == "ladder-resolved": return False age = age_seconds(entry.createdAt, now) + if entry.state == "pending": + return age is None or age <= INBOX_PENDING_TTL_SECONDS return age is None or age <= ttl_seconds diff --git a/mcp/src/agents_remember/controlplane/operator_inbox_records.py b/mcp/src/agents_remember/controlplane/operator_inbox_records.py index 54db11d9..3592ecce 100644 --- a/mcp/src/agents_remember/controlplane/operator_inbox_records.py +++ b/mcp/src/agents_remember/controlplane/operator_inbox_records.py @@ -2,13 +2,14 @@ from __future__ import annotations +from collections.abc import Iterable from typing import Literal from pydantic import BaseModel, ConfigDict, Field OPERATOR_INBOX_RECORD_SCHEMA = "ar-operator-inbox-entry/v1" -OperatorInboxState = Literal["pending", "consumed"] +OperatorInboxState = Literal["pending", "consumed", "ladder-resolved"] OperatorInboxVia = Literal["chat", "dashboard", "cli"] AgentRole = Literal[ "developer", @@ -19,6 +20,9 @@ "manager", "worker", "reviewer", + "system-specialist", + "architect", + "curator", "agent", "system", ] @@ -29,6 +33,9 @@ "master-handover", "nudge", "escalation", + "degradation-alert", + "decision-item", + "decision-ruling", ] InboxDeliveryState = Literal["queued", "no-hosted-session", "delivered", "unconfirmed"] @@ -61,6 +68,11 @@ class OperatorInboxEntry(BaseModel): gateId: str | None = None messageKind: InboxMessageKind = "message" artifactPath: str | None = None + # Leaf-scoped supervisor/completion signals carry their durable routing subject so later + # redelivery/escalation can re-check the live leaf chain instead of trusting a stale address. + leafKey: str | None = None + seatRole: str | None = None + subjectAgentId: str | None = None ask: str response: str createdAt: str @@ -73,6 +85,54 @@ class OperatorInboxEntry(BaseModel): consumedAt: str | None = None consumedBy: str | None = None consumedVia: OperatorInboxVia | None = None + ladderResolvedAt: str | None = None + ladderResolvedReason: str | None = None + # R1 (260707-HFX2-L1): ack semantics -- consume=ack is the ONLY terminal outcome. 'delivered' + # is never terminal (F-A/F-V proved pasted != perceived), so every delivery attempt -- including + # a confirmed paste -- stamps a redelivery schedule until the row is actually consumed. + attemptCount: int = 0 + lastAttemptAt: str | None = None + nextAttemptAt: str | None = None + # Set only when the ladder (HFX2-L4) escalates an unacked row past redelivery; this leaf only + # reserves the field so the row stays escalatable -- it never sets it itself. + escalatedAt: str | None = None + # Independent safety-floor stamp written only by advance_rung. General row ``ts`` changes on + # delivery/renewal and therefore cannot prove when the last rung transition occurred. + rungTransitionAt: str | None = None + # P-15 tier 3 (260707-HFX2-L4): the ladder's own rung marker. 0 = not yet escalated; + # 1 = renudged to the original addressee; 2 = skip-level re-addressed to the owner's owner; + # 3 = surfaced to the developer attention queue. ``escalatedAt`` is re-stamped on every rung + # transition (the anchor the next SLA check reads from), so it always names "since when has + # this row sat at its CURRENT rung", not merely "was this row ever escalated". + rung: int = 0 + # R4 hierarchical routing: the owner address derived from catalog spawn provenance + # (spawned_by_session chain) at post time, so redelivery/escalation never has to + # re-derive it later. ``ownerRole`` mirrors ``recipientRole`` semantics but is the + # ROUTED address (worker -> its manager, manager -> its orchestrator, decision-item -> + # architect) rather than the caller-supplied one; ``None`` when routing had nothing to derive + # (e.g. a role-only mailbox with no catalog provenance). + ownerRole: AgentRole | None = None + ownerAgentId: str | None = None + ownerLifecycleId: str | None = None + + +def fold_operator_inbox_entries( + entries: Iterable[OperatorInboxEntry], +) -> dict[str, OperatorInboxEntry]: + """Fold snapshots by id while preserving the first observed terminal transition. + + Delivery can finish from a stale supervisor snapshot after a concurrent consume. Such a + pending snapshot is physically later in the append-only log, but it cannot reverse an + already-recorded terminal state. Terminal snapshots otherwise remain last-wins so repeated + consumes and ladder resolution keep their existing idempotent behavior. + """ + current: dict[str, OperatorInboxEntry] = {} + for entry in entries: + previous = current.get(entry.id) + if previous is not None and previous.state != "pending" and entry.state == "pending": + continue + current[entry.id] = entry + return current def create_operator_inbox_entry( @@ -91,8 +151,20 @@ def create_operator_inbox_entry( recipient_role: AgentRole | None = None, message_kind: InboxMessageKind = "message", artifact_path: str | None = None, + leaf_key: str | None = None, + seat_role: str | None = None, + subject_agent_id: str | None = None, + owner_role: AgentRole | None = None, + owner_agent_id: str | None = None, + owner_lifecycle_id: str | None = None, ) -> OperatorInboxEntry: - """Create a pending inbox entry. Pure: the caller mints ``entry_id`` and ``now``.""" + """Create a pending inbox entry. Pure: the caller mints ``entry_id`` and ``now``. + + ``owner_*`` (R4) is the routed address the caller derived from catalog spawn provenance + (or a reserved role like ``architect`` for a ``decision-item``) BEFORE posting -- stamped once, + at creation, so redelivery never has to re-derive it from a catalog snapshot that may have + since moved on. + """ require_inbox_address( lifecycle_id=lifecycle_id, agent_id=agent_id, @@ -110,11 +182,17 @@ def create_operator_inbox_entry( gateId=gate_id, messageKind=message_kind, artifactPath=artifact_path, + leafKey=leaf_key, + seatRole=seat_role, + subjectAgentId=subject_agent_id, ask=ask, response=response, createdAt=now, createdBy=created_by, createdVia=created_via, + ownerRole=owner_role, + ownerAgentId=owner_agent_id, + ownerLifecycleId=owner_lifecycle_id, ) @@ -126,7 +204,7 @@ def consume_operator_inbox_entry( consumed_via: OperatorInboxVia, ) -> OperatorInboxEntry: """Return a consumed snapshot, preserving the original post attribution.""" - if entry.state == "consumed": + if entry.state != "pending": return entry return entry.model_copy( update={ diff --git a/mcp/src/agents_remember/controlplane/operator_inbox_store.py b/mcp/src/agents_remember/controlplane/operator_inbox_store.py index 91cd140b..680ce4ba 100644 --- a/mcp/src/agents_remember/controlplane/operator_inbox_store.py +++ b/mcp/src/agents_remember/controlplane/operator_inbox_store.py @@ -6,6 +6,12 @@ from datetime import datetime from pathlib import Path +from agents_remember.controlplane.inbox_backoff import ( + DEFAULT_RATE_LIMIT_SECONDS, + is_ladder_resolved, + next_attempt_at, + redeliverable, +) from agents_remember.controlplane.interaction_retention import inbox_keep_ids from agents_remember.controlplane.operator_inbox_records import ( AgentRole, @@ -13,6 +19,7 @@ OperatorInboxEntry, OperatorInboxVia, consume_operator_inbox_entry, + fold_operator_inbox_entries, require_inbox_address, ) @@ -51,11 +58,8 @@ def read(self) -> list[OperatorInboxEntry]: ] def current(self) -> dict[str, OperatorInboxEntry]: - """Fold the inbox by entry id, last-wins.""" - latest: dict[str, OperatorInboxEntry] = {} - for record in self.read(): - latest[record.id] = record - return latest + """Fold the inbox by entry id, with terminal snapshots dominating stale pending ones.""" + return fold_operator_inbox_entries(self.read()) def list_pending( self, @@ -88,23 +92,206 @@ def record_delivery( delivery_state: InboxDeliveryState, delivered_to_session: str | None = None, delivery_detail: str | None = None, + current: dict[str, OperatorInboxEntry] | None = None, + redelivery_floor_seconds: float | None = None, ) -> OperatorInboxEntry: - """Append a delivery-status snapshot for one pending entry.""" - current = self.current().get(entry_id) - if current is None: + """Append a delivery-status snapshot for one pending entry. + + R1/R3: every attempt -- including a confirmed ``delivered`` paste -- bumps + ``attemptCount``, stamps ``lastAttemptAt``, and schedules ``nextAttemptAt`` from the + backoff ladder. 'delivered' is never terminal (pasted != perceived), so only ``consume`` + clears the redelivery schedule; a still-pending entry always carries a durable next-attempt + row L2 can sweep, restart-proof. + """ + entry = self._entry_from_current(entry_id, current) + if entry is None: raise KeyError(f"no operator inbox entry {entry_id!r}") - delivered = current.model_copy( + attempt_count = entry.attemptCount + 1 + delivered = entry.model_copy( update={ "ts": now, "deliveryState": delivery_state, - "deliveredAt": now if delivery_state == "delivered" else current.deliveredAt, + "deliveredAt": now if delivery_state == "delivered" else entry.deliveredAt, "deliveredToSession": delivered_to_session, "deliveryDetail": delivery_detail, + "attemptCount": attempt_count, + "lastAttemptAt": now, + "nextAttemptAt": ( + next_attempt_at( + now=datetime.fromisoformat(now), + attempt_count=attempt_count, + redelivery_floor_seconds=redelivery_floor_seconds, + ) + if entry.state == "pending" + else entry.nextAttemptAt + ), } ) self.append(delivered) return delivered + def list_redeliverable( + self, + *, + now: datetime, + rate_limit_seconds: float | None = None, + current: dict[str, OperatorInboxEntry] | None = None, + ) -> list[OperatorInboxEntry]: + """Pending rows past their backoff window and clear of the per-target rate limit (R3). + + The pure selection L2's sweep drives redelivery from; this store never redelivers on its + own (no in-memory timer -- the sweep is the only caller of ``deliver_inbox_entry`` again). + """ + entries = self.current() if current is None else current + pending = [entry for entry in entries.values() if entry.state == "pending"] + return redeliverable( + pending, + now=now, + rate_limit_seconds=( + rate_limit_seconds if rate_limit_seconds is not None else DEFAULT_RATE_LIMIT_SECONDS + ), + ) + + def mark_escalated( + self, + entry_id: str, + *, + now: str, + current: dict[str, OperatorInboxEntry] | None = None, + ) -> OperatorInboxEntry: + """Stamp ``escalatedAt`` once the ladder (HFX2-L4) escalates an unacked row. + + This leaf only reserves the field -- it never calls this itself; redelivery keeps running + until either ack or this mark, per R3. + """ + entry = self._entry_from_current(entry_id, current) + if entry is None: + raise KeyError(f"no operator inbox entry {entry_id!r}") + escalated = entry.model_copy(update={"ts": now, "escalatedAt": now}) + self.append(escalated) + return escalated + + def advance_rung( + self, + entry_id: str, + *, + rung: int, + now: str, + owner_role: AgentRole | None = None, + owner_agent_id: str | None = None, + owner_lifecycle_id: str | None = None, + readdress: bool = False, + current: dict[str, OperatorInboxEntry] | None = None, + ) -> OperatorInboxEntry: + """Stamp the ladder's next rung (260707-HFX2-L4, R1/R2): re-anchors ``escalatedAt`` to + ``now`` so the NEXT rung's SLA is measured from this transition, not the row's original + creation. Distinct from :meth:`mark_escalated` (HFX2-L2's reserved "this row is now + escalatable" stamp, rung-agnostic) -- the ladder is the only caller of this method. + + Ruled invariant (developer, 2026-07-09): the ladder climbs by MUTATING this one row -- + with ``readdress=True`` the row itself moves to the next addressee (skip-level owner, + then the developer attention queue). It never mints a sibling row; one root cause is one + row for its whole ladder life. (The escalation storm that took the host down was every + rung transition posting a new pending row whose own rungs posted more rows.) + """ + entry = self._entry_from_current(entry_id, current) + if entry is None: + raise KeyError(f"no operator inbox entry {entry_id!r}") + update: dict[str, object] = { + "ts": now, + "rung": rung, + "escalatedAt": now, + "rungTransitionAt": now, + } + if readdress: + update.update( + { + "recipientRole": owner_role, + "agentId": owner_agent_id, + "lifecycleId": owner_lifecycle_id, + "ownerRole": owner_role, + "ownerAgentId": owner_agent_id, + "ownerLifecycleId": owner_lifecycle_id, + } + ) + advanced = entry.model_copy(update=update) + self.append(advanced) + return advanced + + def renew( + self, + entry_id: str, + *, + now: str, + response: str | None = None, + leaf_key: str | None = None, + seat_role: str | None = None, + subject_agent_id: str | None = None, + owner_role: AgentRole | None = None, + owner_agent_id: str | None = None, + owner_lifecycle_id: str | None = None, + readdress: bool = False, + current: dict[str, OperatorInboxEntry] | None = None, + ) -> OperatorInboxEntry: + """Refresh one still-pending row in place: same id, bumped ``ts``, optionally refreshed + ``response``. The ruled coalescing primitive (developer, 2026-07-09): a condition that + re-fires updates its ONE existing row's date/detail instead of appending a duplicate -- + there is zero reason to repeat the same message until the system catches fire.""" + entry = self._entry_from_current(entry_id, current) + if entry is None: + raise KeyError(f"no operator inbox entry {entry_id!r}") + if entry.state != "pending": + return entry + update: dict[str, object] = {"ts": now} + if response is not None: + update["response"] = response + if leaf_key is not None: + update["leafKey"] = leaf_key + if seat_role is not None: + update["seatRole"] = seat_role + if subject_agent_id is not None: + update["subjectAgentId"] = subject_agent_id + if readdress: + update.update( + { + "recipientRole": owner_role, + "agentId": owner_agent_id, + "lifecycleId": owner_lifecycle_id, + "ownerRole": owner_role, + "ownerAgentId": owner_agent_id, + "ownerLifecycleId": owner_lifecycle_id, + } + ) + renewed = entry.model_copy(update=update) + self.append(renewed) + return renewed + + def mark_ladder_resolved( + self, + entry_id: str, + *, + now: str, + reason: str, + current: dict[str, OperatorInboxEntry] | None = None, + ) -> tuple[OperatorInboxEntry, bool]: + """Terminally resolve a ladder-complete row without treating it as an ack.""" + entry = self._entry_from_current(entry_id, current) + if entry is None: + raise KeyError(f"no operator inbox entry {entry_id!r}") + if is_ladder_resolved(entry): + return entry, False + resolved = entry.model_copy( + update={ + "ts": now, + "state": "ladder-resolved", + "ladderResolvedAt": now, + "ladderResolvedReason": reason, + "nextAttemptAt": None, + } + ) + self.append(resolved) + return resolved, True + def consume( self, entry_id: str, @@ -117,7 +304,7 @@ def consume( current = self.current().get(entry_id) if current is None: raise KeyError(f"no operator inbox entry {entry_id!r}") - if current.state == "consumed": + if current.state != "pending": return current, False consumed = consume_operator_inbox_entry( current, @@ -174,3 +361,11 @@ def _replace(self, records: list[OperatorInboxEntry]) -> None: encoding="utf-8", ) os.replace(tmp, path) + + def _entry_from_current( + self, + entry_id: str, + current: dict[str, OperatorInboxEntry] | None, + ) -> OperatorInboxEntry | None: + entries = self.current() if current is None else current + return entries.get(entry_id) diff --git a/mcp/src/agents_remember/controlplane/orchestration_artifacts.py b/mcp/src/agents_remember/controlplane/orchestration_artifacts.py index cce52d7f..ce9fe89e 100644 --- a/mcp/src/agents_remember/controlplane/orchestration_artifacts.py +++ b/mcp/src/agents_remember/controlplane/orchestration_artifacts.py @@ -8,16 +8,29 @@ from pydantic import BaseModel, ConfigDict, Field -OrchestrationRole = Literal["designer", "strategist", "orchestrator", "manager", "worker", "reviewer"] +OrchestrationRole = Literal[ + "architect", + "designer", + "strategist", + "orchestrator", + "manager", + "worker", + "curator", + "reviewer", + "system-specialist", +] EscalationReason = Literal["blocked", "plan-delta", "quality-failure", "missing-artifact"] _ROLE_ESCALATION: dict[OrchestrationRole, OrchestrationRole | Literal["developer"]] = { "worker": "manager", "manager": "orchestrator", - "orchestrator": "developer", - "designer": "orchestrator", + "orchestrator": "architect", + "architect": "developer", + "designer": "architect", "strategist": "orchestrator", + "curator": "manager", "reviewer": "orchestrator", + "system-specialist": "orchestrator", } _SAFE_STEM = re.compile(r"[^A-Za-z0-9_.-]+") diff --git a/mcp/src/agents_remember/controlplane/orchestration_nudges.py b/mcp/src/agents_remember/controlplane/orchestration_nudges.py index 1e61b066..dd400e32 100644 --- a/mcp/src/agents_remember/controlplane/orchestration_nudges.py +++ b/mcp/src/agents_remember/controlplane/orchestration_nudges.py @@ -7,7 +7,7 @@ from pathlib import Path from typing import Literal -from pydantic import BaseModel, ConfigDict, Field +from pydantic import BaseModel, ConfigDict, Field, ValidationError ORCHESTRATION_NUDGE_SCHEMA = "ar-orchestration-nudge/v1" NudgeReason = Literal["inactive", "missing-turn-report", "manual"] @@ -42,14 +42,19 @@ def log_path(self) -> Path: return self._root / "workspace" / "orchestration-nudges.jsonl" def read(self) -> list[OrchestrationNudgeRecord]: + """Read the nudge log, skipping any torn/legacy line (F12: dashboard-tolerant reader).""" path = self.log_path() if not path.exists(): return [] - return [ - OrchestrationNudgeRecord.model_validate_json(line) - for line in path.read_text(encoding="utf-8").splitlines() - if line.strip() - ] + records: list[OrchestrationNudgeRecord] = [] + for line in path.read_text(encoding="utf-8").splitlines(): + if not line.strip(): + continue + try: + records.append(OrchestrationNudgeRecord.model_validate_json(line)) + except ValidationError: + continue + return records def append(self, record: OrchestrationNudgeRecord) -> None: path = self.log_path() diff --git a/mcp/src/agents_remember/controlplane/orphan_policy.py b/mcp/src/agents_remember/controlplane/orphan_policy.py new file mode 100644 index 00000000..9e35ba0e --- /dev/null +++ b/mcp/src/agents_remember/controlplane/orphan_policy.py @@ -0,0 +1,32 @@ +"""R3 (260707-HFX2-L4): the orphan-workers hook for a dead manager's still-running workers. + +Policy (documented, not automated -- deliberately a stub/hook per the leaf's scope): when a +manager seat is respawned or found dead, its live worker seats do NOT auto re-parent to the +successor and do NOT absorb the manager's role (R4 doctrine, HFX-L6 capture hardening). They are +held under an explicit ORPHAN expectation: the grandparent (the orchestrator that owns the master) +is signaled with the orphan list and decides -- re-parent each worker to the respawned manager once +it exists, or let them run to their own turn-report/completion under no manager and be swept up at +the next leaf dispatch. A future leaf may wire an actual re-parent action; this one only detects +and surfaces the set, via the same catalog spawn-provenance trace every other predicate here reads. +""" + +from __future__ import annotations + +from agents_remember.serving.terminal_catalog import TerminalCatalog, TerminalCatalogEntry + + +def find_orphaned_workers( + catalog: TerminalCatalog, *, manager_agent_id: str +) -> list[TerminalCatalogEntry]: + """Every still-``running`` worker seat spawned by ``manager_agent_id`` (pure catalog read). + + Called once a manager seat is confirmed dead/respawned (``serving/supervisor.py``'s dead- + upstream / respawn actions); the caller is responsible for the actual signal post + event. + """ + return [ + entry + for entry in catalog.list() + if entry.binding_role == "worker" + and entry.spawned_by_session == manager_agent_id + and entry.status == "running" + ] diff --git a/mcp/src/agents_remember/controlplane/signal_routing.py b/mcp/src/agents_remember/controlplane/signal_routing.py new file mode 100644 index 00000000..ee7d8858 --- /dev/null +++ b/mcp/src/agents_remember/controlplane/signal_routing.py @@ -0,0 +1,383 @@ +"""R4 hierarchical routing: derive a signal's owner address from catalog spawn provenance. + +A worker's signal routes to its manager; a manager's signal routes to its orchestrator -- one +hop up the spawn edge, never further (a developer ruling: no layer is addressed its +grandchildren's noise). The address is read straight off the SENDER's own catalog row +(``spawned_by_session`` / ``spawned_by_lifecycle`` -- terminal_catalog.py:48-59), which is exactly +who spawned it; no second catalog lookup is needed to resolve "the manager's session id" because +that IS the sender's ``spawned_by_session``. + +``message_kind == "decision-item"`` is routed to the reserved ``architect`` role regardless of +spawn provenance (the queue itself is AQR Q3's job, not this leaf's -- only the routing target is +reserved here). +""" + +from __future__ import annotations + +from dataclasses import dataclass +from datetime import datetime + +from agents_remember.controlplane.operator_inbox_records import AgentRole, InboxMessageKind +from agents_remember.serving.terminal_catalog import TerminalCatalog, TerminalCatalogEntry + +# One hop up the spawn edge: the role a signal's SENDER was spawned as -> the role its routed +# owner carries. A sender spawned as anything else (orchestrator, strategist, reviewer, ...) has +# no owner-role mapping here -- the caller's explicit recipient_role stands, unrouted. +_OWNER_ROLE_BY_SENDER_SPAWN_ROLE: dict[str, AgentRole] = { + "worker": "manager", + "reviewer": "manager", + "curator": "manager", + "manager": "orchestrator", +} + + +@dataclass(frozen=True) +class RoutedOwner: + """The routed owner address for one signal row; every field ``None`` means "no route derived, + keep the caller's explicit recipient" -- this module never fabricates an address.""" + + role: AgentRole | None = None + agent_id: str | None = None + lifecycle_id: str | None = None + + +def _manager_owner(entry: TerminalCatalogEntry) -> RoutedOwner: + return RoutedOwner( + role="manager", + agent_id=entry.id, + lifecycle_id=entry.lifecycle_id, + ) + + +def _master_key(leaf_key: str | None) -> str | None: + if leaf_key is None: + return None + parts = leaf_key.split("/", 2) + if len(parts) != 3 or not all(parts): + return None + return "/".join(parts[:2]) + + +def signal_leaf_key( + catalog: TerminalCatalog, + *, + sender_agent_id: str | None, + leaf_key: str | None = None, +) -> str | None: + """Best current leaf/master anchor for a signal sender. + + A bound worker/reviewer supplies its leaf directly. An unbound successor seat can still inherit + the master anchor from its recorded manager, including a retired prior manager whose catalog + row remains available for provenance. + """ + if leaf_key is not None: + return leaf_key + sender = catalog.get(sender_agent_id) if sender_agent_id is not None else None + if sender is None: + return None + if sender.binding_leaf_key is not None: + return sender.binding_leaf_key + prior_manager = ( + catalog.get(sender.spawned_by_session) if sender.spawned_by_session is not None else None + ) + return prior_manager.binding_leaf_key if prior_manager is not None else None + + +def _direct_live_manager( + catalog: TerminalCatalog, sender_agent_id: str | None +) -> TerminalCatalogEntry | None: + sender = catalog.get(sender_agent_id) if sender_agent_id is not None else None + if sender is None or sender.spawned_by_session is None: + return None + manager = catalog.get(sender.spawned_by_session) + if ( + manager is None + or manager.kind != "harness" + or manager.status != "running" + or manager.binding_role != "manager" + ): + return None + return manager + + +def _live_managers(catalog: TerminalCatalog) -> list[TerminalCatalogEntry]: + return [ + entry + for entry in catalog.list() + if entry.kind == "harness" + and entry.status == "running" + and entry.binding_role == "manager" + ] + + +def _scoped_managers( + catalog: TerminalCatalog, + managers: list[TerminalCatalogEntry], + *, + route_leaf: str, +) -> list[TerminalCatalogEntry]: + route_master = _master_key(route_leaf) + linked_manager_ids = { + entry.spawned_by_session + for entry in catalog.list() + if entry.binding_leaf_key == route_leaf and entry.spawned_by_session is not None + } + return [ + manager + for manager in managers + if manager.id in linked_manager_ids + or manager.binding_leaf_key == route_leaf + or (route_master is not None and _master_key(manager.binding_leaf_key) == route_master) + ] + + +def derive_leaf_manager_owner( + catalog: TerminalCatalog, + *, + sender_agent_id: str | None, + leaf_key: str | None = None, +) -> RoutedOwner: + """Resolve a leaf signal to its current responsible manager at address time. + + A live direct manager remains authoritative. When that binding is stale, prefer a live manager + attached to the same qualified master or currently parenting a seat bound to the same leaf. + Only an unambiguous single live manager is used without a leaf/master anchor. If no concrete + current manager can be proven, return the role-only manager mailbox; never fall directly to an + orchestrator or architect because the ladder owns later escalation. + """ + direct_manager = _direct_live_manager(catalog, sender_agent_id) + if direct_manager is not None: + return _manager_owner(direct_manager) + + live_managers = _live_managers(catalog) + route_leaf = signal_leaf_key( + catalog, sender_agent_id=sender_agent_id, leaf_key=leaf_key + ) + if route_leaf is not None: + scoped = _scoped_managers(catalog, live_managers, route_leaf=route_leaf) + if scoped: + return _manager_owner( + max(scoped, key=lambda entry: (entry.last_attached_at, entry.created_at, entry.id)) + ) + return RoutedOwner(role="manager") + + +def _parsed_at(value: str | None) -> datetime | None: + if value is None: + return None + try: + return datetime.fromisoformat(value) + except ValueError: + return None + + +def _entry_progressed_after(entry: TerminalCatalogEntry, since: datetime) -> bool: + if entry.status == "running" and entry.turn_state == "working": + return True + timestamps = ( + entry.created_at, + entry.last_attached_at, + entry.turn_state_changed_at, + entry.landed_at, + ) + return any(at is not None and at > since for at in map(_parsed_at, timestamps)) + + +def _entry_carries_leaf_chain( + entry: TerminalCatalogEntry, + *, + leaf_key: str, + manager_agent_id: str | None, +) -> bool: + if entry.binding_leaf_key == leaf_key or ( + manager_agent_id is not None and entry.id == manager_agent_id + ): + return True + # A replacement may be unbound while another role occupies the leaf. Same-manager provenance + # alone is insufficient because one manager can drive parallel leaves; the explicit replacement + # leaf is the production discriminator (catalog cwd is fleet-wide and cannot distinguish work). + return bool( + manager_agent_id is not None + and entry.spawned_by_session == manager_agent_id + and entry.binding_role in ("worker", "reviewer", "curator") + and entry.leaf_key is None + and entry.replacement_for_leaf == leaf_key + ) + + +def _is_chain_progress( + entry: TerminalCatalogEntry, + *, + leaf_key: str, + manager_agent_id: str | None, + subject_agent_id: str | None, + since: datetime, +) -> bool: + if entry.id == subject_agent_id or entry.status not in ("running", "landed"): + return False + return _entry_carries_leaf_chain( + entry, + leaf_key=leaf_key, + manager_agent_id=manager_agent_id, + ) and _entry_progressed_after(entry, since) + + +def leaf_chain_has_progress( + catalog: TerminalCatalog, + *, + leaf_key: str, + subject_agent_id: str | None, + since: str, +) -> bool: + """Whether another observable seat carrying ``leaf_key`` progressed after ``since``. + + The chain includes exact-leaf seats, the current manager, and an unbound worker/reviewer/curator + whose catalog row explicitly names this leaf as its replacement target. + """ + since_at = _parsed_at(since) + if since_at is None: + return False + manager = derive_leaf_manager_owner( + catalog, sender_agent_id=subject_agent_id, leaf_key=leaf_key + ) + return any( + _is_chain_progress( + entry, + leaf_key=leaf_key, + manager_agent_id=manager.agent_id, + subject_agent_id=subject_agent_id, + since=since_at, + ) + for entry in catalog.list() + ) + + +def derive_signal_owner( + catalog: TerminalCatalog, + *, + sender_agent_id: str | None, + message_kind: InboxMessageKind, + leaf_key: str | None = None, +) -> RoutedOwner: + """The owner address for a signal from ``sender_agent_id``, or an empty :class:`RoutedOwner`.""" + if message_kind == "decision-item": + return RoutedOwner(role="architect") + if sender_agent_id is None: + return RoutedOwner() + entry = catalog.get(sender_agent_id) + if entry is None: + return RoutedOwner() + owner_role = _OWNER_ROLE_BY_SENDER_SPAWN_ROLE.get(entry.binding_role) + if owner_role is None: + return RoutedOwner() + if owner_role == "manager": + return derive_leaf_manager_owner( + catalog, sender_agent_id=sender_agent_id, leaf_key=leaf_key + ) + return RoutedOwner( + role=owner_role, + agent_id=entry.spawned_by_session, + lifecycle_id=entry.spawned_by_lifecycle, + ) + + +def _derive_spawn_owner( + catalog: TerminalCatalog, + *, + sender_agent_id: str | None, + message_kind: InboxMessageKind, +) -> RoutedOwner: + """One historical provenance hop for the escalation ladder only. + + Initial signal addressing uses :func:`derive_signal_owner` and refuses stale managers. Once a + manager has failed, the ladder still needs the recorded edge in order to climb past that dead + seat to its owner; replacing this with current-manager routing would erase the path it must walk. + """ + if message_kind == "decision-item": + return RoutedOwner(role="architect") + if sender_agent_id is None: + return RoutedOwner() + entry = catalog.get(sender_agent_id) + if entry is None or entry.spawn_role is None: + return RoutedOwner() + owner_role = _OWNER_ROLE_BY_SENDER_SPAWN_ROLE.get(entry.spawn_role) + if owner_role is None: + return RoutedOwner() + return RoutedOwner( + role=owner_role, + agent_id=entry.spawned_by_session, + lifecycle_id=entry.spawned_by_lifecycle, + ) + + +def is_seat_dead(catalog: TerminalCatalog, agent_id: str | None) -> bool: + """Whether ``agent_id`` cannot receive a delivery: unknown to the catalog, or not ``running`` + (260707-HFX2-L4). A row with no catalog trace at all (never spawned through the harness path, + a role-only mailbox) counts as dead here -- there is nothing live to skip TO it, so the ladder + walk (below) treats "no evidence of life" the same as "confirmed dead".""" + if agent_id is None: + return True + entry = catalog.get(agent_id) + return entry is None or entry.status != "running" + + +def derive_architect_owner(catalog: TerminalCatalog) -> RoutedOwner: + """The ladder's terminal addressee (ruled 2026-07-09): the LIVE architect seat when one is + attached, else the role-only architect mailbox. The developer is an authority label, never an + address -- a human-shaped mailbox cannot mechanically ack, which is how the 2026-07-09 storm's + rows became immortal. The architect seat acks custody and briefs the human; with no architect + seat attached the role-addressed row waits, level-triggered, for the next architect session + (delivery on appearance, or poll at session start).""" + for entry in catalog.list(): + if ( + entry.kind == "harness" + and entry.status == "running" + and entry.binding_role == "architect" + ): + return RoutedOwner( + role="architect", agent_id=entry.id, lifecycle_id=entry.lifecycle_id + ) + return RoutedOwner(role="architect") + + +def derive_skip_level_owner( + catalog: TerminalCatalog, + *, + sender_agent_id: str | None, + message_kind: InboxMessageKind, +) -> RoutedOwner: + """R2/R4 (260707-HFX2-L4): the SENDER's owner's owner -- two spawn-provenance hops, walking + PAST any dead node encountered along the way rather than landing a signal on it. + + This is deliberately a second, separate function rather than a parameter on + :func:`derive_signal_owner` (whose one-hop invariant a test locks: "no layer is addressed its + grandchildren's noise" -- that rule is about who ADDRESSES whom, not about how many hops THIS + walker takes to find a live address for the SAME sender). Two hops means: hop 1 is + ``derive_signal_owner(sender)`` (the ordinary owner -- the rung-1 addressee); hop 2 is + ``derive_signal_owner(hop-1's owner)`` (the owner's owner -- the rung-2/skip-level and R4 + grandparent-signal target). If landing on a hop is dead, the walk continues FROM that hop + exactly as if it had answered -- so a dead direct owner is transparently skipped (its own + owner becomes the effective rung-1 stand-in) and a dead owner's-owner is walked past too (the + walk never stops on a confirmed-dead address). A cycle or an exhausted chain (no further owner + role mapping -- e.g. the top is an orchestrator, which has none) returns whatever the walk last + resolved, ``RoutedOwner()`` if nothing did. + """ + seen: set[str] = set() + current_agent_id = sender_agent_id + hops_done = 0 + owner = RoutedOwner() + while True: + if current_agent_id is None or current_agent_id in seen: + return owner if hops_done >= 2 else RoutedOwner() + seen.add(current_agent_id) + owner = _derive_spawn_owner( + catalog, sender_agent_id=current_agent_id, message_kind=message_kind + ) + if owner.agent_id is None: + return owner + current_agent_id = owner.agent_id + hops_done += 1 + if hops_done >= 2 and not is_seat_dead(catalog, owner.agent_id): + return owner + if len(seen) > 64: # pathological-chain guard; never hangs on a corrupt catalog + return owner diff --git a/mcp/src/agents_remember/controlplane/store.py b/mcp/src/agents_remember/controlplane/store.py index bbee4a62..d3040ae4 100644 --- a/mcp/src/agents_remember/controlplane/store.py +++ b/mcp/src/agents_remember/controlplane/store.py @@ -117,6 +117,29 @@ def compact(self, lifecycle_id: str | None, *, now: datetime) -> int: self._replace(lifecycle_id, kept) return len(records) - len(kept) + def compact_current( + self, lifecycle_id: str | None, *, now: datetime, rewrite: bool = True + ) -> dict[str, GateRecord]: + """Fold + reclaim in ONE read: the live gate set with expired/consumed rows filtered out. + + 260707-HFX2-L12 F8/CS-6 D2: the projection previously did ``compact()`` (a full read + rewrite) + **then** ``current()`` (a second full read) per lifecycle per 1s tick. This folds and applies the + same keep-filter from a SINGLE read, so the projection sees the identical live set with no + double-fold. ``rewrite`` gates the (expensive) physical prune to a low cadence — the projection + output is keep-filtered every tick regardless, so on-disk reclamation lagging a tick is harmless. + """ + records = self.read(lifecycle_id) + if not records: + return {} + keep_ids = gate_keep_ids(records, now=now) + kept = [record for record in records if record.id in keep_ids] + if rewrite and len(kept) != len(records): + self._replace(lifecycle_id, kept) + latest: dict[str, GateRecord] = {} + for record in kept: + latest[record.id] = record + return latest + def lifecycle_ids(self) -> list[str | None]: """Lifecycle ids with gate logs, plus workspace when present.""" ids: list[str | None] = [] diff --git a/mcp/src/agents_remember/controlplane/supervisor_signals.py b/mcp/src/agents_remember/controlplane/supervisor_signals.py new file mode 100644 index 00000000..e84afa56 --- /dev/null +++ b/mcp/src/agents_remember/controlplane/supervisor_signals.py @@ -0,0 +1,201 @@ +"""Durable cooldown records for supervisor-owned pane/liveness signals.""" + +from __future__ import annotations + +import os +from datetime import datetime, timedelta +from pathlib import Path +from typing import Literal + +from pydantic import BaseModel, ConfigDict, Field, ValidationError + +from agents_remember.controlplane.inbox_backoff import require_redelivery_floor_seconds +from agents_remember.controlplane.operator_inbox_records import AgentRole, InboxDeliveryState + +SUPERVISOR_SIGNAL_SCHEMA = "ar-supervisor-signal/v1" +SupervisorSignalState = Literal["sent"] + + +class SupervisorSignalRecord(BaseModel): + """One supervisor signal actually posted to an owner inbox.""" + + model_config = ConfigDict(extra="forbid") + + schema_version: str = Field(default=SUPERVISOR_SIGNAL_SCHEMA, alias="schema") + id: str + ts: str + state: SupervisorSignalState = "sent" + targetAgentId: str | None = None + targetLifecycleId: str | None = None + targetRole: AgentRole | None = None + leafKey: str | None = None + seatRole: str | None = None + findingKind: str + detail: str + deliveryState: InboxDeliveryState + + +class SupervisorSignalCooldownStore: + """Append-only signal log used as the supervisor's cross-sweep cooldown memory. + + 260707-HFX2-L12 (CS-6 D2/D3): the cooldown check must NOT re-parse the whole + log once per finding per sweep -- that is the L7 accidental-quadratic freeze + reincarnated on the supervisor hot path (finding count F x log length L). The + sweep reads the log **once** (via :meth:`compact`, which also reclaims it) and + threads the resulting snapshot into every per-finding :meth:`in_cooldown` call + through ``records=``; the store is therefore read at most once per sweep, and + :meth:`compact` bounds it to one retention window of history on disk. + """ + + def __init__(self, observer_root: Path) -> None: + self._root = observer_root + + def log_path(self) -> Path: + return self._root / "workspace" / "supervisor-signals.jsonl" + + def read(self) -> list[SupervisorSignalRecord]: + """Read the log back as validated records, skipping any unparseable line. + + A torn/legacy/version-skew line is a durability event, not a reason to + freeze the supervisor sweep that folds this non-authoritative cooldown log + (mirrors ``ProviderMetricsStore.read_recent`` tolerance). + """ + path = self.log_path() + if not path.exists(): + return [] + records: list[SupervisorSignalRecord] = [] + for line in path.read_text(encoding="utf-8").splitlines(): + if not line.strip(): + continue + try: + records.append(SupervisorSignalRecord.model_validate_json(line)) + except ValidationError: + continue + return records + + def append(self, record: SupervisorSignalRecord) -> None: + path = self.log_path() + path.parent.mkdir(parents=True, exist_ok=True) + with path.open("a", encoding="utf-8") as handle: + handle.write(record.model_dump_json(by_alias=True, exclude_none=True) + "\n") + + def last_sent( + self, + *, + target_agent_id: str | None, + target_lifecycle_id: str | None, + target_role: AgentRole | None, + leaf_key: str | None, + finding_kind: str, + detail: str, + seat_role: str | None = None, + records: list[SupervisorSignalRecord] | None = None, + ) -> SupervisorSignalRecord | None: + source = self.read() if records is None else records + matches = [ + record + for record in source + if record.state == "sent" + and record.targetAgentId == target_agent_id + and record.targetLifecycleId == target_lifecycle_id + and record.targetRole == target_role + and record.leafKey == leaf_key + and record.seatRole == seat_role + and record.findingKind == finding_kind + and record.detail == detail + ] + return max(matches, key=lambda record: record.ts, default=None) + + def in_cooldown( + self, + *, + target_agent_id: str | None, + target_lifecycle_id: str | None, + target_role: AgentRole | None, + leaf_key: str | None, + finding_kind: str, + detail: str, + now: datetime, + cooldown_seconds: float, + seat_role: str | None = None, + records: list[SupervisorSignalRecord] | None = None, + ) -> bool: + """Whether an identical signal was sent within the cooldown window. + + Pass ``records`` (the sweep's one-read snapshot) so this stays O(1) per + finding instead of a full-file re-parse; ``None`` falls back to a fresh + read for the standalone / test path. + """ + floor = require_redelivery_floor_seconds( + cooldown_seconds, owner="supervisor signal cooldown" + ) + previous = self.last_sent( + target_agent_id=target_agent_id, + target_lifecycle_id=target_lifecycle_id, + target_role=target_role, + leaf_key=leaf_key, + seat_role=seat_role, + finding_kind=finding_kind, + detail=detail, + records=records, + ) + if previous is None: + return False + try: + elapsed = (now - datetime.fromisoformat(previous.ts)).total_seconds() + except ValueError: + return False + return elapsed < floor + + def compact( + self, *, now: datetime, retain_seconds: float + ) -> tuple[int, list[SupervisorSignalRecord]]: + """Reclaim records older than the retention window; return ``(removed, kept)``. + + A record older than ``retain_seconds`` can never satisfy ``elapsed < floor`` + in :meth:`in_cooldown` (the floor is the same window), so dropping it is + provenance-safe -- it can no longer suppress any signal. Records with an + unparseable ``ts`` are always kept (never silently aged out). The rewrite is + atomic (tmp + ``os.replace``); the observer server is the single writer. + The returned ``kept`` list is the sweep's cooldown snapshot, so the caller + reads + compacts the log in one pass. + """ + records = self.read() + if not records: + return 0, [] + cutoff = now - timedelta(seconds=retain_seconds) + kept: list[SupervisorSignalRecord] = [] + for record in records: + try: + ts = datetime.fromisoformat(record.ts) + except ValueError: + kept.append(record) + continue + try: + fresh = ts >= cutoff + except TypeError: + # tz-naive vs tz-aware mismatch: keep rather than risk dropping a live row. + kept.append(record) + continue + if fresh: + kept.append(record) + removed = len(records) - len(kept) + if removed: + self._replace(kept) + return removed, kept + + def _replace(self, records: list[SupervisorSignalRecord]) -> None: + path = self.log_path() + if not records: + path.unlink(missing_ok=True) + return + path.parent.mkdir(parents=True, exist_ok=True) + tmp = path.with_name(f"{path.name}.{os.getpid()}.tmp") + tmp.write_text( + "\n".join( + record.model_dump_json(by_alias=True, exclude_none=True) for record in records + ) + + "\n", + encoding="utf-8", + ) + os.replace(tmp, path) diff --git a/mcp/src/agents_remember/install/runtime.py b/mcp/src/agents_remember/install/runtime.py index 3f799ff7..d5b59efd 100644 --- a/mcp/src/agents_remember/install/runtime.py +++ b/mcp/src/agents_remember/install/runtime.py @@ -24,7 +24,11 @@ agentic_settings_path, default_agentic_settings_seed_text, ) -from agents_remember.mcp.config import DEFAULT_PROVIDER_SETUP_SECONDS, McpRuntimeConfig +from agents_remember.mcp.config import ( + DEFAULT_PROVIDER_SETUP_SECONDS, + McpRuntimeConfig, + reload_provider_authority, +) from agents_remember.providers import lifecycle from agents_remember.providers.settings import lifecycle_settings_from_config @@ -570,7 +574,13 @@ def install_runtime_from_config( timeout = provider_deps_timeout or config.timeout_caps.get( "providerSetupSeconds", DEFAULT_PROVIDER_SETUP_SECONDS ) - provider_settings = lifecycle_settings_from_config(config) + # Containment R1 (260707-HFX-L1): the watcher rebind's stop→start cycle is a + # launch path — derive its settings from the LIVE on-disk authority, never the + # boot snapshot. An empty (or unreadable: fail-closed) live map disables the + # rebind while the runtime install itself proceeds. + provider_settings = lifecycle_settings_from_config( + reload_provider_authority(config).apply(config) + ) if source_root is not None: summary = install_runtime( source_root.resolve(), diff --git a/mcp/src/agents_remember/kernel/agentic_settings.py b/mcp/src/agents_remember/kernel/agentic_settings.py index 7b0dddbe..b34dd097 100644 --- a/mcp/src/agents_remember/kernel/agentic_settings.py +++ b/mcp/src/agents_remember/kernel/agentic_settings.py @@ -49,6 +49,10 @@ make_gate_policy, named_gate_policy, ) +from agents_remember.controlplane.inbox_backoff import ( + DEFAULT_RATE_LIMIT_SECONDS, + MIN_REDELIVERY_INTERVAL_SECONDS, +) from agents_remember.controlplane.records import GateKind, coerce_gate_kind from agents_remember.errors import AgentsRememberError from agents_remember.serving.harnesses import HARNESSES, Harness @@ -69,6 +73,9 @@ class AgenticSettingsError(AgentsRememberError): "concurrency", "spawn", "harnesses", + "expectations", + "supervisor", + "escalation", } ) # One ``orchestration.harnesses.`` entry (260703-L16): define a NEW harness or override a @@ -95,13 +102,105 @@ class AgenticSettingsError(AgentsRememberError): # The dispatch-time complexity scale (blast radius x novelty x size) the loop # thresholds are expressed on (l-01 The Three-Party Loop). COMPLEXITY_SCALE = ("low", "medium", "high") -# The six portable role lifecycles the l-01 registry defines. -KNOWN_ROLES = frozenset({"orchestrator", "designer", "strategist", "manager", "worker", "reviewer"}) +# The nine portable role lifecycles the l-01 registry defines. +KNOWN_ROLES = frozenset( + { + "architect", + "orchestrator", + "designer", + "strategist", + "manager", + "worker", + "curator", + "reviewer", + "system-specialist", + } +) KNOWN_ROLE_KNOB_FIELDS = frozenset( {"harness", "model", "effort", "launchArgs", "promptKeywords", "sessionCommands"} ) KNOWN_CONCURRENCY_FIELDS = frozenset({"maxParallelMasters", "maxParallelLeaves", "maxSubAgents"}) KNOWN_SPAWN_FIELDS = frozenset({"harness"}) +# R1/R5 (260707-HFX2-L2): the deterministic supervisor sweep's own knobs -- interval, enable +# flag, self-liveness staleness cutoff, inbox redelivery rate limit, and owner-signal cooldown. +KNOWN_SUPERVISOR_FIELDS = frozenset( + { + "enabled", + "intervalSeconds", + "staleCutoffSeconds", + "redeliverRateLimitSeconds", + "signalCooldownSeconds", + "redeliverBudget", + "escalationBudget", + } +) +# R1 (260707-HFX2-L4): the escalation ladder's own knobs -- per-kind ack SLA, per-rung timings, +# the renudge rate limit (reusing the OrchestrationNudgeStore rate-limit pattern), and the rung a +# silent seat is marked suspect-for-respawn at (R3). +KNOWN_ESCALATION_FIELDS = frozenset( + {"slaSeconds", "rungSeconds", "nudgeRateLimitSeconds", "respawnAfterRung"} +) +DEFAULT_SUPERVISOR_INTERVAL_SECONDS = 10.0 +DEFAULT_SUPERVISOR_STALE_CUTOFF_SECONDS = 60.0 +DEFAULT_SUPERVISOR_REDELIVER_BUDGET = 1 +# CS-6 D1 (260707-HFX2-L12): per-sweep cap on escalation-rung emission, the twin of the redeliver +# budget. Bounds the synchronous hosted pastes + escalation.rung event appends one sweep can do; +# deferred rows re-fire next sweep (rung_due is level-triggered) so nothing is lost. +DEFAULT_SUPERVISOR_ESCALATION_BUDGET = 250 +# R2 (260707-HFX2-L1): the expectation-row kinds every dispatch surface writes a durable +# what-must-happen-by-when row for, and their default SLAs (schema: docs/reference/settings-json.md, +# Orchestration Expectations). Kept as a plain string set here (not imported from +# controlplane.expectation_rows) to avoid a kernel<->controlplane import cycle; the two must be kept +# in sync -- ``ExpectationKind`` in expectation_rows.py is the sole other definition. +KNOWN_EXPECTATION_KINDS = frozenset({"briefed-by", "turn-report-by", "verdict-by", "ack-by"}) +KNOWN_EXPECTATIONS_FIELDS = frozenset({"defaults"}) +DEFAULT_EXPECTATION_SLA_SECONDS: dict[str, float] = { + "briefed-by": 120.0, + "turn-report-by": 3600.0, + "verdict-by": 1800.0, + # Mirrors AGENT_PICKUP_TTL_SECONDS (interaction_retention.py) -- the existing dashboard + # pickup-staleness convention for an unacked signal. + "ack-by": 300.0, +} + +# R1 (260707-HFX2-L4): the escalation ladder's own per-``message_kind`` ack SLA -- how long a +# PENDING operator-inbox row sits unacked, past HFX2-L2's own redelivery attempts, before the +# ladder walker (``controlplane/escalation_ladder.py``) fires rung 1. Kept as a plain string set +# (not imported from operator_inbox_records) for the same kernel<->controlplane cycle reason as +# ``KNOWN_EXPECTATION_KINDS``; the two must be kept in sync with ``InboxMessageKind``. +KNOWN_ESCALATION_MESSAGE_KINDS = frozenset( + { + "message", + "gate-response", + "turn-report", + "master-handover", + "nudge", + "escalation", + "degradation-alert", + "decision-item", + "decision-ruling", + } +) +DEFAULT_ESCALATION_SLA_SECONDS: dict[str, float] = { + "message": 600.0, + "gate-response": 600.0, + "turn-report": 1800.0, + "master-handover": 1800.0, + "nudge": 300.0, + "escalation": 300.0, + "degradation-alert": 300.0, + "decision-item": 900.0, + "decision-ruling": 900.0, +} +# Conservative-by-default rung timings (R1): seconds a row may sit at its CURRENT rung, past its +# ``escalatedAt`` anchor, before the walker advances it to the next one. Rung 1 = renudge; rung 2 = +# skip-level; rung 3 = developer attention (terminal -- the walker re-surfaces at this cadence but +# never advances past it, R5). +DEFAULT_ESCALATION_RUNG_SECONDS: dict[int, float] = {1: 300.0, 2: 900.0, 3: 1800.0} +KNOWN_ESCALATION_RUNGS = (1, 2, 3) +# R3: a seat addressed by a row that reaches this rung with no ack and no catalog turn-state +# change is marked suspect for respawn -- rung 2 (skip-level failed to raise it) per the leaf spec. +DEFAULT_RESPAWN_AFTER_RUNG = 2 # The BUILTIN registry ids (claude|codex|pi). Harness references (roles..harness, # spawn.harness) are validated against the EFFECTIVE id set -- these plus any @@ -181,6 +280,67 @@ class ConcurrencySettings: max_sub_agents: int | None = None +@dataclass(frozen=True) +class ExpectationSettings: + """``orchestration.expectations`` -- per-kind default SLA seconds (R2, 260707-HFX2-L1). + + Every dispatch surface (spawn, leaf dispatch, gate open, signal post) writes a durable + expectation row at write time; the SLA (seconds until ``dueAt``) is configurable per kind + here, defaulting to :data:`DEFAULT_EXPECTATION_SLA_SECONDS`. + """ + + sla_seconds: dict[str, float] = field( + default_factory=lambda: dict(DEFAULT_EXPECTATION_SLA_SECONDS) + ) + + def sla_for(self, kind: str) -> float: + return self.sla_seconds.get(kind, DEFAULT_EXPECTATION_SLA_SECONDS[kind]) + + +@dataclass(frozen=True) +class SupervisorSettings: + """``orchestration.supervisor`` -- the deterministic sweep's knobs (R1/R5, 260707-HFX2-L2). + + ``redeliver_rate_limit_seconds`` of ``None`` inherits ``OperatorInboxStore.list_redeliverable``'s + own default. ``signal_cooldown_seconds`` controls supervisor pane/seat-liveness signal posts. + """ + + enabled: bool = True + interval_seconds: float = DEFAULT_SUPERVISOR_INTERVAL_SECONDS + stale_cutoff_seconds: float = DEFAULT_SUPERVISOR_STALE_CUTOFF_SECONDS + redeliver_rate_limit_seconds: float | None = None + signal_cooldown_seconds: float = DEFAULT_RATE_LIMIT_SECONDS + redeliver_budget: int = DEFAULT_SUPERVISOR_REDELIVER_BUDGET + escalation_budget: int = DEFAULT_SUPERVISOR_ESCALATION_BUDGET + + +@dataclass(frozen=True) +class EscalationSettings: + """``orchestration.escalation`` -- the P-15 tier-3 ladder's own knobs (R1, 260707-HFX2-L4). + + ``sla_seconds`` gates rung 1 (how long a pending row sits unacked, past ``escalatedAt``, + before the first renudge); ``rung_seconds`` gates every rung's OWN dwell time thereafter + (keyed 1/2/3, re-anchored at every transition -- see ``OperatorInboxStore.advance_rung``). + """ + + sla_seconds: dict[str, float] = field( + default_factory=lambda: dict(DEFAULT_ESCALATION_SLA_SECONDS) + ) + rung_seconds: dict[int, float] = field( + default_factory=lambda: dict(DEFAULT_ESCALATION_RUNG_SECONDS) + ) + nudge_rate_limit_seconds: int = 900 + respawn_after_rung: int = DEFAULT_RESPAWN_AFTER_RUNG + + def sla_for(self, message_kind: str) -> float: + return self.sla_seconds.get( + message_kind, DEFAULT_ESCALATION_SLA_SECONDS.get(message_kind, 300.0) + ) + + def rung_dwell(self, rung: int) -> float: + return self.rung_seconds.get(rung, DEFAULT_ESCALATION_RUNG_SECONDS.get(rung, 900.0)) + + @dataclass(frozen=True) class AgenticSettings: """The merged, typed agentic settings for one read (global <- local).""" @@ -198,6 +358,9 @@ class AgenticSettings: # (see :meth:`resolved_role_knobs`). Level vocabulary matches ``loops.perLevel``. roles_per_level: dict[str, dict[str, RoleKnobs]] = field(default_factory=dict) concurrency: ConcurrencySettings = field(default_factory=ConcurrencySettings) + expectations: ExpectationSettings = field(default_factory=ExpectationSettings) + supervisor: SupervisorSettings = field(default_factory=SupervisorSettings) + escalation: EscalationSettings = field(default_factory=EscalationSettings) spawn_harness: str | None = None # The EFFECTIVE harness registry (260703-L16): the builtin defaults merged with the # ``orchestration.harnesses`` family -- new ids added, builtin ids possibly pre-customized. @@ -418,6 +581,18 @@ def _require_positive_int(raw: object, owner: str, source: str) -> int: return raw +def _require_positive_number(raw: object, owner: str, source: str) -> float: + if isinstance(raw, bool) or not isinstance(raw, (int, float)) or raw <= 0: + raise AgenticSettingsError(f"{owner} must be a positive number: {source}") + return float(raw) + + +def _require_bool(raw: object, owner: str, source: str) -> bool: + if not isinstance(raw, bool): + raise AgenticSettingsError(f"{owner} must be a boolean: {source}") + return raw + + def _require_string_list(raw: object, owner: str, source: str) -> tuple[str, ...]: """A free-form list of non-empty strings (shape-checked only; content never validated). @@ -491,6 +666,9 @@ def _parse_orchestration( raw.get("rolesPerLevel"), source=source, harness_ids=harness_ids ), concurrency=_parse_concurrency(raw.get("concurrency"), source=source), + expectations=_parse_expectations(raw.get("expectations"), source=source), + supervisor=_parse_supervisor(raw.get("supervisor"), source=source), + escalation=_parse_escalation(raw.get("escalation"), source=source), spawn_harness=_parse_spawn(raw.get("spawn"), source=source, harness_ids=harness_ids), harnesses=harnesses, sources=sources, @@ -624,6 +802,15 @@ def _merged_harness( for field_name, declared_value in declared.items(): if declared_value is not None: overrides[field_name] = declared_value + if ( + base is not None + and parsed.effort_flag is not None + and parsed.effort_flag != base.effort_flag + ): + # The Codex builtin's value template belongs to its ``--config`` vehicle. Replacing that + # flag through settings restores the ordinary two-argv-element mapping instead of leaking + # ``model_reasoning_effort=...`` into an unrelated custom flag. + overrides["effort_flag_value_template"] = None merged = replace(fallback, **overrides) _refuse_unpaired_vehicles(merged, owner, source) _refuse_bad_effort_template(merged, owner, source) @@ -978,6 +1165,178 @@ def _parse_concurrency(raw: object, *, source: str) -> ConcurrencySettings: return ConcurrencySettings(**parsed) +def _parse_expectations(raw: object, *, source: str) -> ExpectationSettings: + """``orchestration.expectations.defaults``: per-kind SLA seconds, hyphenated kind keys + (matching the expectation-row kind vocabulary), overriding :data:`DEFAULT_EXPECTATION_SLA_SECONDS` + entry-by-entry -- an omitted kind keeps its documented default.""" + if raw is None: + return ExpectationSettings() + block = _require_object(raw, "orchestration.expectations", source) + _refuse_unknown(block, KNOWN_EXPECTATIONS_FIELDS, "orchestration.expectations", source) + sla_seconds = dict(DEFAULT_EXPECTATION_SLA_SECONDS) + raw_defaults = block.get("defaults") + if raw_defaults is not None: + defaults = _require_object( + raw_defaults, "orchestration.expectations.defaults", source + ) + for kind, value in defaults.items(): + if kind not in KNOWN_EXPECTATION_KINDS: + allowed = ", ".join(sorted(KNOWN_EXPECTATION_KINDS)) + raise AgenticSettingsError( + f"orchestration.expectations.defaults key {kind!r} is not a known " + f"expectation kind; allowed: {allowed}: {source}" + ) + owner = f"orchestration.expectations.defaults.{kind}" + if isinstance(value, bool) or not isinstance(value, (int, float)) or value <= 0: + raise AgenticSettingsError(f"{owner} must be a positive number of seconds: {source}") + sla_seconds[kind] = float(value) + return ExpectationSettings(sla_seconds=sla_seconds) + + +def _parse_supervisor(raw: object, *, source: str) -> SupervisorSettings: + """``orchestration.supervisor``: the deterministic sweep's own knobs (R1/R5). + + Absent -> the documented defaults (enabled, 10s interval, 60s staleness cutoff, inbox-store + redelivery rate limit inherited). + """ + if raw is None: + return SupervisorSettings() + block = _require_object(raw, "orchestration.supervisor", source) + _refuse_unknown(block, KNOWN_SUPERVISOR_FIELDS, "orchestration.supervisor", source) + enabled = ( + _require_bool(block["enabled"], "orchestration.supervisor.enabled", source) + if "enabled" in block + else True + ) + interval_seconds = ( + _require_positive_number( + block["intervalSeconds"], "orchestration.supervisor.intervalSeconds", source + ) + if "intervalSeconds" in block + else DEFAULT_SUPERVISOR_INTERVAL_SECONDS + ) + stale_cutoff_seconds = ( + _require_positive_number( + block["staleCutoffSeconds"], "orchestration.supervisor.staleCutoffSeconds", source + ) + if "staleCutoffSeconds" in block + else DEFAULT_SUPERVISOR_STALE_CUTOFF_SECONDS + ) + redeliver_rate_limit_seconds = ( + _require_supervisor_floor_seconds( + block["redeliverRateLimitSeconds"], + "orchestration.supervisor.redeliverRateLimitSeconds", + source, + ) + if "redeliverRateLimitSeconds" in block + else None + ) + signal_cooldown_seconds = ( + _require_supervisor_floor_seconds( + block["signalCooldownSeconds"], + "orchestration.supervisor.signalCooldownSeconds", + source, + ) + if "signalCooldownSeconds" in block + else DEFAULT_RATE_LIMIT_SECONDS + ) + redeliver_budget = ( + _require_positive_int( + block["redeliverBudget"], "orchestration.supervisor.redeliverBudget", source + ) + if "redeliverBudget" in block + else DEFAULT_SUPERVISOR_REDELIVER_BUDGET + ) + escalation_budget = ( + _require_positive_int( + block["escalationBudget"], "orchestration.supervisor.escalationBudget", source + ) + if "escalationBudget" in block + else DEFAULT_SUPERVISOR_ESCALATION_BUDGET + ) + return SupervisorSettings( + enabled=enabled, + interval_seconds=interval_seconds, + stale_cutoff_seconds=stale_cutoff_seconds, + redeliver_rate_limit_seconds=redeliver_rate_limit_seconds, + signal_cooldown_seconds=signal_cooldown_seconds, + redeliver_budget=redeliver_budget, + escalation_budget=escalation_budget, + ) + + +def _require_supervisor_floor_seconds(raw: object, owner: str, source: str) -> float: + value = _require_positive_number(raw, owner, source) + if value < MIN_REDELIVERY_INTERVAL_SECONDS: + raise AgenticSettingsError( + f"{owner} must be at least {MIN_REDELIVERY_INTERVAL_SECONDS:g} seconds: {source}" + ) + return value + + +def _parse_escalation(raw: object, *, source: str) -> EscalationSettings: + """``orchestration.escalation`` (R1, 260707-HFX2-L4): per-``message_kind`` ack SLAs, per-rung + dwell timings, the renudge rate limit, and the respawn-after-rung threshold. Absent -> the + documented conservative defaults.""" + if raw is None: + return EscalationSettings() + block = _require_object(raw, "orchestration.escalation", source) + _refuse_unknown(block, KNOWN_ESCALATION_FIELDS, "orchestration.escalation", source) + sla_seconds = dict(DEFAULT_ESCALATION_SLA_SECONDS) + raw_sla = block.get("slaSeconds") + if raw_sla is not None: + sla_block = _require_object(raw_sla, "orchestration.escalation.slaSeconds", source) + for kind, value in sla_block.items(): + if kind not in KNOWN_ESCALATION_MESSAGE_KINDS: + allowed = ", ".join(sorted(KNOWN_ESCALATION_MESSAGE_KINDS)) + raise AgenticSettingsError( + f"orchestration.escalation.slaSeconds key {kind!r} is not a known " + f"message kind; allowed: {allowed}: {source}" + ) + owner = f"orchestration.escalation.slaSeconds.{kind}" + sla_seconds[kind] = _require_positive_number(value, owner, source) + rung_seconds = dict(DEFAULT_ESCALATION_RUNG_SECONDS) + raw_rungs = block.get("rungSeconds") + if raw_rungs is not None: + rung_block = _require_object(raw_rungs, "orchestration.escalation.rungSeconds", source) + for raw_rung, value in rung_block.items(): + try: + rung = int(raw_rung) + except (TypeError, ValueError): + rung = -1 + if rung not in KNOWN_ESCALATION_RUNGS: + allowed = ", ".join(str(r) for r in KNOWN_ESCALATION_RUNGS) + raise AgenticSettingsError( + f"orchestration.escalation.rungSeconds key {raw_rung!r} must be one of: " + f"{allowed}: {source}" + ) + owner = f"orchestration.escalation.rungSeconds.{rung}" + rung_seconds[rung] = _require_positive_number(value, owner, source) + nudge_rate_limit_seconds = 900 + if "nudgeRateLimitSeconds" in block: + nudge_rate_limit_seconds = _require_positive_int( + block["nudgeRateLimitSeconds"], + "orchestration.escalation.nudgeRateLimitSeconds", + source, + ) + respawn_after_rung = DEFAULT_RESPAWN_AFTER_RUNG + if "respawnAfterRung" in block: + respawn_after_rung = _require_positive_int( + block["respawnAfterRung"], "orchestration.escalation.respawnAfterRung", source + ) + if respawn_after_rung not in KNOWN_ESCALATION_RUNGS: + allowed = ", ".join(str(r) for r in KNOWN_ESCALATION_RUNGS) + raise AgenticSettingsError( + f"orchestration.escalation.respawnAfterRung must be one of: {allowed}: {source}" + ) + return EscalationSettings( + sla_seconds=sla_seconds, + rung_seconds=rung_seconds, + nudge_rate_limit_seconds=nudge_rate_limit_seconds, + respawn_after_rung=respawn_after_rung, + ) + + def _parse_spawn(raw: object, *, source: str, harness_ids: tuple[str, ...] | None) -> str | None: if raw is None: return None diff --git a/mcp/src/agents_remember/kernel/coordination_context/contracts.py b/mcp/src/agents_remember/kernel/coordination_context/contracts.py index ef75d76f..67955cf4 100644 --- a/mcp/src/agents_remember/kernel/coordination_context/contracts.py +++ b/mcp/src/agents_remember/kernel/coordination_context/contracts.py @@ -3,11 +3,11 @@ from pathlib import Path from typing import Any +from agents_remember.worktrees.leaf_refs import resolve_leaf_enclosure_contract_for_ref from agents_remember.worktrees.task_resolver import ( SERIES_CONTRACT_FILENAME, is_archived_path, resolve_active_task_root, - resolve_leaf_enclosure_contract, series_contract_path, ) from agents_remember.worktrees.worktree_contract import ( @@ -56,7 +56,7 @@ def find_task_contract( leaf_id: str | None = None, ) -> Path | None: if leaf_id: - return resolve_leaf_enclosure_contract( + return resolve_leaf_enclosure_contract_for_ref( coordination_root, code_repository_name, task_name, diff --git a/mcp/src/agents_remember/mcp/__init__.py b/mcp/src/agents_remember/mcp/__init__.py index 95619269..0f1de166 100644 --- a/mcp/src/agents_remember/mcp/__init__.py +++ b/mcp/src/agents_remember/mcp/__init__.py @@ -8,4 +8,4 @@ # Single source of truth: the installed package metadata (mcp/pyproject.toml). SERVER_VERSION = version("agents-remember-mcp") except PackageNotFoundError: # running from a source checkout without an install - SERVER_VERSION = "3.0.0rc3" + SERVER_VERSION = "3.0.0rc4" diff --git a/mcp/src/agents_remember/mcp/config.py b/mcp/src/agents_remember/mcp/config.py index 5dc698de..cca5263f 100644 --- a/mcp/src/agents_remember/mcp/config.py +++ b/mcp/src/agents_remember/mcp/config.py @@ -4,7 +4,7 @@ import json import warnings -from dataclasses import dataclass, field +from dataclasses import dataclass, field, replace from pathlib import Path from typing import Any @@ -16,6 +16,11 @@ load_agentic_settings, parse_gate_delegation, ) +from agents_remember.mcp.provider_degradation_settings import ( + ProviderDegradationSettings, + ProviderDegradationSettingsError, + parse_provider_degradation_settings, +) from agents_remember.providers.identity import ( explicit_provider_instance_id, provider_instance_id, @@ -37,6 +42,17 @@ # Same fail-loud discipline as timeoutCaps: a typo ("autostart") must surface at # boot, not silently leave the daemon unsupervised. KNOWN_DASHBOARD_FIELDS = frozenset({"autoStart", "port"}) +# 260707-HFX2-L11: the auto-land hooks are boot-snapshot, MCP-authority-file settings (unlike +# orchestration.gateDelegation, which moved to the global agentic settings file). The legacy +# autoRetire* names are accepted as migration aliases for existing local authority files. +KNOWN_RETIREMENT_FIELDS = frozenset( + { + "autoLandOnIntegration", + "autoLandOnFinalize", + "autoRetireOnIntegration", + "autoRetireOnFinalize", + } +) # The agentic orchestration family moved to the coordinator's global settings # file (260703-L13); the authority file keeps ONLY the one-cycle gateDelegation # legacy fallback. loops/roles/concurrency/spawn here fail loud, pointing at @@ -82,6 +98,18 @@ class OrchestrationSettings: require_reviewer_verdict_at_seams: bool = False +@dataclass(frozen=True) +class RetirementSettings: + """The optional ``retirement`` settings object: auto-land hook gates. + + Both default ON: a completed leaf/master lands spent chats into an inspectable non-active + archive. Explicit retire / group cleanup remains the path that closes sessions. + """ + + auto_land_on_integration: bool = True + auto_land_on_finalize: bool = True + + @dataclass(frozen=True) class McpRuntimeConfig: config_path: Path @@ -95,6 +123,10 @@ class McpRuntimeConfig: benchmarks_enabled: bool = False dashboard: DashboardSettings = field(default_factory=DashboardSettings) orchestration: OrchestrationSettings = field(default_factory=OrchestrationSettings) + provider_degradation: ProviderDegradationSettings = field( + default_factory=ProviderDegradationSettings + ) + retirement: RetirementSettings = field(default_factory=RetirementSettings) @property def allowed_repo_ids(self) -> tuple[str, ...]: @@ -116,6 +148,77 @@ def load_config(config_path: str | Path) -> McpRuntimeConfig: return config_from_mapping(data, path) +@dataclass(frozen=True) +class ProviderAuthority: + """The providers map re-read from the on-disk authority settings. + + Containment R1 (260707-HFX-L1): the boot-snapshot config is NOT launch + authority. A server process loads its config once and closes over it, so + editing the authority file to ``"providers": {}`` — the operator's only + fleet-wide kill-switch — previously changed nothing until every running + server restarted. Launch-capable operations re-read the file through this + type instead. ``error`` carries the fail-closed reason when the file could + not be read or parsed: callers must treat that as "no launch authority", + never fall back to the snapshot. Stopping and cleanup are never gated. + """ + + providers: dict[str, ProviderScope] + source_path: Path + error: str | None = None + + def apply(self, config: McpRuntimeConfig) -> McpRuntimeConfig: + """The boot config with the live providers map swapped in.""" + return replace(config, providers=dict(self.providers)) + + +def reload_provider_authority(config: McpRuntimeConfig) -> ProviderAuthority: + """Re-read only the providers map from the authority settings file.""" + path = config.config_path + try: + data = json.loads(path.read_text(encoding="utf-8")) + except (OSError, json.JSONDecodeError) as error: + return ProviderAuthority( + providers={}, + source_path=path, + error=f"cannot re-read MCP authority settings: {path}: {error}", + ) + if not isinstance(data, dict): + return ProviderAuthority( + providers={}, + source_path=path, + error=f"MCP authority settings must be a JSON object: {path}", + ) + try: + providers = parse_providers( + data.get("providers", {}), + config.coordination_root, + config.workspace_root, + ) + except ConfigError as error: + return ProviderAuthority(providers={}, source_path=path, error=str(error)) + return ProviderAuthority(providers=providers, source_path=path) + + +def require_provider_launch_authority(config: McpRuntimeConfig, *, operation: str) -> McpRuntimeConfig: + """Gate a provider-launching operation on the on-disk authority (fail-closed). + + Returns the boot config with the live providers map applied; raises + ``ConfigError`` when the authority file disables providers or cannot be + read. Stop/status/cleanup paths must not call this — stopping is always + legal. + """ + authority = reload_provider_authority(config) + if authority.error is not None: + raise ConfigError(f"{operation} refused (containment R1, fail-closed): {authority.error}") + if not authority.providers: + raise ConfigError( + f"{operation} refused: providers are disabled in the on-disk authority settings " + f"({authority.source_path}); the boot snapshot ({sorted(config.providers)}) is not " + "launch authority (containment R1). stop/status/cleanup remain available." + ) + return authority.apply(config) + + def require_config_path(config_path: str | Path) -> Path: if not config_path: raise ConfigError("--config is required") @@ -157,11 +260,16 @@ def config_from_mapping(data: dict[str, Any], config_path: Path) -> McpRuntimeCo timeout_caps = parse_timeout_caps(data.get("timeoutCaps", {})) benchmarks_enabled = parse_benchmarks_enabled(data.get("benchmarksEnabled", False)) dashboard = parse_dashboard_settings(data.get("dashboard")) + try: + provider_degradation = parse_provider_degradation_settings(data.get("providerDegradation")) + except ProviderDegradationSettingsError as error: + raise ConfigError(str(error)) from error orchestration = parse_orchestration_settings( data.get("orchestration"), coordination_root=coordination_root, config_path=config_path, ) + retirement = parse_retirement_settings(data.get("retirement")) return McpRuntimeConfig( config_path=config_path, @@ -175,6 +283,8 @@ def config_from_mapping(data: dict[str, Any], config_path: Path) -> McpRuntimeCo benchmarks_enabled=benchmarks_enabled, dashboard=dashboard, orchestration=orchestration, + provider_degradation=provider_degradation, + retirement=retirement, ) @@ -341,6 +451,29 @@ def parse_dashboard_settings(raw: object) -> DashboardSettings: return DashboardSettings(auto_start=auto_start, port=port) +def parse_retirement_settings(raw: object) -> RetirementSettings: + if raw is None: + return RetirementSettings() + if not isinstance(raw, dict): + raise ConfigError("retirement settings must be an object") + unknown = sorted(set(raw) - KNOWN_RETIREMENT_FIELDS) + if unknown: + allowed = ", ".join(sorted(KNOWN_RETIREMENT_FIELDS)) + unknown_text = ", ".join(unknown) + raise ConfigError(f"unsupported retirement setting(s): {unknown_text}; allowed: {allowed}") + for key in sorted(KNOWN_RETIREMENT_FIELDS): + if key in raw and not isinstance(raw[key], bool): + raise ConfigError(f"retirement.{key} must be a boolean") + auto_land_on_integration = raw.get( + "autoLandOnIntegration", raw.get("autoRetireOnIntegration", True) + ) + auto_land_on_finalize = raw.get("autoLandOnFinalize", raw.get("autoRetireOnFinalize", True)) + return RetirementSettings( + auto_land_on_integration=auto_land_on_integration, + auto_land_on_finalize=auto_land_on_finalize, + ) + + def parse_orchestration_settings( raw: object, *, diff --git a/mcp/src/agents_remember/mcp/provider_degradation_settings.py b/mcp/src/agents_remember/mcp/provider_degradation_settings.py new file mode 100644 index 00000000..f01cf660 --- /dev/null +++ b/mcp/src/agents_remember/mcp/provider_degradation_settings.py @@ -0,0 +1,157 @@ +"""Settings for the provider degradation detector.""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any + +from agents_remember.errors import AgentsRememberError + +KNOWN_PROVIDER_DEGRADATION_FIELDS = frozenset( + { + "enabled", + "failSafeEnabled", + "memoryDegradedRatio", + "memoryCriticalRatio", + "degradedSamples", + "criticalSamples", + "healthySamples", + "watcherLagDegradedCommits", + "watcherLagCriticalCommits", + "watcherLagDegradedMinutes", + "watcherLagCriticalMinutes", + "probeDegradedMs", + "probeCriticalMs", + "setupFailureDegradedStreak", + "setupFailureCriticalStreak", + "recentSampleLimit", + } +) + + +class ProviderDegradationSettingsError(AgentsRememberError): + """Raised when provider degradation settings are malformed.""" + + +@dataclass(frozen=True) +class ProviderDegradationSettings: + """Provider-only degradation detector thresholds.""" + + enabled: bool = True + fail_safe_enabled: bool = True + memory_degraded_ratio: float = 0.80 + memory_critical_ratio: float = 0.92 + degraded_samples: int = 3 + critical_samples: int = 2 + healthy_samples: int = 3 + watcher_lag_degraded_commits: int = 5 + watcher_lag_critical_commits: int = 20 + watcher_lag_degraded_minutes: int = 10 + watcher_lag_critical_minutes: int = 30 + probe_degraded_ms: int = 2_000 + probe_critical_ms: int = 10_000 + setup_failure_degraded_streak: int = 2 + setup_failure_critical_streak: int = 3 + recent_sample_limit: int = 120 + + +def parse_provider_degradation_settings(raw: object) -> ProviderDegradationSettings: + """Parse the optional ``providerDegradation`` authority block.""" + + if raw is None: + return ProviderDegradationSettings() + if not isinstance(raw, dict): + raise ProviderDegradationSettingsError("providerDegradation settings must be an object") + unknown = sorted(set(raw) - KNOWN_PROVIDER_DEGRADATION_FIELDS) + if unknown: + allowed = ", ".join(sorted(KNOWN_PROVIDER_DEGRADATION_FIELDS)) + unknown_text = ", ".join(unknown) + raise ProviderDegradationSettingsError( + f"unsupported providerDegradation setting(s): {unknown_text}; allowed: {allowed}" + ) + defaults = ProviderDegradationSettings() + return ProviderDegradationSettings( + enabled=_bool_setting(raw, "enabled", defaults.enabled), + fail_safe_enabled=_bool_setting( + raw, + "failSafeEnabled", + defaults.fail_safe_enabled, + ), + memory_degraded_ratio=_ratio_setting( + raw, + "memoryDegradedRatio", + defaults.memory_degraded_ratio, + ), + memory_critical_ratio=_ratio_setting( + raw, + "memoryCriticalRatio", + defaults.memory_critical_ratio, + ), + degraded_samples=_positive_setting(raw, "degradedSamples", defaults.degraded_samples), + critical_samples=_positive_setting(raw, "criticalSamples", defaults.critical_samples), + healthy_samples=_positive_setting(raw, "healthySamples", defaults.healthy_samples), + watcher_lag_degraded_commits=_positive_setting( + raw, + "watcherLagDegradedCommits", + defaults.watcher_lag_degraded_commits, + ), + watcher_lag_critical_commits=_positive_setting( + raw, + "watcherLagCriticalCommits", + defaults.watcher_lag_critical_commits, + ), + watcher_lag_degraded_minutes=_positive_setting( + raw, + "watcherLagDegradedMinutes", + defaults.watcher_lag_degraded_minutes, + ), + watcher_lag_critical_minutes=_positive_setting( + raw, + "watcherLagCriticalMinutes", + defaults.watcher_lag_critical_minutes, + ), + probe_degraded_ms=_positive_setting(raw, "probeDegradedMs", defaults.probe_degraded_ms), + probe_critical_ms=_positive_setting(raw, "probeCriticalMs", defaults.probe_critical_ms), + setup_failure_degraded_streak=_positive_setting( + raw, + "setupFailureDegradedStreak", + defaults.setup_failure_degraded_streak, + ), + setup_failure_critical_streak=_positive_setting( + raw, + "setupFailureCriticalStreak", + defaults.setup_failure_critical_streak, + ), + recent_sample_limit=_positive_setting(raw, "recentSampleLimit", defaults.recent_sample_limit), + ) + + +def _bool_setting(raw: dict[str, Any], key: str, default: bool) -> bool: + if key not in raw: + return default + value = raw[key] + if not isinstance(value, bool): + raise ProviderDegradationSettingsError(f"providerDegradation.{key} must be a boolean") + return value + + +def _positive_setting(raw: dict[str, Any], key: str, default: int) -> int: + if key not in raw: + return default + value = raw[key] + if isinstance(value, bool) or not isinstance(value, int) or value < 1: + raise ProviderDegradationSettingsError( + f"providerDegradation.{key} must be a positive integer" + ) + return value + + +def _ratio_setting(raw: dict[str, Any], key: str, default: float) -> float: + if key not in raw: + return default + value = raw[key] + if isinstance(value, bool) or not isinstance(value, (int, float)) or not 0 < value <= 1: + raise ProviderDegradationSettingsError( + f"providerDegradation.{key} must be a number in (0, 1]" + ) + return float(value) diff --git a/mcp/src/agents_remember/mcp/server.py b/mcp/src/agents_remember/mcp/server.py index 8aa0af84..135ea075 100644 --- a/mcp/src/agents_remember/mcp/server.py +++ b/mcp/src/agents_remember/mcp/server.py @@ -55,6 +55,8 @@ route_index_refresh_payload, runtime_install_payload, server_info_payload, + session_rename_payload, + session_retire_payload, skills_install_payload, spawn_agent_session_payload, switch_lifecycle_payload, @@ -134,7 +136,11 @@ def read_ar_files( return read_ar_files_payload(config, repo_id, files, refresh=refresh) @server.tool() - def attach_terminal_session_to_leaf(session_id: str, leaf_key: str) -> dict[str, Any]: + def attach_terminal_session_to_leaf( + session_id: str, + leaf_key: str, + role: str | None = None, + ) -> dict[str, Any]: """Move an existing hosted terminal/chat session to a durable task leaf. Reuses the dashboard terminal catalog's server-authoritative `(leaf, role)` uniqueness @@ -145,12 +151,14 @@ def attach_terminal_session_to_leaf(session_id: str, leaf_key: str) -> dict[str, config, session_id=session_id, leaf_key=leaf_key, + role=role, ) @server.tool() def spawn_agent_session( harness: str | None = None, leaf_key: str | None = None, + replacement_for_leaf: str | None = None, context: str | None = None, submit: bool = False, label: str | None = None, @@ -170,37 +178,36 @@ def spawn_agent_session( Composes the EXISTING session primitives so an orchestrator can spawn a manager and a manager a worker without dashboard clicks: create a hosted session via the serving opener, attach it to `leaf_key` (server-arbitrated uniqueness — a taken leaf returns status 'leaf-taken', never - overridden), seed the role knobs (`model`/`effort`/`env` injected as spawn env — the terminal - host's `tmux new-session -e KEY=VALUE` seam — AND mapped onto the harness argv per-harness - via the registry: claude gets `--model`/`--effort`; a mapping-less harness stays env-only), - and deliver `context` as an echo-confirmed bracketed paste. `effort` is validated against the - resolved harness's known vocabulary BEFORE spawning: an unknown value returns status - 'effort-invalid' naming the harness and its valid sets (the CLI would warn-and-silently- - degrade); a session-level value (claude 'ultracode') is delivered as a post-launch session - command instead of the flag. The free-form escape hatch is never validated, only recorded in - spawn provenance: `launch_args` (appended to the harness argv verbatim), `session_commands` - (each line pasted + submitted into the fresh session BEFORE the brief), `prompt_keywords` - (prepended as the first line of the brief paste — session modes the model interprets). + overridden), resolve the role knobs from developer-owned agentic settings, seed resolved + `model`/`effort` into spawn env, map them onto the harness argv per-harness via the registry, + and deliver `context` as an id-bearing, harness-log-confirmed paste. Ordinary callers declare + the seat (`env.AR_SPAWN_ROLE`) and dispatch `level`; they do not choose harness/model/effort or direct + launch/session spend controls. Legacy non-null `harness`, `model`, `effort`, `launch_args`, + `prompt_keywords`, `session_commands`, `env.AR_SPAWN_MODEL`, `env.AR_SPAWN_EFFORT`, or + harness-native spend/endpoint env keys such as `ANTHROPIC_MODEL` and `OPENAI_BASE_URL` + return status 'spend-override-unsupported' before spawning, with guidance to configure + `orchestration.roles`, `orchestration.rolesPerLevel`, `orchestration.spawn`, or + `orchestration.harnesses` instead. + An unbound replacement declares its actual work with `replacement_for_leaf`; that provenance + is the leaf-chain discriminator while `leaf_key` remains free for the occupied role seat. `level` declares the dispatch level (leaf|master|portfolio, default leaf — a manager dispatching leaf seats passes leaf, the seam reviewer master, portfolio seats portfolio): - unset knobs resolve from the agentic settings as `orchestration.rolesPerLevel[level]` - deep-merged over the flat `orchestration.roles` default, keyed by the AR_SPAWN_ROLE riding - `env`; the resolved level + source land in spawn provenance. - `submit=true` presses Enter so a worker auto-starts; leave it false for a - draft. `harness` is optional: explicit values are validated against the detection set; omitted, - it resolves per-use from the agentic settings (role knobs, else repo-local - `/system/settings.json` over the global coordination-root file, - `orchestration.spawn.harness`; the repo comes from the - qualified leaf key), else the first detected registry harness. Each spawned session - is its own harness process (the ambient-lifecycle singleton is untouched). Spawned-by - provenance (`spawned_by_session` + the active/`spawned_by_lifecycle` lifecycle) is recorded on - the catalog row so the dashboard can render the orchestration tree. Status 'spawned' on - success; 'harness-unknown'/'harness-not-detected'/'effort-invalid'/'model-invalid'/ - 'level-invalid'/'bad-kind' are pre-spawn refusals.""" + knobs resolve from the agentic settings as `orchestration.rolesPerLevel[level]` deep-merged + over the flat `orchestration.roles` default, keyed by the AR_SPAWN_ROLE riding `env`; the + resolved level + source land in spawn provenance. `submit=true` presses Enter so a worker + auto-starts; leave it false for a draft. If role settings do not choose a harness, the + dispatch falls through to repo-local/global `orchestration.spawn.harness`, then the first + detected registry harness. Each spawned session is its own harness process (the + ambient-lifecycle singleton is untouched). Spawned-by provenance (`spawned_by_session` + the + active/`spawned_by_lifecycle` lifecycle) is recorded on the catalog row so the dashboard can + render the orchestration tree. Status 'spawned' on success; 'spend-override-unsupported', + 'harness-unknown'/'harness-not-detected'/'effort-invalid'/'model-invalid'/'level-invalid'/ + 'bad-kind' are pre-spawn refusals.""" return spawn_agent_session_payload( config, harness=harness, leaf_key=leaf_key, + replacement_for_leaf=replacement_for_leaf, context=context, submit=submit, label=label, @@ -216,6 +223,41 @@ def spawn_agent_session( kind=kind, ) + @server.tool() + def session_retire( + actor_session_id: str, + session_id: str, + reason: str = "manual retire", + ) -> dict[str, Any]: + """Retire a tracked chat/terminal session (260707-HFX-L8, issue #12): kill its tmux session, + mark the catalog row terminated with retirement provenance (who/why/when/edge), and remove + it from the active rail. Transcripts are never deleted. + + `actor_session_id` is the RETIRING seat's own catalog session id (self-declared -- there is + no ambient "who am I" resolution, mirroring `spawn_agent_session`'s `spawned_by_session`). + Authority is enforced server-side and refusals are loud and policy-naming: a seat never + retires itself (`retire-refused`); a manager may retire only worker/reviewer seats of its + OWN master; the orchestrator may retire any seat, including a completed manager. Status + 'retired' on success, 'already-retired' when the target was already terminated (idempotent), + 'unknown-session'/'unknown-actor' when a session id has no catalog row, 'retire-refused' for + every authority-policy refusal.""" + return session_retire_payload( + config, + actor_session_id=actor_session_id, + session_id=session_id, + reason=reason, + ) + + @server.tool() + def session_rename(session_id: str, label: str) -> dict[str, Any]: + """Update a chat/terminal session's display label post-spawn (260707-HFX-L8, issue #4). + + Identity text only: the seat's spawned role never changes (L6 role-seat immutability). The + FIRST rename freezes the original spawn-time label into spawn provenance for audit (later + renames leave it alone). Status 'renamed' on success, 'unknown-session' when the session has + no catalog row or is already retired.""" + return session_rename_payload(config, session_id=session_id, label=label) + @server.tool() def runtime_install( dry_run: bool = False, diff --git a/mcp/src/agents_remember/mcp/tools/__init__.py b/mcp/src/agents_remember/mcp/tools/__init__.py index 1d9e58f5..66cddbe1 100644 --- a/mcp/src/agents_remember/mcp/tools/__init__.py +++ b/mcp/src/agents_remember/mcp/tools/__init__.py @@ -67,7 +67,12 @@ ) from .read_files import read_ar_files_payload from .task_doc import task_doc_payload, task_reopen_payload -from .terminal import attach_terminal_session_to_leaf_payload, spawn_agent_session_payload +from .terminal import ( + attach_terminal_session_to_leaf_payload, + session_rename_payload, + session_retire_payload, + spawn_agent_session_payload, +) from .worktree import ( worktree_abandon_payload, worktree_attach_payload, @@ -133,6 +138,8 @@ "route_index_refresh_payload", "runtime_install_payload", "server_info_payload", + "session_rename_payload", + "session_retire_payload", "skills_install_payload", "spawn_agent_session_payload", "switch_lifecycle_payload", diff --git a/mcp/src/agents_remember/mcp/tools/base.py b/mcp/src/agents_remember/mcp/tools/base.py index a01da328..260a72be 100644 --- a/mcp/src/agents_remember/mcp/tools/base.py +++ b/mcp/src/agents_remember/mcp/tools/base.py @@ -2,11 +2,14 @@ from __future__ import annotations +from datetime import UTC, datetime from typing import Any +from agents_remember.kernel.agentic_settings import DEFAULT_SUPERVISOR_STALE_CUTOFF_SECONDS from agents_remember.models.tokens import finalize_payload_tokens from agents_remember.models.tool_registry import TOOL_RESPONSE_MODELS from agents_remember.observer.ambient import ambient +from agents_remember.serving.supervisor_heartbeat import supervisor_staleness_banner from .next_step import next_step_for @@ -18,6 +21,8 @@ "read_ar_files", "attach_terminal_session_to_leaf", "spawn_agent_session", + "session_retire", + "session_rename", "runtime_install", "resolve_context", "drift_check", @@ -96,4 +101,16 @@ def _tool_payload(tool_name: str, payload: dict[str, Any]) -> dict[str, Any]: next_step = next_step_for(amb, tool_name) if next_step is not None: finalized["nextStep"] = next_step + # 260707-HFX2-L2 R5: "the watcher must be code AND watched" (#15) -- a stale supervisor + # surfaces a fail-loud banner at ANY seat's next AR call, opportunistically, off the + # heartbeat row it writes on every sweep. Exception-safe (an unreadable heartbeat file + # degrades to "never ticked", never blocks the tool response). + try: + banner = supervisor_staleness_banner( + amb.root, now=datetime.now(UTC), stale_cutoff_seconds=DEFAULT_SUPERVISOR_STALE_CUTOFF_SECONDS + ) + except Exception: + banner = None + if banner is not None: + finalized["supervisorBanner"] = banner return finalized diff --git a/mcp/src/agents_remember/mcp/tools/gates.py b/mcp/src/agents_remember/mcp/tools/gates.py index b1748655..249c05e4 100644 --- a/mcp/src/agents_remember/mcp/tools/gates.py +++ b/mcp/src/agents_remember/mcp/tools/gates.py @@ -24,8 +24,10 @@ import time from collections.abc import Callable +from datetime import UTC, datetime from typing import TYPE_CHECKING, Any, cast +from agents_remember.controlplane.expectation_rows import ExpectationRowStore, write_expectation_row from agents_remember.controlplane.gate_policy import ( DEFAULT_GATE_POLICY, SEAM_GATE_KINDS, @@ -49,6 +51,10 @@ expire_gate, ) from agents_remember.controlplane.store import GateStore +from agents_remember.kernel.agentic_settings import ( + DEFAULT_EXPECTATION_SLA_SECONDS, + load_agentic_settings, +) from agents_remember.observer import observer_root from agents_remember.observer.ambient import ambient, build_ask, require_ambient from agents_remember.observer.events import now_iso @@ -69,6 +75,33 @@ def _inbox_store(config: McpRuntimeConfig) -> OperatorInboxStore: return OperatorInboxStore(observer_root(config)) +def _expectation_store(config: McpRuntimeConfig) -> ExpectationRowStore: + return ExpectationRowStore(observer_root(config)) + + +def _expectation_sla_seconds(config: McpRuntimeConfig, kind: str) -> float: + if getattr(config, "coordination_root", None) is None: + return DEFAULT_EXPECTATION_SLA_SECONDS[kind] + return load_agentic_settings(config.coordination_root).expectations.sla_for(kind) + + +def _write_verdict_by_row(config: McpRuntimeConfig, gate: GateRecord) -> None: + """R2: a gate open atomically writes its ``verdict-by`` expectation row (same call, never a + forgettable follow-up).""" + if getattr(config, "coordination_root", None) is None: + return + write_expectation_row( + _expectation_store(config), + row_id=new_ulid(), + now=datetime.now(UTC), + kind="verdict-by", + sla_seconds=_expectation_sla_seconds(config, "verdict-by"), + source_id=gate.id, + subject_lifecycle_id=gate.lifecycleId, + note=f"verdict-by: {gate.kind} gate {gate.id}", + ) + + def _resolve_gate_lifecycle_id(lifecycle_id: str | None) -> str: if lifecycle_id is not None: if lifecycle_id.strip(): @@ -157,6 +190,7 @@ def gate_create_payload( evidence_refs=evidence_refs, ) store.append(gate) + _write_verdict_by_row(config, gate) return _tool_payload( "gate_create", { @@ -266,6 +300,7 @@ def lifecycle_gate_payload( evidence_refs=evidence_refs, ) store.append(gate) + _write_verdict_by_row(config, gate) if not wait: return _tool_payload( "lifecycle_gate", @@ -397,6 +432,13 @@ def gate_decide_payload( if decision == "cancel": store.delete(updated.id, updated.lifecycleId) _inbox_store(config).delete_by_gate(updated.id) + if getattr(config, "coordination_root", None) is not None: + # R2 fulfillment: any terminal decision (approve/reject/cancel) meets the gate's + # verdict-by expectation row, stopping the L2 sweep from flagging it overdue. + expectations = _expectation_store(config) + row = expectations.find_by_source(updated.id, kind="verdict-by") + if row is not None: + expectations.mark_met(row.id, now=now_iso()) return _tool_payload( "gate_decide", { diff --git a/mcp/src/agents_remember/mcp/tools/leaf_ref.py b/mcp/src/agents_remember/mcp/tools/leaf_ref.py new file mode 100644 index 00000000..3830a92c --- /dev/null +++ b/mcp/src/agents_remember/mcp/tools/leaf_ref.py @@ -0,0 +1,29 @@ +"""MCP payload helpers for leaf-ref validation refusals.""" + +from __future__ import annotations + +from typing import Any + +from agents_remember.worktrees.leaf_refs import LeafRefResolutionError + +from .base import _tool_payload + + +def leaf_ref_refusal_payload( + operation: str, + leaf_key: str, + error: LeafRefResolutionError, + *, + kind: str | None = None, +) -> dict[str, Any]: + payload: dict[str, Any] = { + "ok": False, + "operation": operation, + "status": error.status, + "session": "", + "leafKey": leaf_key, + "detail": str(error), + } + if operation == "spawn_agent_session": + payload["kind"] = kind if kind in ("harness", "terminal") else None + return _tool_payload(operation, payload) diff --git a/mcp/src/agents_remember/mcp/tools/next_step.py b/mcp/src/agents_remember/mcp/tools/next_step.py index 7e082e2f..68de021f 100644 --- a/mcp/src/agents_remember/mcp/tools/next_step.py +++ b/mcp/src/agents_remember/mcp/tools/next_step.py @@ -55,7 +55,8 @@ # leaf-26 Lifecycle Flow tab RUNDOWN, with the developer-chosen plan-approval # hand-off appended (S5: ride the first gate tool). FRONT_HALF_RUNDOWN: list[str] = [ - "reframe — restate the request as agreed work, then present it for the developer's agreement (the orchestrator lifecycle: l-01-agent-lifecycles roles/orchestrator.md).", + "reframe — restate the request as agreed work, then present it for the developer's agreement " + "(the architect lifecycle: l-01-agent-lifecycles roles/architect.md).", "research — read_ar_files · grepai · cgc (they fire unpredictably, so this half is prose-guided, " "not per-tool).", "route the event (the lens tunes it): no task doc → design one ; approved doc + code change " diff --git a/mcp/src/agents_remember/mcp/tools/operator_inbox.py b/mcp/src/agents_remember/mcp/tools/operator_inbox.py index 9a07de56..321af294 100644 --- a/mcp/src/agents_remember/mcp/tools/operator_inbox.py +++ b/mcp/src/agents_remember/mcp/tools/operator_inbox.py @@ -5,6 +5,7 @@ from datetime import UTC, datetime from typing import TYPE_CHECKING, Any +from agents_remember.controlplane.expectation_rows import ExpectationRowStore, write_expectation_row from agents_remember.controlplane.operator_inbox_records import ( AgentRole, InboxMessageKind, @@ -13,6 +14,15 @@ create_operator_inbox_entry, ) from agents_remember.controlplane.operator_inbox_store import OperatorInboxStore +from agents_remember.controlplane.signal_routing import ( + RoutedOwner, + derive_signal_owner, + signal_leaf_key, +) +from agents_remember.kernel.agentic_settings import ( + DEFAULT_EXPECTATION_SLA_SECONDS, + load_agentic_settings, +) from agents_remember.observer import observer_root from agents_remember.observer.events import now_iso from agents_remember.observer.ulid import new_ulid @@ -31,10 +41,61 @@ def _store(config: McpRuntimeConfig) -> OperatorInboxStore: return OperatorInboxStore(observer_root(config)) +def _expectation_store(config: McpRuntimeConfig) -> ExpectationRowStore: + return ExpectationRowStore(observer_root(config)) + + +def _expectation_sla_seconds(config: McpRuntimeConfig | None, kind: str) -> float: + """The configured SLA for ``kind`` (``orchestration.expectations.defaults``), or the + documented default when ``config`` carries none (or is absent, e.g. an injected-store test).""" + if config is None: + return DEFAULT_EXPECTATION_SLA_SECONDS[kind] + return load_agentic_settings(config.coordination_root).expectations.sla_for(kind) + + +def _redelivery_floor_seconds(config: McpRuntimeConfig | None) -> float | None: + if config is None: + return None + return load_agentic_settings(config.coordination_root).supervisor.redeliver_rate_limit_seconds + + def _entry_payload(entry: OperatorInboxEntry) -> dict[str, Any]: return entry.model_dump(mode="json", by_alias=True, exclude_none=True) +def _signal_route( + catalog: TerminalCatalog | None, + *, + sender_agent_id: str | None, + message_kind: InboxMessageKind, +) -> tuple[RoutedOwner, str | None]: + if catalog is None: + return RoutedOwner(), None + return ( + derive_signal_owner( + catalog, + sender_agent_id=sender_agent_id, + message_kind=message_kind, + ), + signal_leaf_key(catalog, sender_agent_id=sender_agent_id), + ) + + +def _post_address( + owner: RoutedOwner, + *, + message_kind: InboxMessageKind, + lifecycle_id: str | None, + agent_id: str | None, + recipient_role: AgentRole | None, +) -> tuple[str | None, str | None, AgentRole | None]: + """Completion/artifact signals address the current owner; ordinary peer messages stay explicit.""" + has_owner = owner.agent_id is not None or owner.lifecycle_id is not None or owner.role is not None + if message_kind not in ("turn-report", "master-handover") or not has_owner: + return lifecycle_id, agent_id, recipient_role + return owner.lifecycle_id, owner.agent_id, owner.role + + def operator_inbox_post_payload( config: McpRuntimeConfig, *, @@ -55,11 +116,28 @@ def operator_inbox_post_payload( terminal_host: TerminalHost | None = None, terminal_paster: TerminalPaster | None = None, ) -> dict[str, Any]: + catalog = terminal_catalog or ( + TerminalCatalog(terminal_catalog_path(config.coordination_root)) if config is not None else None + ) + owner, leaf_key = _signal_route( + catalog, sender_agent_id=sender_agent_id, message_kind=message_kind + ) + # Completion/artifact messages are hierarchy signals, not arbitrary peer messages. Address + # them to the current routed owner in the same post that attempts hosted delivery; merely + # recording owner metadata leaves the stale caller-supplied mailbox in control and reproduces + # the reviewer-finished/manager-never-woken halt. + target_lifecycle_id, target_agent_id, target_role = _post_address( + owner, + message_kind=message_kind, + lifecycle_id=lifecycle_id, + agent_id=agent_id, + recipient_role=recipient_role, + ) entry = create_operator_inbox_entry( entry_id=new_ulid(), now=now_iso(), - lifecycle_id=lifecycle_id, - agent_id=agent_id, + lifecycle_id=target_lifecycle_id, + agent_id=target_agent_id, gate_id=gate_id, ask=ask, response=response, @@ -67,21 +145,41 @@ def operator_inbox_post_payload( created_via=created_via, sender_agent_id=sender_agent_id, sender_role=sender_role, - recipient_role=recipient_role, + recipient_role=target_role, message_kind=message_kind, artifact_path=artifact_path, + leaf_key=leaf_key, + subject_agent_id=sender_agent_id, + owner_role=owner.role, + owner_agent_id=owner.agent_id, + owner_lifecycle_id=owner.lifecycle_id, ) store = _store(config) store.append(entry) store.compact(now=datetime.now(UTC)) + # R2: the signal-post -> ack-by expectation row, written atomically in the SAME call as the + # post (never a follow-up step a caller could forget). consume=ack fulfills it. + if config is not None: + write_expectation_row( + _expectation_store(config), + row_id=new_ulid(), + now=datetime.now(UTC), + kind="ack-by", + sla_seconds=_expectation_sla_seconds(config, "ack-by"), + source_id=entry.id, + subject_agent_id=target_agent_id, + subject_lifecycle_id=target_lifecycle_id, + note=f"ack-by: {message_kind} to {target_role or target_agent_id or target_lifecycle_id}", + ) if deliver_to_hosted: entry = deliver_inbox_entry( store=store, - catalog=terminal_catalog or TerminalCatalog(terminal_catalog_path(config.coordination_root)), + catalog=catalog or TerminalCatalog(terminal_catalog_path(config.coordination_root)), host=terminal_host or TerminalHost(), paster=terminal_paster or TerminalPaster(), entry=entry, submit=True, + redelivery_floor_seconds=_redelivery_floor_seconds(config), ) return _tool_payload( "operator_inbox_post", @@ -95,6 +193,9 @@ def operator_inbox_post_payload( "senderAgentId": entry.senderAgentId, "senderRole": entry.senderRole, "recipientRole": entry.recipientRole, + "ownerRole": entry.ownerRole, + "ownerAgentId": entry.ownerAgentId, + "ownerLifecycleId": entry.ownerLifecycleId, "gateId": entry.gateId, "messageKind": entry.messageKind, "artifactPath": entry.artifactPath, @@ -146,7 +247,13 @@ def operator_inbox_consume_payload( consumed_by=consumed_by, consumed_via=consumed_via, ) - store.delete(entry.id) + if consumed_now and config is not None: + # R1: consume=ack is the ONLY terminal delivery outcome; it also fulfills the signal's + # ack-by expectation row (R2), so redelivery/backoff and the deadline sweep both stop. + expectations = _expectation_store(config) + row = expectations.find_by_source(entry.id, kind="ack-by") + if row is not None: + expectations.mark_met(row.id, now=now_iso()) return _tool_payload( "operator_inbox_consume", { diff --git a/mcp/src/agents_remember/mcp/tools/terminal.py b/mcp/src/agents_remember/mcp/tools/terminal.py index 217a43f2..0a5740eb 100644 --- a/mcp/src/agents_remember/mcp/tools/terminal.py +++ b/mcp/src/agents_remember/mcp/tools/terminal.py @@ -4,16 +4,21 @@ import os from dataclasses import dataclass +from datetime import UTC, datetime from pathlib import Path from typing import TYPE_CHECKING, Any from uuid import uuid4 +from agents_remember.controlplane.expectation_rows import ExpectationRowStore, write_expectation_row from agents_remember.kernel.agentic_settings import ( AgenticSettings, RoleKnobs, load_agentic_settings, ) +from agents_remember.observer import observer_root from agents_remember.observer.ambient import ambient +from agents_remember.observer.events import now_iso +from agents_remember.serving.harness_logs import HarnessSessionLog from agents_remember.serving.harnesses import ( Harness, Which, @@ -24,17 +29,35 @@ is_detected, unknown_harness_detail, ) +from agents_remember.serving.injector import ( + DeliveryRow, + deliver, + verify_or_reissue_command, +) +from agents_remember.serving.leaf_ref_validation import resolve_catalog_leaf_key +from agents_remember.serving.retire import retire_entry +from agents_remember.serving.retire_policy import ( + RetirePolicyError, + SeatRef, + check_retire_authority, +) +from agents_remember.serving.seat_events import log_rename_event, log_retire_event from agents_remember.serving.terminal import TerminalHost from agents_remember.serving.terminal_catalog import ( TerminalCatalog, TerminalCatalogEntry, terminal_catalog_path, ) -from agents_remember.serving.terminal_leaf_assignment import assign_terminal_session_to_leaf +from agents_remember.serving.terminal_leaf_assignment import ( + LeafAssignmentHost, + assign_terminal_session_to_leaf, +) from agents_remember.serving.terminal_opener import open_terminal_session from agents_remember.serving.terminal_paste import TerminalPaster +from agents_remember.worktrees.leaf_refs import LeafRefResolutionError from .base import _tool_payload +from .leaf_ref import leaf_ref_refusal_payload if TYPE_CHECKING: from agents_remember.mcp.config import McpRuntimeConfig @@ -46,6 +69,42 @@ # congruent. The dispatcher knows its level: a manager dispatching leaf seats = leaf, the seam # reviewer = master, portfolio/end-to-end seats = portfolio. Omitted = leaf. _SPAWN_LEVELS = ("leaf", "master", "portfolio") +_REMOVED_CALLER_SPEND_FIELDS = ( + "harness", + "model", + "effort", + "launch_args", + "prompt_keywords", + "session_commands", +) +# The retained caller env field reaches the spawned harness process through tmux -e. Block +# harness-native model, effort, endpoint, and credential env vars so env cannot bypass the +# developer-owned settings surface. +_HARNESS_NATIVE_SPEND_ENV_KEYS = ( + "ANTHROPIC_MODEL", + "ANTHROPIC_SMALL_FAST_MODEL", + "ANTHROPIC_DEFAULT_SONNET_MODEL", + "ANTHROPIC_DEFAULT_OPUS_MODEL", + "ANTHROPIC_DEFAULT_HAIKU_MODEL", + "ANTHROPIC_DEFAULT_FABLE_MODEL", + "MAX_THINKING_TOKENS", + "DISABLE_PROMPT_CACHING", + "ANTHROPIC_BASE_URL", + "ANTHROPIC_BEDROCK_BASE_URL", + "ANTHROPIC_VERTEX_BASE_URL", + "ANTHROPIC_API_KEY", + "ANTHROPIC_AUTH_TOKEN", + "AWS_BEARER_TOKEN_BEDROCK", + "OPENAI_MODEL", + "OPENAI_DEFAULT_MODEL", + "OPENAI_BASE_URL", + "OPENAI_API_BASE", + "OPENAI_API_KEY", + "OPENAI_ORGANIZATION", + "OPENAI_ORG_ID", + "OPENAI_PROJECT", +) +_SPEND_ENV_KEYS = ("AR_SPAWN_MODEL", "AR_SPAWN_EFFORT", *_HARNESS_NATIVE_SPEND_ENV_KEYS) def attach_terminal_session_to_leaf_payload( @@ -53,14 +112,23 @@ def attach_terminal_session_to_leaf_payload( *, session_id: str, leaf_key: str, + role: str | None = None, + host: LeafAssignmentHost | None = None, ) -> dict[str, Any]: """Move an existing hosted terminal/chat session to a durable leaf key.""" + try: + leaf_key = resolve_catalog_leaf_key(config, leaf_key) + except LeafRefResolutionError as exc: + return leaf_ref_refusal_payload("attach_terminal_session_to_leaf", leaf_key, exc) catalog = TerminalCatalog(terminal_catalog_path(config.coordination_root)) + assignment_host = host if host is not None else TerminalHost() result = assign_terminal_session_to_leaf( catalog, + assignment_host, session_id=session_id, leaf_key=leaf_key, + role=role, ) return _tool_payload( "attach_terminal_session_to_leaf", @@ -73,6 +141,8 @@ def attach_terminal_session_to_leaf_payload( "previousLeafKey": result.previous_leaf_key, "ownerSession": result.owner_session_id, "role": result.role, + "seatRole": result.seat_role, + "previousSeatRole": result.previous_seat_role, }, ) @@ -85,15 +155,14 @@ def _spawn_env( """Fold the role knobs into the spawn env the terminal host seeds at ``tmux new-session``. Model/effort ride as namespaced env vars (``AR_SPAWN_MODEL`` / ``AR_SPAWN_EFFORT``) alongside any - caller-supplied ``env`` (explicit keys win). This is the injection SEAM the leaf owns; mapping these - to a given harness's concrete CLI flags / config is the role-knob resolution layer's job (job-file - defaults + settings overrides), deferred to that layer -- this tool just delivers the passthrough. + caller-supplied ``env``. Caller-supplied spend env keys are rejected before this helper runs; settings + are the only authority for model/effort selection. """ resolved = dict(env or {}) if model: - resolved.setdefault("AR_SPAWN_MODEL", model) + resolved["AR_SPAWN_MODEL"] = model if effort: - resolved.setdefault("AR_SPAWN_EFFORT", effort) + resolved["AR_SPAWN_EFFORT"] = effort return resolved @@ -127,14 +196,14 @@ def _resolve_spawn_harness( ) -> tuple[Harness | None, dict[str, Any] | None]: """Resolve the harness for a spawn (260703-L13 seam; effective registry 260703-L16). - Precedence: explicit argument > repo-local settings > global settings > - detection-gated default (the first EFFECTIVE-registry harness detected on - PATH). Every id resolves against ``settings.harnesses`` -- the builtin - registry merged with the ``orchestration.harnesses`` family, so users can - teach the system a new TUI or pre-customize a builtin's launch. An id known - nowhere refuses loudly pointing at the manual (never a crash); ``settings`` - comes from the per-use loader (a malformed file raises -- never a silent - fallback). Returns ``(harness, refusal_payload)`` with exactly one side set. + Precedence: role/level settings > spawn settings > detection-gated default + (the first EFFECTIVE-registry harness detected on PATH). Every id resolves + against ``settings.harnesses`` -- the builtin registry merged with the + ``orchestration.harnesses`` family, so users can teach the system a new TUI + or pre-customize a builtin's launch. An id known nowhere refuses loudly + pointing at the manual (never a crash); ``settings`` comes from the per-use + loader (a malformed file raises -- never a silent fallback). Returns + ``(harness, refusal_payload)`` with exactly one side set. """ registry = settings.harnesses if harness is not None: @@ -182,7 +251,7 @@ def _resolve_spawn_harness( "harness", detail=( "no harness given, none preferred in settings, and none detected on " - f"PATH; install one of: {ids} (or pass harness explicitly)" + f"PATH; install one of: {ids} or configure orchestration.roles / orchestration.spawn" ), ) @@ -192,9 +261,8 @@ class _HarnessDispatch: """The pre-spawn knob bundle for one harness-kind dispatch (260703-L16). Everything the settings rungs resolved for this seat: the harness id (effective-registry - validated), the effective registry itself, the folded model/effort (explicit args over role - knobs), the free-form escape hatch, the RESOLVED session-command list (effort vehicle first), - and the level provenance. + validated), the effective registry itself, the resolved model/effort, the settings-owned free-form + escape hatch, the RESOLVED session-command list (effort vehicle first), and the level provenance. """ harness_id: str @@ -211,26 +279,20 @@ class _HarnessDispatch: def _resolve_harness_dispatch( config: McpRuntimeConfig, *, - harness: str | None, leaf_key: str | None, level: str | None, - model: str | None, - effort: str | None, env: dict[str, str] | None, - launch_args: list[str] | None, - prompt_keywords: list[str] | None, - session_commands: list[str] | None, which: Which | None, ) -> tuple[_HarnessDispatch | None, dict[str, Any] | None]: """Resolve + validate every knob BEFORE anything spawns (260703-L16). - Realizes the dispatch chain explicit args > repo-local level override > global level override > + Realizes the settings-only dispatch chain repo-local level override > global level override > repo-local role default > global role default > spawn preference > detection-gated default: the - settings rungs come from ``resolved_role_knobs(AR_SPAWN_ROLE, level)`` (per-use read; the - repo-local layer selected by the qualified leaf key), the harness resolves against the EFFECTIVE - registry, and model/effort are refused per-harness (``model-invalid``/``effort-invalid``) before - any tmux exists. A session-vocabulary effort (claude ``ultracode``) contributes the FIRST - session command. Returns ``(dispatch, refusal)`` with exactly one side set. + settings rungs come from ``resolved_role_knobs(AR_SPAWN_ROLE, level)`` (per-use read; the repo-local + layer selected by the qualified leaf key), the harness resolves against the EFFECTIVE registry, and + model/effort are refused per-harness (``model-invalid``/``effort-invalid``) before any tmux exists. + A session-vocabulary effort (claude ``ultracode``) contributes the FIRST session command. Returns + ``(dispatch, refusal)`` with exactly one side set. """ spawn_level = level or "leaf" spawn_level_source = "explicit" if level is not None else "default" @@ -238,7 +300,7 @@ def _resolve_harness_dispatch( valid = ", ".join(_SPAWN_LEVELS) return None, _spawn_refusal( "level-invalid", - harness, + None, "harness", detail=f"unknown dispatch level {spawn_level!r}; valid levels: [{valid}]", ) @@ -246,32 +308,29 @@ def _resolve_harness_dispatch( config.coordination_root, repo_root=_spawn_repo_root(config, leaf_key) ) # The settings rungs, keyed by the AR_SPAWN_ROLE riding the caller's env (no role = no settings - # rung, today's behavior); unset explicit args fold in from the level-merged role knobs. + # rung, today's behavior); role/level settings are the sole spend source for ordinary spawns. role = (env or {}).get("AR_SPAWN_ROLE") knobs = settings.resolved_role_knobs(role, spawn_level) if role else RoleKnobs() - model = model or knobs.model - effort = effort or knobs.effort - if launch_args is None and knobs.launch_args: - launch_args = list(knobs.launch_args) - if prompt_keywords is None and knobs.prompt_keywords: - prompt_keywords = list(knobs.prompt_keywords) - resolved_session_commands = ( - list(session_commands) if session_commands is not None else list(knobs.session_commands) - ) - # Harness rung order: explicit argument > role knobs (level-merged) > spawn preference > - # detection-gated default (the last two inside _resolve_spawn_harness). - found, refusal = _resolve_spawn_harness(settings, harness or knobs.harness, which) + model = knobs.model + effort = knobs.effort + launch_args = list(knobs.launch_args) if knobs.launch_args else None + prompt_keywords = list(knobs.prompt_keywords) if knobs.prompt_keywords else None + resolved_session_commands = list(knobs.session_commands) + # Harness rung order: role knobs (level-merged) > spawn preference > detection-gated default + # (the last two inside _resolve_spawn_harness). + found, refusal = _resolve_spawn_harness(settings, knobs.harness, which) if refusal is not None: return None, refusal assert found is not None # no refusal => a resolved effective-registry harness - # Validate the EFFECTIVE values (env wins over arg wins over settings, mirroring _spawn_env). - effective_model = (env or {}).get("AR_SPAWN_MODEL") or model - effective_effort = (env or {}).get("AR_SPAWN_EFFORT") or effort + # Validate the settings-resolved values. Caller-provided AR_SPAWN_MODEL/AR_SPAWN_EFFORT keys are + # rejected before this function runs, so env cannot replace the developer's settings. + effective_model = model + effective_effort = effort refusal = _knob_refusal(found, effective_model, effective_effort) if refusal is not None: return None, refusal # A session-vocabulary effort is delivered as the FIRST post-launch session command, ahead of - # any caller/settings free-form ones, then the brief. + # any settings-owned free-form ones, then the brief. resolved_session_commands = ( effort_session_commands(found, effective_effort) + resolved_session_commands ) @@ -296,7 +355,10 @@ def _knob_refusal( ) -> dict[str, Any] | None: """The ``model-invalid``/``effort-invalid`` pre-spawn refusal, or ``None`` when the knobs apply.""" checks = ( - ("model-invalid", invalid_model_detail(found, effective_model) if effective_model else None), + ( + "model-invalid", + invalid_model_detail(found, effective_model) if effective_model else None, + ), ( "effort-invalid", invalid_effort_detail(found, effective_effort) if effective_effort else None, @@ -308,6 +370,53 @@ def _knob_refusal( return None +def _caller_spend_override_refusal( + *, + harness: str | None, + model: str | None, + effort: str | None, + env: dict[str, str] | None, + launch_args: list[str] | None, + prompt_keywords: list[str] | None, + session_commands: list[str] | None, + kind: str, +) -> dict[str, Any] | None: + """Reject legacy caller-controlled spend knobs before any spawn-side effect.""" + + removed = [] + values = { + "harness": harness, + "model": model, + "effort": effort, + "launch_args": launch_args, + "prompt_keywords": prompt_keywords, + "session_commands": session_commands, + } + for field in _REMOVED_CALLER_SPEND_FIELDS: + if values[field] is not None: + removed.append(field) + for key in _SPEND_ENV_KEYS: + if env is not None and key in env: + removed.append(f"env.{key}") + if not removed: + return None + fields = ", ".join(removed) + detail = ( + "spawn_agent_session no longer accepts caller-selected spend fields " + f"({fields}) for ordinary agent-driven spawns. Configure harness/model/effort and " + "launch/session spend controls in agentic settings under orchestration.roles, " + "orchestration.rolesPerLevel, orchestration.spawn, or orchestration.harnesses; call " + "spawn_agent_session with role (env.AR_SPAWN_ROLE), level, leaf_key, context, submit, label, " + "env, and provenance only." + ) + return _spawn_refusal( + "spend-override-unsupported", + harness, + kind, + detail=detail, + ) + + def _brief_packet(context: str | None, prompt_keywords: list[str] | None) -> str | None: """The brief paste text: promptKeywords ride as its first line (delivered alone with no brief).""" if not prompt_keywords: @@ -316,35 +425,148 @@ def _brief_packet(context: str | None, prompt_keywords: list[str] | None) -> str return f"{keyword_line}\n\n{context}" if context else keyword_line +_EMPTY_PANE_CAPTURE = "(empty pane capture)" +"""Explicit stand-in when a failed delivery's pane capture is empty (a vanished/unreadable pane): +review N3 alignment with ``inbox_delivery`` -- a False outcome NEVER ships evidence-less, so the +``deliveryCapture`` field is present (with this marker) rather than silently omitted.""" + + +@dataclass(frozen=True) +class _SpawnDelivery: + """Delivery outcomes for one spawn's session layer (``None`` = that piece was not sent). + + ``failure_capture`` is the pane capture attached by the paster to the latest failed paste -- + the 260707-HFX-L3 loud-failure evidence (SF-1: a bare ``contextDelivered:true`` once masked a + codex seat that booted clean with no payload). ``None`` when every sent piece verified. + """ + + session_commands_delivered: bool | None = None + context_delivered: bool | None = None + submitted: bool | None = None + failure_capture: str | None = None + bound_entry_id: str | None = None + session_log_path: Path | None = None + + def _deliver_spawn_pastes( paster: TerminalPaster, tmux_name: str, session_commands: list[str], packet: str | None, submit: bool, -) -> tuple[bool | None, bool | None, bool | None]: - """Deliver the session layer: session commands FIRST (each its own echo-confirmed paste, always - submitted -- an unexecuted ``/effort ultracode`` would be a silent downgrade), THEN the brief. - - Returns ``(session_commands_delivered, context_delivered, submitted)`` (``None`` = not sent). + *, + entry_id: str, + harness: str | None, + cwd: Path, + created_at: str, + session_log: HarnessSessionLog | None = None, +) -> _SpawnDelivery: + """Deliver commands first, bind on the id-bearing brief, then verify every input in that log. + + 260707-HFX2-L3 (R3, ONE PATH): this is no longer a separate raw-spawn delivery loop -- every + paste here goes through ``serving.injector.deliver``, the same path ``inbox_delivery.py`` calls. + Commands remain verbatim local inputs; the brief gains the standard id envelope that binds the + spawn-cwd session log. A failed final outcome alone captures the pane for diagnostics. + + Numeric bound (L15 FIX-K/f): one Claude input has three calibrated 40.3 s log windows (initial, + Enter re-press, re-paste), at most seven 5 s transport operations, two 5 s absence captures, two + 100 ms settles, and one 5 s failure capture: <=171.1 s. Codex substitutes 29.0 s windows: + <=137.2 s. A visible prior payload takes the shorter clear-or-fail branch. Commands sent before the + brief do one unverified transport, then incur the same bound only when retroactive evidence is + missing/errored. No screen wait or switch flow remains. """ session_commands_delivered: bool | None = None context_delivered: bool | None = None submitted: bool | None = None + failure_capture: str | None = None + bound_entry_id: str | None = None + session_log_path: Path | None = None + failed = False + log = session_log or HarnessSessionLog( + harness=harness or "", + cwd=cwd, + started_at=datetime.fromisoformat(created_at), + ) + command_rows: list[DeliveryRow] = [] if session_commands: session_commands_delivered = True for command_line in session_commands: - outcome = paster.paste(tmux_name, command_line, submit=True) - session_commands_delivered = ( - session_commands_delivered and outcome.delivered and outcome.submitted + row = DeliveryRow( + kind="session-command", + entry_id=entry_id, + text=command_line, + submit=True, + envelope=False, ) + command_rows.append(row) + result = deliver( + row, + tmux_name=tmux_name, + paster=paster, + harness=harness, + session_log=log, + ) + if result.outcome in ("blocked", "failed"): + failure_capture = result.capture or failure_capture if packet: - # Workers auto-start (paste + submit); a human draft-only flow leaves submit=False so the - # draft stays editable. Echo-confirmed server-side (the frontend pasteAndConfirm mirror). - outcome = paster.paste(tmux_name, packet, submit=submit) - context_delivered = outcome.delivered - submitted = outcome.submitted if submit else None - return session_commands_delivered, context_delivered, submitted + row = DeliveryRow( + kind="brief", entry_id=entry_id, text=packet, submit=submit, envelope=True + ) + result = deliver( + row, + tmux_name=tmux_name, + paster=paster, + harness=harness, + session_log=log, + ) + context_delivered = result.outcome == "acked" + submitted = result.submitted if submit else None + bound_entry_id = result.bound_entry_id + session_log_path = result.session_log_path + if result.outcome in ("blocked", "failed") or (submit and result.outcome != "acked"): + failed = True + failure_capture = result.capture or failure_capture + if command_rows: + if log.bound_path is None: + session_commands_delivered = False + failed = True + else: + for row in command_rows: + result = verify_or_reissue_command( + row, + tmux_name=tmux_name, + paster=paster, + harness=harness, + session_log=log, + ) + if result.outcome != "acked": + session_commands_delivered = False + failed = True + failure_capture = result.capture or failure_capture + session_log_path = log.bound_path + bound_entry_id = entry_id + if failed and failure_capture is None: + failure_capture = _EMPTY_PANE_CAPTURE + return _SpawnDelivery( + session_commands_delivered=session_commands_delivered, + context_delivered=context_delivered, + submitted=submitted, + failure_capture=failure_capture, + bound_entry_id=bound_entry_id, + session_log_path=session_log_path, + ) + + +def _resolve_spawn_leaf( + config: McpRuntimeConfig, leaf_ref: str | None, *, kind: str +) -> tuple[str | None, dict[str, Any] | None]: + """Resolve one optional spawn leaf reference, preserving the public refusal payload.""" + if leaf_ref is None: + return None, None + try: + return resolve_catalog_leaf_key(config, leaf_ref), None + except LeafRefResolutionError as exc: + return None, leaf_ref_refusal_payload("spawn_agent_session", leaf_ref, exc, kind=kind) def spawn_agent_session_payload( @@ -352,6 +574,7 @@ def spawn_agent_session_payload( *, harness: str | None = None, leaf_key: str | None = None, + replacement_for_leaf: str | None = None, context: str | None = None, submit: bool = False, label: str | None = None, @@ -368,54 +591,75 @@ def spawn_agent_session_payload( session_id: str | None = None, host: TerminalHost | None = None, paster: TerminalPaster | None = None, + session_log: HarnessSessionLog | None = None, which: Which | None = None, ) -> dict[str, Any]: """Spawn one role-configured, leaf-attached, context-primed hosted session (L2 dispatch). Composes the EXISTING session primitives -- the shared serving opener (create + leaf claim + - detached tmux ensure with env-seeded knobs), then an echo-confirmed context-packet paste with an - optional submit -- so orchestrators spawn managers and managers spawn workers without dashboard - clicks. ``harness`` is optional (260703-L13): omitted, it resolves per-use through the agentic - settings (repo-local over global ``orchestration.spawn.harness``), else the detection-gated - default. Leaf uniqueness stays server-arbitrated: a taken leaf returns ``leaf-taken`` (never - overridden). ``host``/``paster``/``which``/``session_id`` are injectable seams for tests. + detached tmux ensure with argv/session-command-pinned knobs), then a log-verified context input + with optional submit (``contextDelivered:true`` only after the id-bearing user record exists) -- + so orchestrators spawn + managers and managers spawn workers without dashboard clicks. The harness is resolved per-use + through the agentic settings (role/level knobs, repo-local over global + ``orchestration.spawn.harness``), else the detection-gated default. Leaf uniqueness stays + server-arbitrated: a taken leaf returns ``leaf-taken`` (never overridden). + ``host``/``paster``/``which``/``session_id`` are injectable seams for tests. Per-level knob resolution (ruling 2026-07-07T08:15): the settings rungs come from ``resolved_role_knobs(AR_SPAWN_ROLE, level)`` -- the ``orchestration.rolesPerLevel[level]`` - override deep-merged over the flat ``orchestration.roles`` default -- realizing the chain - explicit args > repo-local level override > global level override > repo-local role default > - global role default > detection-gated default. ``level`` is the dispatcher's declaration - (leaf|master|portfolio, default leaf); the RESOLVED level + its source (explicit/default) are - recorded in spawn provenance. - - Knob application (260703-L16): ``model``/``effort`` keep riding the spawn env AND are mapped onto - the harness argv per-harness via the EFFECTIVE registry -- the builtin table merged with the - ``orchestration.harnesses`` settings family (new ids add a harness, builtin ids can be - pre-customized; an id known nowhere refuses loudly pointing at ``docs/reference/harnesses.md``, - never a crash). Env-only builtins get no flags; a settings-defined harness with no declared - mapping refuses the knob with guidance (``model-invalid``/``effort-invalid``). ``effort`` is - validated against the resolved harness's known vocabulary BEFORE anything spawns -- an unknown - value returns the ``effort-invalid`` refusal naming the harness and its valid sets (the CLI - would warn-and-silently-degrade). A session-level effort value (claude ``ultracode``) rides a - post-launch session command instead of the flag. The free-form escape hatch is never validated, - only recorded in spawn provenance: ``launch_args`` (verbatim argv), ``session_commands`` (each - line pasted + submitted into the fresh session BEFORE the brief; the resolved list -- effort - vehicle first, then the caller's -- is what gets recorded), ``prompt_keywords`` (prepended as - the first line of the brief paste). + override deep-merged over the flat ``orchestration.roles`` default -- realizing the settings-only + chain repo-local level override > global level override > repo-local role default > global role + default > detection-gated default. ``level`` is the dispatcher's declaration (leaf|master|portfolio, + default leaf); the RESOLVED level + its source (explicit/default) are recorded in spawn provenance. + + Knob application (260703-L16): settings-resolved ``model``/``effort`` keep riding the spawn env + AND are mapped onto the harness argv per-harness via the EFFECTIVE registry -- the builtin table + merged with the ``orchestration.harnesses`` settings family (new ids add a harness, builtin ids + can be pre-customized; an id known nowhere refuses loudly pointing at + ``docs/reference/harnesses.md``, never a crash). Env-only builtins get no flags; a + settings-defined harness with no declared mapping refuses the knob with guidance + (``model-invalid``/``effort-invalid``). ``effort`` is validated against the resolved harness's + known vocabulary BEFORE anything spawns -- an unknown value returns the ``effort-invalid`` + refusal naming the harness and its valid sets (the CLI would warn-and-silently-degrade). A + session-level effort value (claude ``ultracode``) rides a post-launch session command instead of + the flag. The settings-owned free-form escape hatch is never validated, only recorded in spawn + provenance: ``launch_args`` (verbatim argv), ``session_commands`` (each line pasted + submitted + into the fresh session BEFORE the brief; the resolved list -- effort vehicle first, then + settings commands -- is what gets recorded), ``prompt_keywords`` (prepended as the first line of + the brief paste). """ - dispatch: _HarnessDispatch | None = None + spend_refusal = _caller_spend_override_refusal( + harness=harness, + model=model, + effort=effort, + env=env, + launch_args=launch_args, + prompt_keywords=prompt_keywords, + session_commands=session_commands, + kind=kind, + ) + if spend_refusal is not None: + return spend_refusal + leaf_key, refusal = _resolve_spawn_leaf(config, leaf_key, kind=kind) + if refusal is not None: + return refusal + replacement_for_leaf, refusal = _resolve_spawn_leaf(config, replacement_for_leaf, kind=kind) + if refusal is not None: + return refusal + + resolved_session_commands = list(session_commands or []) + spawn_level: str | None = None + spawn_level_source: str | None = None + resolved_model: str | None = None + resolved_effort: str | None = None + harnesses: tuple[Harness, ...] | None = None if kind == "harness": dispatch, refusal = _resolve_harness_dispatch( config, - harness=harness, - leaf_key=leaf_key, + leaf_key=leaf_key or replacement_for_leaf, level=level, - model=model, - effort=effort, env=env, - launch_args=launch_args, - prompt_keywords=prompt_keywords, - session_commands=session_commands, which=which, ) if refusal is not None: @@ -426,10 +670,13 @@ def spawn_agent_session_payload( effort = dispatch.effort launch_args = dispatch.launch_args prompt_keywords = dispatch.prompt_keywords + resolved_session_commands = dispatch.session_commands + spawn_level = dispatch.spawn_level + spawn_level_source = dispatch.spawn_level_source + resolved_model = dispatch.model + resolved_effort = dispatch.effort + harnesses = dispatch.registry - resolved_session_commands = ( - dispatch.session_commands if dispatch is not None else list(session_commands or []) - ) sid = session_id or uuid4().hex spawn_env = _spawn_env(model, effort, env) provenance_lifecycle = spawned_by_lifecycle or _ambient_lifecycle_id() @@ -444,19 +691,22 @@ def spawn_agent_session_payload( kind=kind, workspace_root=config.workspace_root, shell=shell, - harness=harness if kind == "harness" else None, + harness=harness, label=label, leaf_key=leaf_key, + replacement_for_leaf=replacement_for_leaf, env=spawn_env, launch_args=launch_args, prompt_keywords=prompt_keywords, session_commands=resolved_session_commands or None, - spawn_level=dispatch.spawn_level if dispatch is not None else None, - spawn_level_source=dispatch.spawn_level_source if dispatch is not None else None, + spawn_level=spawn_level, + spawn_level_source=spawn_level_source, + resolved_model=resolved_model, + resolved_effort=resolved_effort, spawned_by_session=spawned_by_session, spawned_by_lifecycle=provenance_lifecycle, which=which, - harnesses=dispatch.registry if dispatch is not None else None, + harnesses=harnesses, ) if result.status == "bad-kind": @@ -469,38 +719,85 @@ def spawn_agent_session_payload( "operation": "spawn_agent_session", "status": "leaf-taken", "session": sid, - "harness": harness if kind == "harness" else None, + "harness": harness, "kind": result.kind, "leafKey": leaf_key, + "seatRole": result.seat_role, "ownerSession": result.owner_session_id, }, ) entry = result.entry assert entry is not None # opened => an upserted row + spawn_paster = paster if paster is not None else TerminalPaster() + _write_spawn_expectation_rows(config, entry) packet = _brief_packet(context, prompt_keywords) - session_commands_delivered: bool | None = None - context_delivered: bool | None = None - submitted: bool | None = None + delivery = _SpawnDelivery() if resolved_session_commands or packet: - spawn_paster = paster if paster is not None else TerminalPaster() - session_commands_delivered, context_delivered, submitted = _deliver_spawn_pastes( - spawn_paster, entry.tmux_name, resolved_session_commands, packet, submit + delivery = _deliver_spawn_pastes( + spawn_paster, + entry.tmux_name, + resolved_session_commands, + packet, + submit, + entry_id=entry.id, + harness=harness, + cwd=entry.cwd, + created_at=entry.created_at, + session_log=session_log, ) - - return _tool_payload( - "spawn_agent_session", - _spawned_payload(entry, session_commands_delivered, context_delivered, submitted), + if delivery.session_log_path is not None and delivery.bound_entry_id is not None: + bound = catalog.bind_session_log( + entry.id, + entry_id=delivery.bound_entry_id, + path=delivery.session_log_path, + ) + if bound is not None: + entry = bound + + return _tool_payload("spawn_agent_session", _spawned_payload(entry, delivery)) + + +def _write_spawn_expectation_rows(config: McpRuntimeConfig, entry: TerminalCatalogEntry) -> None: + """Every spawn atomically writes ``briefed-by`` (the id-bearing log entry must exist), plus a + ``turn-report-by`` row when the spawn carries a bound or declared replacement leaf (a bare + scratch/command chat with no ``leaf_key`` owes no turn report). Written in the SAME call as + the catalog upsert -- never a forgettable follow-up step.""" + settings = load_agentic_settings(config.coordination_root) + store = ExpectationRowStore(observer_root(config)) + now = datetime.now(UTC) + expectation_leaf = entry.leaf_key or entry.replacement_for_leaf + write_expectation_row( + store, + row_id=uuid4().hex, + now=now, + kind="briefed-by", + sla_seconds=settings.expectations.sla_for("briefed-by"), + source_id=entry.id, + subject_agent_id=entry.id, + subject_lifecycle_id=entry.lifecycle_id, + leaf_key=expectation_leaf, + seat_role=entry.binding_role, + note=f"briefed-by: {entry.label} ({entry.spawn_role or entry.kind})", ) + if expectation_leaf is not None: + write_expectation_row( + store, + row_id=uuid4().hex, + now=now, + kind="turn-report-by", + sla_seconds=settings.expectations.sla_for("turn-report-by"), + source_id=entry.id, + subject_agent_id=entry.id, + subject_lifecycle_id=entry.lifecycle_id, + leaf_key=expectation_leaf, + seat_role=entry.binding_role, + note=f"turn-report-by: {expectation_leaf}", + ) -def _spawned_payload( - entry: TerminalCatalogEntry, - session_commands_delivered: bool | None, - context_delivered: bool | None, - submitted: bool | None, -) -> dict[str, Any]: +def _spawned_payload(entry: TerminalCatalogEntry, delivery: _SpawnDelivery) -> dict[str, Any]: """The ``spawned`` payload: the upserted row (incl. the L16 provenance) + delivery outcomes.""" return { "ok": True, @@ -510,6 +807,8 @@ def _spawned_payload( "harness": entry.harness, "kind": entry.kind, "leafKey": entry.leaf_key, + "seatRole": entry.binding_role, + "replacementForLeaf": entry.replacement_for_leaf, "label": entry.label, "cwd": str(entry.cwd), "tmuxName": entry.tmux_name, @@ -519,13 +818,20 @@ def _spawned_payload( # The resolved dispatch level + how it was supplied (rolesPerLevel resolution input). "spawnLevel": entry.spawn_level, "spawnLevelSource": entry.spawn_level_source, + "resolvedModel": entry.resolved_model, + "resolvedEffort": entry.resolved_effort, + "sessionLogEntryId": entry.session_log_entry_id, + "sessionLogPath": str(entry.session_log_path) if entry.session_log_path else None, # Free-form spawn provenance (260703-L16), echoed as recorded on the catalog row. "launchArgs": list(entry.launch_args) if entry.launch_args else None, "promptKeywords": list(entry.prompt_keywords) if entry.prompt_keywords else None, "sessionCommands": list(entry.session_commands) if entry.session_commands else None, - "sessionCommandsDelivered": session_commands_delivered, - "contextDelivered": context_delivered, - "submitted": submitted, + "sessionCommandsDelivered": delivery.session_commands_delivered, + "contextDelivered": delivery.context_delivered, + "submitted": delivery.submitted, + # 260707-HFX-L3 loud failure: on any False outcome above the pane capture is the attached + # evidence -- the caller must treat the seat as blind, never assume the brief landed. + "deliveryCapture": delivery.failure_capture, } @@ -549,3 +855,145 @@ def _spawn_refusal( "detail": detail, }, ) + + +def session_retire_payload( + config: McpRuntimeConfig, + *, + actor_session_id: str, + session_id: str, + reason: str = "manual retire", + host: TerminalHost | None = None, +) -> dict[str, Any]: + """Retire ``session_id`` (issue #12): terminal mark + provenance, authority enforced server-side. + + ``actor_session_id`` is the RETIRING seat's own catalog session id (self-declared, mirroring the + ``spawned_by_session`` provenance pattern -- there is no ambient "who am I" session-id + resolution). Authority: owner-never-self-retires; a manager retires only worker/reviewer seats + of its own master; the orchestrator retires anything. Idempotent against an already-retired + target -- a second retire call reports ``already-retired``, never re-stamps provenance. + """ + catalog = TerminalCatalog(terminal_catalog_path(config.coordination_root)) + target_entry = catalog.get(session_id) + if target_entry is None: + return _tool_payload( + "session_retire", + { + "ok": False, + "operation": "session_retire", + "status": "unknown-session", + "session": session_id, + "detail": f"no catalog entry for session {session_id!r}", + }, + ) + actor_entry = catalog.get(actor_session_id) + if actor_entry is None: + return _tool_payload( + "session_retire", + { + "ok": False, + "operation": "session_retire", + "status": "unknown-actor", + "session": session_id, + "detail": f"no catalog entry for actor session {actor_session_id!r}", + }, + ) + if target_entry.status == "terminated": + return _tool_payload( + "session_retire", + { + "ok": True, + "operation": "session_retire", + "status": "already-retired", + "session": session_id, + "retiredAt": target_entry.retired_at, + "retiredBySession": target_entry.retired_by_session, + "retiredReason": target_entry.retired_reason, + "retiredEdge": target_entry.retired_edge, + }, + ) + try: + check_retire_authority( + SeatRef( + session_id=actor_entry.id, + leaf_key=actor_entry.binding_leaf_key, + seat_role=actor_entry.binding_role, + ), + SeatRef( + session_id=target_entry.id, + leaf_key=target_entry.binding_leaf_key, + seat_role=target_entry.binding_role, + ), + ) + except RetirePolicyError as exc: + return _tool_payload( + "session_retire", + { + "ok": False, + "operation": "session_retire", + "status": "retire-refused", + "session": session_id, + "detail": str(exc), + }, + ) + retire_host = host if host is not None else TerminalHost() + updated = retire_entry( + catalog, + retire_host, + target_entry, + at=now_iso(), + by_session=actor_session_id, + reason=reason, + edge="manual", + ) + assert updated is not None # the entry existed above; nothing between here removes rows + log_retire_event(config, updated) + return _tool_payload( + "session_retire", + { + "ok": True, + "operation": "session_retire", + "status": "retired", + "session": session_id, + "retiredAt": updated.retired_at, + "retiredBySession": updated.retired_by_session, + "retiredReason": updated.retired_reason, + "retiredEdge": updated.retired_edge, + }, + ) + + +def session_rename_payload( + config: McpRuntimeConfig, + *, + session_id: str, + label: str, +) -> dict[str, Any]: + """Rename ``session_id``'s display label post-spawn (issue #4). Identity text only -- never role.""" + catalog = TerminalCatalog(terminal_catalog_path(config.coordination_root)) + entry = catalog.get(session_id) + if entry is None or entry.status == "terminated": + return _tool_payload( + "session_rename", + { + "ok": False, + "operation": "session_rename", + "status": "unknown-session", + "session": session_id, + "label": label, + }, + ) + updated = catalog.set_label(session_id, label) + assert updated is not None + log_rename_event(config, updated) + return _tool_payload( + "session_rename", + { + "ok": True, + "operation": "session_rename", + "status": "renamed", + "session": session_id, + "label": updated.label, + "spawnedLabel": updated.spawned_label, + }, + ) diff --git a/mcp/src/agents_remember/models/lifecycle_finalize.py b/mcp/src/agents_remember/models/lifecycle_finalize.py index 7bff7b3b..ec2caccc 100644 --- a/mcp/src/agents_remember/models/lifecycle_finalize.py +++ b/mcp/src/agents_remember/models/lifecycle_finalize.py @@ -27,3 +27,6 @@ class LifecycleFinalizeTaskResponse(ToolResponse): taskUpdates: dict[str, Any] = Field(default_factory=dict) taskArchive: dict[str, Any] = Field(default_factory=dict) summary: str = "" + # Session ids auto-landed at this master->super finalize edge (config-gated, default ON). Empty + # when the gate is off, nothing matched, or this call was a dry run. + autoLandedSeats: list[str] = Field(default_factory=list) diff --git a/mcp/src/agents_remember/models/operator_inbox.py b/mcp/src/agents_remember/models/operator_inbox.py index 4e61b6c5..519ce0fc 100644 --- a/mcp/src/agents_remember/models/operator_inbox.py +++ b/mcp/src/agents_remember/models/operator_inbox.py @@ -23,6 +23,9 @@ class OperatorInboxPostResponse(ToolResponse): senderAgentId: str | None = None senderRole: AgentRole | None = None recipientRole: AgentRole | None = None + ownerRole: AgentRole | None = None + ownerAgentId: str | None = None + ownerLifecycleId: str | None = None gateId: str | None = None messageKind: InboxMessageKind artifactPath: str | None = None diff --git a/mcp/src/agents_remember/models/terminal.py b/mcp/src/agents_remember/models/terminal.py index 02bf3dfe..4a135226 100644 --- a/mcp/src/agents_remember/models/terminal.py +++ b/mcp/src/agents_remember/models/terminal.py @@ -6,7 +6,14 @@ from agents_remember.models.base import ToolResponse -LeafAssignmentStatus = Literal["attached", "leaf-taken", "unknown-session"] +LeafAssignmentStatus = Literal[ + "attached", + "leaf-taken", + "unknown-session", + "role-required", + "leaf-ref-not-found", + "leaf-ref-ambiguous", +] class AttachTerminalSessionToLeafResponse(ToolResponse): @@ -19,6 +26,9 @@ class AttachTerminalSessionToLeafResponse(ToolResponse): previousLeafKey: str | None = None ownerSession: str | None = None role: Literal["chat", "terminal"] | None = None + seatRole: str | None = None + previousSeatRole: str | None = None + detail: str | None = None SpawnAgentSessionStatus = Literal[ @@ -33,8 +43,15 @@ class AttachTerminalSessionToLeafResponse(ToolResponse): # 260703-L16: a settings-defined harness with no declared modelFlag got a model knob -- refused # with guidance (declare the flag or use launchArgs); explicit over guessing. "model-invalid", + # 260707-HFX2-L10: ordinary spawn callers cannot choose spend knobs. Removed/legacy caller + # harness/model/effort fields, direct free-form launch/session controls, AR_SPAWN_MODEL/ + # AR_SPAWN_EFFORT, and harness-native spend/endpoint env keys refuse before spawning; settings + # are the authority. + "spend-override-unsupported", # 260703-L16 (ruling 2026-07-07T08:15): the dispatch level is outside leaf|master|portfolio. "level-invalid", + "leaf-ref-not-found", + "leaf-ref-ambiguous", "bad-kind", ] @@ -42,7 +59,7 @@ class AttachTerminalSessionToLeafResponse(ToolResponse): class SpawnAgentSessionResponse(ToolResponse): """``spawn_agent_session``: spawn a role-configured, leaf-attached, context-primed hosted session. - Composes the existing session primitives (opener + leaf claim + echo-confirmed paste + optional + Composes the existing session primitives (opener + leaf claim + log-verified input + optional submit). ``ok`` is true only for ``spawned``; ``leaf-taken`` surfaces the server-arbitrated refusal (the tool never overrides it), and the harness/kind statuses report a validation refusal before anything is spawned. @@ -54,6 +71,8 @@ class SpawnAgentSessionResponse(ToolResponse): harness: str | None = None kind: Literal["harness", "terminal"] | None = None leafKey: str | None = None + seatRole: str | None = None + replacementForLeaf: str | None = None label: str | None = None cwd: str | None = None tmuxName: str | None = None @@ -67,6 +86,10 @@ class SpawnAgentSessionResponse(ToolResponse): # (260703-L16, ruling 2026-07-07T08:15), recorded on the catalog row. spawnLevel: str | None = None spawnLevelSource: str | None = None + resolvedModel: str | None = None + resolvedEffort: str | None = None + sessionLogEntryId: str | None = None + sessionLogPath: str | None = None # Free-form spawn provenance (260703-L16), as recorded on the catalog row: launchArgs rode the # argv verbatim, sessionCommands were pasted post-launch before the brief (the resolved list -- # a session-vocabulary effort like claude's ultracode arrives here as "/effort ultracode"), @@ -74,11 +97,60 @@ class SpawnAgentSessionResponse(ToolResponse): launchArgs: list[str] | None = None promptKeywords: list[str] | None = None sessionCommands: list[str] | None = None - # Whether every session command was echo-confirmed AND submitted (None = none were sent). + # Whether every session command has both a bound-log command entry and non-error stdout. sessionCommandsDelivered: bool | None = None # Set on ``leaf-taken``: the running same-role session that already owns the leaf. ownerSession: str | None = None - # Context-packet delivery outcome (echo-confirmed paste; submit only when requested). + # Context-packet delivery outcome: true only after the id-bearing user entry is in the bound log. contextDelivered: bool | None = None submitted: bool | None = None + # 260707-HFX-L3 loud-failure evidence: the final pane capture, attached whenever any delivery + # outcome above reports False -- a blind seat is diagnosed from the payload itself, never + # trusted from a bare boolean. Absent on full success. + deliveryCapture: str | None = None + detail: str | None = None + + +SessionRetireStatus = Literal[ + "retired", + "already-retired", + "unknown-session", + "unknown-actor", + "retire-refused", +] + + +class SessionRetireResponse(ToolResponse): + """``session_retire`` (260707-HFX-L8, issue #12): terminate/park a tracked chat session. + + ``ok`` is true for ``retired``/``already-retired`` (idempotent); false for every refusal + status. ``retire-refused`` is the server-side authority-policy refusal (owner-never-self- + retires, manager-scoped-to-its-own-master, orchestrator-only-otherwise) -- ``detail`` names the + exact clause that fired. + """ + + operation: Literal["session_retire"] = "session_retire" + status: SessionRetireStatus + session: str + retiredAt: str | None = None + retiredBySession: str | None = None + retiredReason: str | None = None + retiredEdge: str | None = None detail: str | None = None + + +SessionRenameStatus = Literal["renamed", "unknown-session"] + + +class SessionRenameResponse(ToolResponse): + """``session_rename`` (260707-HFX-L8, issue #4): update a chat's display label post-spawn. + + Identity text only -- the seat's ``spawn_role`` (L6 role-seat immutability) never changes. + ``spawnedLabel`` is the ORIGINAL spawn-time label, frozen for audit on the first rename. + """ + + operation: Literal["session_rename"] = "session_rename" + status: SessionRenameStatus + session: str + label: str | None = None + spawnedLabel: str | None = None diff --git a/mcp/src/agents_remember/models/tool_registry.py b/mcp/src/agents_remember/models/tool_registry.py index 45ba3697..c22d7a80 100644 --- a/mcp/src/agents_remember/models/tool_registry.py +++ b/mcp/src/agents_remember/models/tool_registry.py @@ -82,6 +82,8 @@ from agents_remember.models.task_doc import TaskDocResponse, TaskReopenResponse from agents_remember.models.terminal import ( AttachTerminalSessionToLeafResponse, + SessionRenameResponse, + SessionRetireResponse, SpawnAgentSessionResponse, ) from agents_remember.models.worktree import ( @@ -113,6 +115,8 @@ "read_ar_files": ReadArFilesResponse, "attach_terminal_session_to_leaf": AttachTerminalSessionToLeafResponse, "spawn_agent_session": SpawnAgentSessionResponse, + "session_retire": SessionRetireResponse, + "session_rename": SessionRenameResponse, "runtime_install": RuntimeInstallResponse, "resolve_context": ResolveContextResponse, "drift_check": DriftCheckResponse, diff --git a/mcp/src/agents_remember/observer/__init__.py b/mcp/src/agents_remember/observer/__init__.py index e2878571..2b519983 100644 --- a/mcp/src/agents_remember/observer/__init__.py +++ b/mcp/src/agents_remember/observer/__init__.py @@ -46,6 +46,7 @@ Analytics, DriftSnapshotNode, EnclosureNode, + ExpectationRowNode, LedgerNode, LifecycleProjection, Metrics, @@ -100,6 +101,7 @@ "EnclosureNode", "Event", "EventStore", + "ExpectationRowNode", "GuardedStartError", "LedgerNode", "LifecycleError", diff --git a/mcp/src/agents_remember/observer/ambient.py b/mcp/src/agents_remember/observer/ambient.py index ab34ed5f..171a892b 100644 --- a/mcp/src/agents_remember/observer/ambient.py +++ b/mcp/src/agents_remember/observer/ambient.py @@ -25,6 +25,7 @@ from collections.abc import Callable from dataclasses import replace from datetime import UTC, datetime +from pathlib import Path from typing import Any from pydantic import ValidationError @@ -111,6 +112,15 @@ def __init__( self._served_store = served_store or ServedStore(store.root) self._served: dict[str, set[str]] = {} + @property + def root(self) -> Path: + """The observer store root (``logs/observer``) this ambient lifecycle writes under. + + 260707-HFX2-L2 R5: the MCP tool choke point (``_tool_payload``) reads this to check the + supervisor heartbeat opportunistically, without needing its own ``McpRuntimeConfig``. + """ + return self._store.root + # --- signals ----------------------------------------------------------- def start(self, *, fleeting: bool = True, phase: Phase = INITIAL_PHASE) -> LifecycleState: diff --git a/mcp/src/agents_remember/observer/event_retention.py b/mcp/src/agents_remember/observer/event_retention.py index 9c8af88b..5158ec9a 100644 --- a/mcp/src/agents_remember/observer/event_retention.py +++ b/mcp/src/agents_remember/observer/event_retention.py @@ -11,16 +11,27 @@ from __future__ import annotations import json +import os +import shutil from collections.abc import Iterator from contextlib import suppress from datetime import UTC, datetime, timedelta from pathlib import Path from typing import Any +from agents_remember.observer.store import ( + HEARTBEAT_KIND, + WORKSPACE_SOURCE, + set_workspace_base_offset, + workspace_base_offset, + workspace_river_lock, +) + # Legacy terminal grace, kept for the explicit ``lifecycle.ended`` reader and the # workspace TTL below. LIFECYCLE_EVENT_GRACE_SECONDS = 60 * 60 WORKSPACE_EVENT_TTL_SECONDS = 60 * 60 +WORKSPACE_EVENT_COMPACT_INTERVAL_SECONDS = 60.0 # Inactivity TTLs: how long a log may sit with no real (non-heartbeat) activity # before the dashboard retires it. Fleeting sessions and enclosure-backed # (promoted) lifecycles get their own knob even though they currently match. @@ -29,8 +40,6 @@ # A fresh connection replays at most this much recent history per active source, # so a reload never re-streams an entire long-lived log from byte zero. REPLAY_WINDOW_SECONDS = 60 * 60 -WORKSPACE_SOURCE = "workspace" -HEARTBEAT_KIND = "lifecycle.heartbeat" def initial_event_offsets(root: Path, *, now: datetime) -> dict[str, int]: @@ -55,7 +64,8 @@ def initial_event_offsets(root: Path, *, now: datetime) -> dict[str, int]: workspace = root / WORKSPACE_SOURCE / "events.jsonl" if workspace.is_file(): - offsets[WORKSPACE_SOURCE] = _first_retained_offset( + base_offset = workspace_base_offset(root) + offsets[WORKSPACE_SOURCE] = base_offset + _first_retained_offset( workspace, cutoff=now - timedelta(seconds=WORKSPACE_EVENT_TTL_SECONDS) ) return offsets @@ -88,13 +98,68 @@ def prune_expired_lifecycle_event_logs( continue # part of a live master series -> never pruned by inactivity if not lifecycle_is_dormant(path, now=now): continue - path.unlink() + # F7 reclamation owns the whole dormant lifecycle directory, not only events.jsonl. + # Heartbeats are coalesced into heartbeat.json and served-onboarding state may also live + # beside the log; unlinking only the log makes every such directory permanently + # unreapable. The lifecycle is dormant and unprotected at this point, so reclaim the + # complete directory through the same project-and-prune boundary. + shutil.rmtree(entry) removed.append(path) - with suppress(OSError): - entry.rmdir() return removed +def compact_workspace_river( + root: Path, *, now: datetime, ttl_seconds: float = WORKSPACE_EVENT_TTL_SECONDS +) -> int: + """Reclaim ``workspace/events.jsonl`` rows older than the replay TTL (F3/CS-6 D3). + + The per-lifecycle logs are reclaimed wholesale by :func:`prune_expired_lifecycle_event_logs`, but + the shared workspace river is a single append-only file that is never deleted -- so it is the one + river that grows unbounded on disk, even though a fresh connection already only replays its last + ``WORKSPACE_EVENT_TTL_SECONDS`` (:func:`initial_event_offsets`). Rows older than that window are + therefore never streamed to any client and are safe to drop physically. + + CURSOR-SAFETY: live ``/api/events`` clients carry a VIRTUAL byte offset for the workspace source. + Physical compaction increases ``events.cursor.json::baseOffset`` by the bytes dropped, so a cursor + issued before compaction still maps onto the same retained tail after the rewrite. The compactor and + the two workspace writers share ``events.lock`` through :func:`workspace_river_lock`, so no process + appends into a file another process is replacing. + + Returns the number of rows reclaimed. + """ + path = root / WORKSPACE_SOURCE / "events.jsonl" + if not path.is_file(): + return 0 + with workspace_river_lock(root): + cutoff = now - timedelta(seconds=ttl_seconds) + retained_start: int | None = None + dropped = 0 + for start, _offset_after, payload in _iter_event_payloads(path): + ts = _event_time(payload) + if ts is None or ts >= cutoff: + retained_start = start + break + dropped += 1 + if dropped == 0: + return 0 + if retained_start is None: + # Every row aged out -> truncate to an empty (but present) source file. + retained_start = path.stat().st_size + with path.open("rb") as handle: + handle.seek(retained_start) + retained = handle.read() + tmp = path.with_name(f".{path.name}.compact.{os.getpid()}.tmp") + try: + tmp.write_bytes(retained) + os.replace(tmp, path) + set_workspace_base_offset(root, workspace_base_offset(root) + retained_start) + except BaseException: + with suppress(OSError): + tmp.unlink() + raise + return dropped + + def lifecycle_is_dormant(path: Path, *, now: datetime) -> bool: """True when a log has had no real activity past its inactivity TTL. diff --git a/mcp/src/agents_remember/observer/projection.py b/mcp/src/agents_remember/observer/projection.py index feacadb1..11fa404c 100644 --- a/mcp/src/agents_remember/observer/projection.py +++ b/mcp/src/agents_remember/observer/projection.py @@ -278,7 +278,13 @@ class SetupProgressNode(BaseModel): class AgentPickupNode(BaseModel): - """A pending dashboard response waiting for an agent to consume it.""" + """A pending dashboard response waiting for an agent to consume it. + + R5 (260707-HFX2-L1): every pending row here IS an unacked signal -- consume=ack is the only + terminal outcome, so this list already surfaces "pending/unacked" for the dashboard. The + attempt/backoff/escalation fields ride along so the panel can show redelivery state without a + second surface; an L2 predicate reads the stores directly and never this projection. + """ model_config = ConfigDict(extra="forbid") @@ -289,16 +295,42 @@ class AgentPickupNode(BaseModel): senderAgentId: str | None = None senderRole: str | None = None recipientRole: str | None = None + ownerRole: str | None = None + ownerAgentId: str | None = None + ownerLifecycleId: str | None = None gateId: str | None = None messageKind: str = "message" artifactPath: str | None = None deliveryState: str = "queued" deliveredToSession: str | None = None + attemptCount: int = 0 + lastAttemptAt: str | None = None + nextAttemptAt: str | None = None + escalatedAt: str | None = None state: str ageSeconds: float | None = None ttlSeconds: float +class ExpectationRowNode(BaseModel): + """A durable what-must-happen-by-when row (R2/R5, 260707-HFX2-L1): projected for dashboard/ + architect observability. An L2 predicate reads ``ExpectationRowStore`` directly, never this + node -- surfacing only, never the correctness path.""" + + model_config = ConfigDict(extra="forbid") + + id: str + kind: str + state: str + sourceId: str + subjectAgentId: str | None = None + subjectLifecycleId: str | None = None + leafKey: str | None = None + dueAt: str + overdue: bool = False + note: str | None = None + + class RouteCoverageNode(BaseModel): """One onboarding route's coverage, from its ``overview.index.json`` (3b, surface 10).""" @@ -473,6 +505,9 @@ class TaskDocNode(BaseModel): stepsTotal: int = 0 currentStep: str | None = None docPath: str + # Hash of the reader body fields omitted from the always-on summary. Dashboard readers use it + # to refetch an already-open task document only when the on-demand body changed. + bodyRevision: str = "" createdAt: str = "" ageSeconds: float | None = None steps: list[TaskStepNode] = Field(default_factory=list) @@ -764,6 +799,33 @@ class EngineProcessFacts: ledger_row_count: int = 0 +# 260707-HFX2-L13 F6/CS-6 D2+D1: ``TaskDocNode`` in the always-on projection is a bounded summary. +# The full reader body is fetched through the on-demand task-document endpoint. This budget remains as +# a regression guardrail: the write path should now measure 0 body bytes for broadcast task documents. +TASK_DOCUMENTS_PAYLOAD_BUDGET_BYTES = 256 * 1024 + + +def task_documents_body_bytes(docs: list[TaskDocNode]) -> int: + """Approximate byte cost of task-doc reader bodies still present in the broadcast (F6 guardrail). + + For summary nodes this returns zero; tests seed full nodes to prove the guard still catches any + regression that reintroduces body fields to the always-on path. + """ + total = 0 + for doc in docs: + total += len(doc.objective) + len(doc.design or "") + len(doc.currentStep or "") + total += sum(len(item) for item in doc.requirements) + total += sum(len(item) for item in doc.openQuestions) + total += sum(len(item) for item in doc.references) + total += sum(len(section.heading) + len(section.body) for section in doc.sections) + total += sum( + len(example.distinctChange) + len(example.why) + len(example.snippet) + len(example.title) + for example in doc.codeExamples + ) + total += sum(len(decision.decision) + len(decision.rationale) for decision in doc.decisions) + return total + + class Analytics(BaseModel): """The slice-3b analytical surfaces: charts/feeds for specific cockpit panels. @@ -786,6 +848,7 @@ class Analytics(BaseModel): routeCoverage: list[RouteCoverageNode] = Field(default_factory=list) toolReports: list[ToolReportNode] = Field(default_factory=list) agentPickups: list[AgentPickupNode] = Field(default_factory=list) + expectationRows: list[ExpectationRowNode] = Field(default_factory=list) ledgers: list[LedgerNode] = Field(default_factory=list) taskDocuments: list[TaskDocNode] = Field(default_factory=list) attentionQueue: list[AttentionItem] = Field(default_factory=list) diff --git a/mcp/src/agents_remember/observer/projection_store.py b/mcp/src/agents_remember/observer/projection_store.py index fdfcf2c7..c47d2c99 100644 --- a/mcp/src/agents_remember/observer/projection_store.py +++ b/mcp/src/agents_remember/observer/projection_store.py @@ -28,10 +28,12 @@ from agents_remember.observer.lifecycle_state import TERMINAL_STATES from agents_remember.observer.paths import observer_root from agents_remember.observer.projection import ( + TASK_DOCUMENTS_PAYLOAD_BUDGET_BYTES, LedgerNode, RouteCoverageNode, SidecarStaleNode, WorkspaceProjection, + task_documents_body_bytes, ) from agents_remember.observer.reducer import project_workspace from agents_remember.observer.snapshots import ( @@ -39,6 +41,7 @@ read_drift_snapshots, read_enclosures, read_engine_process_facts, + read_expectation_rows, read_gates, read_ledger, read_providers, @@ -67,9 +70,16 @@ LATEST_METRICS = "latest-metrics.json" PROVIDER_REFRESH_TTL_SECONDS = 10.0 REPO_SURFACE_REFRESH_TTL_SECONDS = 15.0 +# F6 guardrail: the projection ticks every 1s, so a per-tick over-budget warning would be pure spam. +# Log at most this often -- enough to surface a body-heavy broadcast in the logs without flooding them. +TASK_DOCUMENTS_PAYLOAD_WARN_INTERVAL_SECONDS = 300.0 logger = logging.getLogger(__name__) +# F6: last time the over-budget task-doc payload warning fired, keyed "at" (an in-place-mutated dict -- +# the codebase idiom for cross-tick module state, e.g. _task_doc_cache -- so no rebinding `global`). +_last_task_payload_warn: dict[str, datetime] = {} + @dataclass(frozen=True) class _RepoSurfaceCacheEntry: @@ -80,19 +90,59 @@ class _RepoSurfaceCacheEntry: _repo_surface_cache: dict[tuple[tuple[str, str, str], ...], _RepoSurfaceCacheEntry] = {} +@dataclass +class _LifecycleLogCacheEntry: + mtime_ns: int + size: int + log_events: list[Event] + + +# 260707-HFX2-L12 F9/F7: the projection re-read + re-validated EVERY lifecycle's full events.jsonl +# each 1s tick. Heartbeats append only every 15s (and stop entirely once a leaf is parked past its +# inactivity cutoff), so most logs are byte-identical between ticks. This cache reuses the parsed +# event list when a log's (mtime_ns, size) is unchanged, bounding per-tick parse cost to the logs +# that actually changed. Keyed by log path; pruned to the live set each tick so it cannot leak. +_lifecycle_log_cache: dict[str, _LifecycleLogCacheEntry] = {} + + def read_lifecycle_logs(root: Path) -> list[list[Event]]: - """Every per-lifecycle log under ``lifecycles//events.jsonl``, validated.""" + """Every per-lifecycle log under ``lifecycles//events.jsonl``, validated (F9/F7-cached). + + Heartbeats are coalesced into ``heartbeat.json`` sidecars, so a heartbeat tick changes one tiny + JSON file instead of invalidating the cached parse of the full event log. + """ store = EventStore(root) lifecycles_dir = root / "lifecycles" if not lifecycles_dir.is_dir(): return [] logs: list[list[Event]] = [] + seen: set[str] = set() for entry in sorted(lifecycles_dir.iterdir()): if not entry.is_dir(): continue - events = store.read(entry.name) + path = entry / "events.jsonl" + try: + stat = path.stat() + except OSError: + continue + key = str(path) + seen.add(key) + cached = _lifecycle_log_cache.get(key) + if cached is not None and cached.mtime_ns == stat.st_mtime_ns and cached.size == stat.st_size: + log_events = cached.log_events + else: + log_events = store.read_log(entry.name) + _lifecycle_log_cache[key] = _LifecycleLogCacheEntry( + mtime_ns=stat.st_mtime_ns, size=stat.st_size, log_events=log_events + ) + events = list(log_events) + heartbeat = store.read_heartbeat(entry.name) + if heartbeat is not None and (not events or heartbeat.ts >= events[-1].ts): + events.append(heartbeat) if events: logs.append(events) + for stale in [key for key in _lifecycle_log_cache if key not in seen]: + _lifecycle_log_cache.pop(stale, None) return logs @@ -180,11 +230,12 @@ def project_and_write( route_coverage=route_coverage, tool_reports=read_tool_reports(coordination_root, now=moment), agent_pickups=read_agent_pickups(coordination_root, now=moment), + expectation_rows=read_expectation_rows(coordination_root, now=moment), ledgers=ledgers, task_documents=read_task_documents(coordination_root, enclosures=enclosures, now=moment), series=read_series_documents(coordination_root, now=moment), engine_process_facts=read_engine_process_facts( - coordination_root, active_worktree_groups=engine_groups + coordination_root, active_worktree_groups=engine_groups, now=moment ), engine_start_progress=read_start_progress_entries(coordination_root, now=moment), gates=read_gates(coordination_root, now=moment), @@ -197,10 +248,40 @@ def project_and_write( attention_store.prune_lifecycles( {lifecycle.id for lifecycle in projection.lifecycles if lifecycle.state not in TERMINAL_STATES} ) + _warn_if_task_documents_payload_over_budget(projection, now=moment) write_projection(root, projection) return projection +def _warn_if_task_documents_payload_over_budget( + projection: WorkspaceProjection, *, now: datetime +) -> int: + """Flag (rate-limited) when the broadcast serialises a body-heavy task-doc payload (F6/CS-6 D1). + + Returns the measured body-byte cost so callers/tests can assert on it. This is observability only -- + no truncation, no contract change -- surfacing the unbounded ``analytics.taskDocuments`` payload that + the escalated F6 windowing follow-up will bound, so the growth is visible in logs instead of silent. + """ + payload_bytes = task_documents_body_bytes(projection.analytics.taskDocuments) + if payload_bytes <= TASK_DOCUMENTS_PAYLOAD_BUDGET_BYTES: + return payload_bytes + last = _last_task_payload_warn.get("at") + if last is not None: + age = (now - last).total_seconds() + if 0 <= age < TASK_DOCUMENTS_PAYLOAD_WARN_INTERVAL_SECONDS: + return payload_bytes + _last_task_payload_warn["at"] = now + logger.warning( + "analytics.taskDocuments broadcast body payload %d bytes exceeds budget %d across %d docs; " + "task-doc bodies are serialised into every projection tick -- F6 windowing (bodies on-demand) " + "is the follow-up that bounds this", + payload_bytes, + TASK_DOCUMENTS_PAYLOAD_BUDGET_BYTES, + len(projection.analytics.taskDocuments), + ) + return payload_bytes + + def _gather_repo_surfaces( config: McpRuntimeConfig, moment: datetime ) -> tuple[list[SidecarStaleNode], list[RouteCoverageNode], list[LedgerNode]]: diff --git a/mcp/src/agents_remember/observer/reducer.py b/mcp/src/agents_remember/observer/reducer.py index 33e260ba..4148a997 100644 --- a/mcp/src/agents_remember/observer/reducer.py +++ b/mcp/src/agents_remember/observer/reducer.py @@ -42,6 +42,7 @@ EngineProcessEdge, EngineProcessFacts, EngineProcessNode, + ExpectationRowNode, GateNode, LandingRefNode, LedgerNode, @@ -108,6 +109,7 @@ def project_workspace( route_coverage: list[RouteCoverageNode] | None = None, tool_reports: list[ToolReportNode] | None = None, agent_pickups: list[AgentPickupNode] | None = None, + expectation_rows: list[ExpectationRowNode] | None = None, ledgers: list[LedgerNode] | None = None, task_documents: list[TaskDocNode] | None = None, series: list[SeriesNode] | None = None, @@ -157,6 +159,7 @@ def project_workspace( route_coverage=route_coverage or [], tool_reports=tool_reports or [], agent_pickups=agent_pickups or [], + expectation_rows=expectation_rows or [], ledgers=ledgers or [], task_documents=task_docs, series=series_nodes, @@ -543,6 +546,7 @@ def build_analytics( route_coverage: list[RouteCoverageNode], tool_reports: list[ToolReportNode], agent_pickups: list[AgentPickupNode] | None = None, + expectation_rows: list[ExpectationRowNode] | None = None, ledgers: list[LedgerNode], task_documents: list[TaskDocNode] | None = None, series: list[SeriesNode] | None = None, @@ -559,6 +563,7 @@ def build_analytics( routeCoverage=route_coverage, toolReports=tool_reports, agentPickups=agent_pickups or [], + expectationRows=expectation_rows or [], ledgers=ledgers, taskDocuments=task_documents or [], series=series or [], diff --git a/mcp/src/agents_remember/observer/served_store.py b/mcp/src/agents_remember/observer/served_store.py index 5ef029a2..a025fff2 100644 --- a/mcp/src/agents_remember/observer/served_store.py +++ b/mcp/src/agents_remember/observer/served_store.py @@ -33,7 +33,7 @@ from datetime import UTC, datetime from pathlib import Path -from pydantic import BaseModel, ConfigDict, Field +from pydantic import BaseModel, ConfigDict, Field, ValidationError SERVED_RECORD_SCHEMA = "ar-served-record/v1" @@ -92,15 +92,23 @@ def append(self, lifecycle_id: str, record: ServedRecord) -> None: handle.write(line + "\n") def read(self, lifecycle_id: str) -> list[ServedRecord]: - """Read a served log back as validated records (empty when absent).""" + """Read a served log back as validated records (empty when absent). + + 260707-HFX2-L12 F12: skip a torn/legacy line rather than raise — a served ledger read that + feeds ``read_ar_files`` dedup + the dashboard must degrade to "re-serve once" on one bad row, + not crash the read.""" path = self.log_path(lifecycle_id) if not path.exists(): return [] - return [ - ServedRecord.model_validate_json(line) - for line in path.read_text(encoding="utf-8").splitlines() - if line.strip() - ] + records: list[ServedRecord] = [] + for line in path.read_text(encoding="utf-8").splitlines(): + if not line.strip(): + continue + try: + records.append(ServedRecord.model_validate_json(line)) + except ValidationError: + continue + return records def served_set(self, lifecycle_id: str) -> set[str]: """Fold the log into the set of ``"::"`` keys served.""" diff --git a/mcp/src/agents_remember/observer/snapshots.py b/mcp/src/agents_remember/observer/snapshots.py index cc5c2103..6eac7371 100644 --- a/mcp/src/agents_remember/observer/snapshots.py +++ b/mcp/src/agents_remember/observer/snapshots.py @@ -16,7 +16,9 @@ from __future__ import annotations import contextlib +import hashlib import json +from dataclasses import dataclass from datetime import datetime from pathlib import Path from typing import Any @@ -25,6 +27,7 @@ AttentionDismissalRecord, AttentionDismissalStore, ) +from agents_remember.controlplane.expectation_rows import ExpectationRowStore from agents_remember.controlplane.interaction_retention import ( AGENT_PICKUP_TTL_SECONDS, pickup_age_seconds, @@ -50,12 +53,12 @@ DriftSnapshotNode, EnclosureNode, EngineProcessFacts, + ExpectationRowNode, LedgerNode, LedgerRefNode, ProviderNode, RouteCoverageNode, SeriesNode, - SeriesSectionNode, SeriesSubTaskNode, SetupProgressNode, SetupSummaryNode, @@ -108,6 +111,75 @@ WORKTREE_PROVIDER_STATE_SCHEMA = "ar-worktree-provider-state/v1" WORKTREE_PROVIDER_INSPECT_SECONDS = 5 +# 260707-HFX2-L12 F8: cadence for the (write-amplifying) physical gate-log prune. The projection +# folds the keep-filtered live set every tick regardless; only the on-disk reclamation is throttled +# to this interval so no whole-file rewrite rides the 1s hot path. Keyed by observer-logs root. +GATE_COMPACT_TTL_SECONDS = 30.0 +_last_gate_compact: dict[str, datetime] = {} + +# 260707-HFX2-L12 F10: the tasks tree is walked + every task JSON parsed by BOTH read_task_documents +# and read_series_documents each 1s tick (2x rglob + 2x parse of the whole, growing corpus). This TTL +# cache holds ONE parsed sweep of the task-document payloads, shared by both readers, so a tick pays +# for one walk+parse (and only every few seconds). Keyed by tasks-root. +TASK_DOC_CACHE_TTL_SECONDS = 3.0 +TASK_DOCUMENT_SUMMARY_LIMIT = 250 +SERIES_DOCUMENT_SUMMARY_LIMIT = 250 +_task_doc_cache: dict[str, tuple[datetime, list[tuple[Path, dict[str, object]]]]] = {} + +# 260707-HFX2-L12 F11: status_payload() shells out to git per leaf; without a cache the projection +# fires O(active-leaf) git subprocesses every tick. Git state changes slowly, so this TTL cache lets +# each leaf's git probe run at most once per interval; the cache is pruned to the live leaf set each +# tick so it cannot grow unbounded. Keyed by enclosure-contract path. +STATUS_PAYLOAD_TTL_SECONDS = 8.0 +_status_payload_cache: dict[str, tuple[datetime, dict[str, Any] | None]] = {} + + +@dataclass(frozen=True) +class _TaskDocumentLifecycleMaps: + lifecycle_by_enclosure: dict[str, str] + lifecycle_by_dir: dict[Path, str] + lifecycle_by_root_doc: dict[Path, str] + lifecycle_by_leaf_doc: dict[tuple[Path, str], str] + + +def _iter_task_document_payloads( + tasks_root: Path, *, now: datetime | None +) -> list[tuple[Path, dict[str, object]]]: + """Shared, TTL-cached ``(path, payload)`` list of task-document JSONs under ``tasks_root``. + + Removes the per-tick double walk+parse (F10): both task and series readers pull from this one + parsed sweep. ``now=None`` bypasses the cache (fresh walk) for non-projection callers/tests. + """ + if now is not None: + key = str(tasks_root) + cached = _task_doc_cache.get(key) + if cached is not None and 0 <= (now - cached[0]).total_seconds() < TASK_DOC_CACHE_TTL_SECONDS: + return cached[1] + docs: list[tuple[Path, dict[str, object]]] = [] + for path in _iter_task_json(tasks_root): + payload = _read_json(path) + if payload is None or payload.get("schema") != TASK_DOCUMENT_SCHEMA: + continue + docs.append((path, payload)) + if now is not None: + _task_doc_cache[str(tasks_root)] = (now, docs) + return docs + + +def _bounded_task_document_payloads( + docs: list[tuple[Path, dict[str, object]]], *, limit: int +) -> list[tuple[Path, dict[str, object]]]: + if len(docs) <= limit: + return docs + return sorted(docs, key=lambda item: (-_stat_mtime_ns(item[0]), item[0].as_posix()))[:limit] + + +def _stat_mtime_ns(path: Path) -> int: + try: + return path.stat().st_mtime_ns + except OSError: + return 0 + def read_providers( config: McpRuntimeConfig, @@ -442,23 +514,31 @@ def read_gates(coordination_root: Path, *, now: datetime | None = None) -> list[ Reads the gate logs co-located with the event store under ``observer_logs_root`` and folds each by id (last-wins), so the projection sees live gate state with no event machinery. A malformed log is skipped, never fatal to the tick. + + 260707-HFX2-L12 F8/CS-6 D2: one directory scan + one read per gate log per tick + (was ``lifecycle_ids()`` + a ``glob`` scan, and ``compact()``'s read + ``current()``'s + read per lifecycle). Physical prune is gated to ``GATE_COMPACT_TTL_SECONDS`` so no + whole-file rewrite rides the 1s hot path, while the projection stays keep-filtered + every tick. """ root = observer_logs_root(coordination_root) store = GateStore(root) + prune = False if now is not None: - for lifecycle_id in store.lifecycle_ids(): - with contextlib.suppress(OSError, ValueError): - store.compact(lifecycle_id, now=now) + key = str(root) + last = _last_gate_compact.get(key) + if last is None or (now - last).total_seconds() >= GATE_COMPACT_TTL_SECONDS: + prune = True + _last_gate_compact[key] = now gates: list[GateRecord] = [] - lifecycles_dir = root / "lifecycles" - if lifecycles_dir.is_dir(): - for log in sorted(lifecycles_dir.glob("*/gates.jsonl")): - try: - gates.extend(store.current(log.parent.name).values()) - except (OSError, ValueError): - continue - with contextlib.suppress(OSError, ValueError): - gates.extend(store.current(None).values()) + for lifecycle_id in store.lifecycle_ids(): + with contextlib.suppress(OSError, ValueError): + if now is None: + gates.extend(store.current(lifecycle_id).values()) + else: + gates.extend( + store.compact_current(lifecycle_id, now=now, rewrite=prune).values() + ) return gates @@ -494,11 +574,18 @@ def read_agent_pickups(coordination_root: Path, *, now: datetime) -> list[AgentP senderAgentId=entry.senderAgentId, senderRole=entry.senderRole, recipientRole=entry.recipientRole, + ownerRole=entry.ownerRole, + ownerAgentId=entry.ownerAgentId, + ownerLifecycleId=entry.ownerLifecycleId, gateId=entry.gateId, messageKind=entry.messageKind, artifactPath=entry.artifactPath, deliveryState=entry.deliveryState, deliveredToSession=entry.deliveredToSession, + attemptCount=entry.attemptCount, + lastAttemptAt=entry.lastAttemptAt, + nextAttemptAt=entry.nextAttemptAt, + escalatedAt=entry.escalatedAt, state=pickup_state(entry, now=now), ageSeconds=pickup_age_seconds(entry, now=now), ttlSeconds=AGENT_PICKUP_TTL_SECONDS, @@ -507,10 +594,43 @@ def read_agent_pickups(coordination_root: Path, *, now: datetime) -> list[AgentP return sorted(pickups, key=lambda item: item.ageSeconds or 0.0, reverse=True) +def read_expectation_rows(coordination_root: Path, *, now: datetime) -> list[ExpectationRowNode]: + """Pending expectation (deadline) rows, for dashboard/architect observability (R5). + + Surfacing only: an L2 predicate reads ``ExpectationRowStore`` directly and never this + projection (the #22 correctness half stays L2's rule; this is the visibility half). + """ + store = ExpectationRowStore(observer_logs_root(coordination_root)) + rows: list[ExpectationRowNode] = [] + with contextlib.suppress(OSError, ValueError): + for row in store.pending(): + try: + due_at = datetime.fromisoformat(row.dueAt) + overdue = now >= due_at + except ValueError: + overdue = False + rows.append( + ExpectationRowNode( + id=row.id, + kind=row.kind, + state=row.state, + sourceId=row.sourceId, + subjectAgentId=row.subjectAgentId, + subjectLifecycleId=row.subjectLifecycleId, + leafKey=row.leafKey, + dueAt=row.dueAt, + overdue=overdue, + note=row.note, + ) + ) + return sorted(rows, key=lambda item: item.dueAt) + + def read_engine_process_facts( coordination_root: Path, *, active_worktree_groups: set[str] | None = None, + now: datetime | None = None, ) -> list[EngineProcessFacts]: """Slice 5e: gather one fact bundle per leaf enclosure for the Engine Room map. @@ -524,6 +644,7 @@ def read_engine_process_facts( """ tasks_root = coordination_root / "tasks" facts: list[EngineProcessFacts] = [] + seen_status_keys: set[str] = set() for path in iter_leaf_enclosure_contracts(tasks_root): try: contract = load_contract(path) @@ -540,24 +661,43 @@ def read_engine_process_facts( code_root=cp.get("code_worktree"), memory_root=cp.get("memory_worktree"), ) + status_key = str(path) + seen_status_keys.add(status_key) facts.append( EngineProcessFacts( contract=cp, guidance=lifecycle_guidance(contract), - status=_safe_status_payload(contract), + status=_safe_status_payload(contract, cache_key=status_key, now=now), ledger_rows=ledger_rows, ledger_row_count=ledger_total, ) ) + # F11: keep the git-status cache bounded to the live leaf set (no unbounded growth over daemon life). + if now is not None: + for stale in [key for key in _status_payload_cache if key not in seen_status_keys]: + _status_payload_cache.pop(stale, None) return facts -def _safe_status_payload(contract: Any) -> dict[str, Any] | None: - """``status_payload`` is the only git-touching part; never let it crash the tick.""" +def _safe_status_payload( + contract: Any, *, cache_key: str | None = None, now: datetime | None = None +) -> dict[str, Any] | None: + """``status_payload`` is the only git-touching part; never let it crash the tick. + + 260707-HFX2-L12 F11: when ``cache_key``/``now`` are given, the git probe result is TTL-cached so + the projection does not fire one git subprocess per leaf every tick — git state changes slowly. + """ + if cache_key is not None and now is not None: + cached = _status_payload_cache.get(cache_key) + if cached is not None and 0 <= (now - cached[0]).total_seconds() < STATUS_PAYLOAD_TTL_SECONDS: + return cached[1] try: - return status_payload(contract) + value = status_payload(contract) except Exception: # a single worktree's git state must never fail the projection tick - return None + value = None + if cache_key is not None and now is not None: + _status_payload_cache[cache_key] = (now, value) + return value def _ledger_window( @@ -910,10 +1050,12 @@ def read_ledger(memory_root: Path, code_root: Path | None = None) -> LedgerNode def read_task_documents( coordination_root: Path, *, enclosures: list[EnclosureNode], now: datetime ) -> list[TaskDocNode]: - """Surface 7 (slices 3c + 6g): active task-document reader content. + """Surface 7 (slices 3c + 6g): active task-document summaries. Reads each ``ar-task-document/v1`` JSON under ``tasks///`` -- the - source of truth, never the rendered markdown. ``lifecycleId`` is optional + source of truth, never the rendered markdown. Full reader bodies are intentionally + omitted from this always-on projection and served by :func:`read_task_document_body`. + ``lifecycleId`` is optional runtime context: a ``light``/``subTask`` doc uses its own lifecycle id or the lifecycle of its matching leaf enclosure when present, but planning docs are still projected before an enclosure exists. Master docs are projected here as @@ -922,6 +1064,72 @@ def read_task_documents( tasks_root = coordination_root / "tasks" if not tasks_root.is_dir(): return [] + lifecycle_maps = _task_document_lifecycle_maps(enclosures) + nodes: list[TaskDocNode] = [] + for path, payload in _bounded_task_document_payloads( + _iter_task_document_payloads(tasks_root, now=now), + limit=TASK_DOCUMENT_SUMMARY_LIMIT, + ): + try: + doc = TaskDocument.model_validate(payload) + except ValueError: + continue + lifecycle_id = _task_doc_lifecycle_id(doc, path, lifecycle_maps) + nodes.append( + _task_doc_node( + doc, + lifecycle_id, + path, + lifecycle_maps.lifecycle_by_dir, + now, + include_body=False, + ) + ) + return nodes + + +def read_task_document_body( + coordination_root: Path, + *, + doc_path: str, + enclosures: list[EnclosureNode], + now: datetime, +) -> TaskDocNode | None: + """Read one full task-document body for the on-demand dashboard endpoint.""" + tasks_root = (coordination_root / "tasks").resolve() + path = Path(doc_path) + candidates = [path] if path.is_absolute() else [coordination_root / path, tasks_root / path] + resolved: Path | None = None + for candidate in candidates: + try: + candidate_resolved = candidate.resolve() + except OSError: + continue + if candidate_resolved.is_file() and candidate_resolved.is_relative_to(tasks_root): + resolved = candidate_resolved + break + if resolved is None: + return None + payload = _read_json(resolved) + if payload is None or payload.get("schema") != TASK_DOCUMENT_SCHEMA: + return None + try: + doc = TaskDocument.model_validate(payload) + except ValueError: + return None + lifecycle_maps = _task_document_lifecycle_maps(enclosures) + lifecycle_id = _task_doc_lifecycle_id(doc, resolved, lifecycle_maps) + return _task_doc_node( + doc, + lifecycle_id, + resolved, + lifecycle_maps.lifecycle_by_dir, + now, + include_body=True, + ) + + +def _task_document_lifecycle_maps(enclosures: list[EnclosureNode]) -> _TaskDocumentLifecycleMaps: lifecycle_by_enclosure = { enclosure.enclosure: enclosure.lifecycleId for enclosure in enclosures @@ -949,27 +1157,25 @@ def read_task_documents( for enclosure in enclosures if enclosure.lifecycleId and enclosure.taskRoot and enclosure.leafId } - nodes: list[TaskDocNode] = [] - for path in _iter_task_json(tasks_root): - payload = _read_json(path) - if payload is None or payload.get("schema") != TASK_DOCUMENT_SCHEMA: - continue - try: - doc = TaskDocument.model_validate(payload) - except ValueError: - continue - lifecycle_id: str | None = None - if doc.kind == "master": - lifecycle_id = lifecycle_by_root_doc.get(path.parent.resolve()) - else: - lifecycle_id = ( - doc.lifecycleId - or _doc_enclosure_lifecycle(doc, lifecycle_by_enclosure) - or lifecycle_by_leaf_doc.get((path.parent.resolve(), doc.id.lower())) - or lifecycle_by_leaf_doc.get((path.parent.resolve(), path.stem.lower())) - ) - nodes.append(_task_doc_node(doc, lifecycle_id, path, lifecycle_by_dir, now)) - return nodes + return _TaskDocumentLifecycleMaps( + lifecycle_by_enclosure=lifecycle_by_enclosure, + lifecycle_by_dir=lifecycle_by_dir, + lifecycle_by_root_doc=lifecycle_by_root_doc, + lifecycle_by_leaf_doc=lifecycle_by_leaf_doc, + ) + + +def _task_doc_lifecycle_id( + doc: TaskDocument, path: Path, maps: _TaskDocumentLifecycleMaps +) -> str | None: + if doc.kind == "master": + return maps.lifecycle_by_root_doc.get(path.parent.resolve()) + return ( + doc.lifecycleId + or _doc_enclosure_lifecycle(doc, maps.lifecycle_by_enclosure) + or maps.lifecycle_by_leaf_doc.get((path.parent.resolve(), doc.id.lower())) + or maps.lifecycle_by_leaf_doc.get((path.parent.resolve(), path.stem.lower())) + ) def _doc_enclosure_lifecycle( @@ -996,10 +1202,10 @@ def read_series_documents(coordination_root: Path, *, now: datetime) -> list[Ser if not tasks_root.is_dir(): return [] nodes: list[SeriesNode] = [] - for path in _iter_task_json(tasks_root): - payload = _read_json(path) - if payload is None or payload.get("schema") != TASK_DOCUMENT_SCHEMA: - continue + for path, payload in _bounded_task_document_payloads( + _iter_task_document_payloads(tasks_root, now=now), + limit=SERIES_DOCUMENT_SUMMARY_LIMIT, + ): if payload.get("kind") != "master": continue try: @@ -1013,18 +1219,12 @@ def read_series_documents(coordination_root: Path, *, now: datetime) -> list[Ser title=doc.title, status=doc.status, createdAt=doc.createdAt, - objective=doc.objective, + objective="", subTasks=_series_subtask_nodes(path, doc), doneCount=series_done(doc), totalCount=series_total(doc), - sections=[ - SeriesSectionNode(kind=section.kind, heading=section.heading, body=section.body) - for section in doc.sections - ], - decisions=[ - TaskDecisionNode(at=item.at, decision=item.decision, rationale=item.rationale) - for item in doc.decisions - ], + sections=[], + decisions=[], docPath=path.as_posix(), ageSeconds=_file_age_seconds(path, now), ) @@ -1092,6 +1292,8 @@ def _task_doc_node( path: Path, lifecycle_by_dir: dict[Path, str], now: datetime, + *, + include_body: bool, ) -> TaskDocNode: """Project one task document, carrying the resolved lifecycle id and (for a master) its index. @@ -1100,6 +1302,7 @@ def _task_doc_node( """ base_dir = path.parent parent_lifecycle = _ref_lifecycle(base_dir, doc.master, lifecycle_by_dir) + body_revision = _task_doc_body_revision(doc) return TaskDocNode( id=doc.id, lifecycleId=lifecycle_id, @@ -1111,6 +1314,7 @@ def _task_doc_node( stepsTotal=step_total(doc), currentStep=current_step(doc), docPath=path.as_posix(), + bodyRevision=body_revision, createdAt=doc.createdAt, ageSeconds=_file_age_seconds(path, now), steps=[ @@ -1125,9 +1329,9 @@ def _task_doc_node( ) for step in doc.steps ], - objective=doc.objective, - requirements=list(doc.requirements), - design=doc.design, + objective=doc.objective if include_body else "", + requirements=list(doc.requirements) if include_body else [], + design=doc.design if include_body else None, codeExamples=[ TaskCodeExampleNode( id=example.id, @@ -1138,13 +1342,17 @@ def _task_doc_node( snippet=example.snippet, ) for example in doc.codeExamples - ], + ] + if include_body + else [], decisions=[ TaskDecisionNode(at=item.at, decision=item.decision, rationale=item.rationale) for item in doc.decisions - ], - openQuestions=list(doc.openQuestions), - references=list(doc.references), + ] + if include_body + else [], + openQuestions=list(doc.openQuestions) if include_body else [], + references=list(doc.references) if include_body else [], subTasks=[ TaskSubTaskRefNode( number=ref.number, @@ -1159,12 +1367,29 @@ def _task_doc_node( sections=[ TaskSectionNode(kind=section.kind, heading=section.heading, body=section.body) for section in doc.sections - ], + ] + if include_body + else [], masterLifecycleId=parent_lifecycle, orchestrates=list(doc.orchestrates), ) +def _task_doc_body_revision(doc: TaskDocument) -> str: + payload = { + "objective": doc.objective, + "requirements": list(doc.requirements), + "design": doc.design, + "codeExamples": [example.model_dump(mode="json") for example in doc.codeExamples], + "decisions": [item.model_dump(mode="json") for item in doc.decisions], + "openQuestions": list(doc.openQuestions), + "references": list(doc.references), + "sections": [section.model_dump(mode="json") for section in doc.sections], + } + raw = json.dumps(payload, sort_keys=True, separators=(",", ":")) + return hashlib.sha256(raw.encode("utf-8")).hexdigest() + + # --- shared helpers ---------------------------------------------------------- diff --git a/mcp/src/agents_remember/observer/store.py b/mcp/src/agents_remember/observer/store.py index e15bed0c..54c65ff3 100644 --- a/mcp/src/agents_remember/observer/store.py +++ b/mcp/src/agents_remember/observer/store.py @@ -15,10 +15,93 @@ from __future__ import annotations +import fcntl +import json +import os +from collections.abc import Iterator +from contextlib import contextmanager, suppress +from datetime import UTC, datetime from pathlib import Path +from pydantic import ValidationError + from agents_remember.observer.events import Event +WORKSPACE_SOURCE = "workspace" +WORKSPACE_CURSOR_FILE = "events.cursor.json" +WORKSPACE_LOCK_FILE = "events.lock" +HEARTBEAT_KIND = "lifecycle.heartbeat" +HEARTBEAT_SIDECAR_FILE = "heartbeat.json" + + +@contextmanager +def workspace_river_lock(root: Path) -> Iterator[None]: + """Cross-process exclusive lock for workspace-river append and physical compaction. + + The workspace river has multiple process writers (serving + MCP tools), and live compaction + physically rewrites the file. A POSIX lock on a sibling lockfile is therefore the one defensive + mechanism this path needs: without it, a writer can append into the file while another process + is replacing it, which is exactly the F3 tear risk. + """ + path = root / WORKSPACE_SOURCE / WORKSPACE_LOCK_FILE + path.parent.mkdir(parents=True, exist_ok=True) + with path.open("a+b") as handle: + fcntl.flock(handle.fileno(), fcntl.LOCK_EX) + try: + yield + finally: + fcntl.flock(handle.fileno(), fcntl.LOCK_UN) + + +def workspace_base_offset(root: Path) -> int: + """Virtual byte offset already reclaimed from ``workspace/events.jsonl``.""" + path = root / WORKSPACE_SOURCE / WORKSPACE_CURSOR_FILE + try: + payload = json.loads(path.read_text(encoding="utf-8")) + except (OSError, json.JSONDecodeError, TypeError): + return 0 + value = payload.get("baseOffset") if isinstance(payload, dict) else None + return value if isinstance(value, int) and value >= 0 else 0 + + +def set_workspace_base_offset(root: Path, base_offset: int) -> None: + """Persist the workspace river's virtual base offset. + + Callers hold :func:`workspace_river_lock`; this helper only performs the atomic sidecar write. + """ + path = root / WORKSPACE_SOURCE / WORKSPACE_CURSOR_FILE + path.parent.mkdir(parents=True, exist_ok=True) + tmp = path.with_name(f".{path.name}.{os.getpid()}.tmp") + tmp.write_text(json.dumps({"baseOffset": base_offset}, separators=(",", ":")) + "\n", encoding="utf-8") + os.replace(tmp, path) + + +def workspace_logical_size(root: Path) -> int: + """Virtual EOF for the workspace river (base offset + current physical size).""" + path = root / WORKSPACE_SOURCE / "events.jsonl" + try: + size = path.stat().st_size + except OSError: + size = 0 + return workspace_base_offset(root) + size + + +def heartbeat_sidecar_path(root: Path, lifecycle_id: str) -> Path: + return root / "lifecycles" / lifecycle_id / HEARTBEAT_SIDECAR_FILE + + +def read_heartbeat_sidecar(root: Path, lifecycle_id: str) -> Event | None: + path = heartbeat_sidecar_path(root, lifecycle_id) + try: + text = path.read_text(encoding="utf-8") + except OSError: + return None + try: + event = Event.model_validate_json(text) + except ValidationError: + return None + return event if event.kind == HEARTBEAT_KIND else None + class EventStore: """Resolve per-lifecycle / workspace log paths and append events as JSONL.""" @@ -34,27 +117,87 @@ def log_path(self, lifecycle_id: str | None) -> Path: """The JSONL log a given event routes to (workspace when lifecycle-less).""" if lifecycle_id: return self._root / "lifecycles" / lifecycle_id / "events.jsonl" - return self._root / "workspace" / "events.jsonl" + return self._root / WORKSPACE_SOURCE / "events.jsonl" def append(self, event: Event) -> None: """Append one event to its log, creating parent dirs on first write.""" + if event.lifecycleId and event.kind == HEARTBEAT_KIND: + self._write_heartbeat_sidecar(event) + return path = self.log_path(event.lifecycleId) path.parent.mkdir(parents=True, exist_ok=True) line = event.model_dump_json(by_alias=True, exclude_none=True) + if event.lifecycleId is None: + with workspace_river_lock(self._root), path.open("a", encoding="utf-8") as handle: + handle.write(line + "\n") + return with path.open("a", encoding="utf-8") as handle: handle.write(line + "\n") def read(self, lifecycle_id: str | None) -> list[Event]: """Read a log back as validated events (empty when the log is absent). - The projection layer will read more richly; this is the minimal, - validated read that proves the write format round-trips. + Corrupt/legacy/version-skew lines are skipped rather than raised (CS-6 D3, + 260707-HFX2-L12): this read feeds the shared projection tick + (``project_and_write`` -> ``read_lifecycle_logs``), so one torn append line + must not freeze the whole projection fleet-wide -- it costs at most the one + unparseable row, mirroring ``ProviderMetricsStore.read_recent`` tolerance. """ + events = self.read_log(lifecycle_id) + if lifecycle_id is not None: + heartbeat = read_heartbeat_sidecar(self._root, lifecycle_id) + if heartbeat is not None and (not events or not _event_before(heartbeat, events[-1])): + events.append(heartbeat) + return events + + def read_log(self, lifecycle_id: str | None) -> list[Event]: + """Read only the JSONL file, excluding any coalesced heartbeat sidecar.""" path = self.log_path(lifecycle_id) if not path.exists(): return [] - return [ - Event.model_validate_json(line) - for line in path.read_text(encoding="utf-8").splitlines() - if line.strip() - ] + events: list[Event] = [] + for line in path.read_text(encoding="utf-8").splitlines(): + if not line.strip(): + continue + try: + events.append(Event.model_validate_json(line)) + except ValidationError: + continue + return events + + def read_heartbeat(self, lifecycle_id: str) -> Event | None: + """Read the coalesced heartbeat sidecar for one lifecycle.""" + return read_heartbeat_sidecar(self._root, lifecycle_id) + + def _write_heartbeat_sidecar(self, event: Event) -> None: + path = heartbeat_sidecar_path(self._root, event.lifecycleId or "") + path.parent.mkdir(parents=True, exist_ok=True) + tmp = path.with_name(f".{path.name}.{os.getpid()}.tmp") + try: + tmp.write_text( + event.model_dump_json(by_alias=True, exclude_none=True) + "\n", + encoding="utf-8", + ) + os.replace(tmp, path) + except BaseException: + with suppress(OSError): + tmp.unlink() + raise + + +def _event_before(left: Event, right: Event) -> bool: + left_ts = _event_time(left) + right_ts = _event_time(right) + if left_ts is None or right_ts is None: + return left.ts < right.ts + return left_ts < right_ts + + +def _event_time(event: Event) -> datetime | None: + try: + parsed = datetime.fromisoformat(event.ts.replace("Z", "+00:00")) + except ValueError: + return None + if parsed.tzinfo is None: + return parsed.replace(tzinfo=UTC) + return parsed.astimezone(UTC) diff --git a/mcp/src/agents_remember/package_data/dashboard.fingerprint b/mcp/src/agents_remember/package_data/dashboard.fingerprint index fc762ba9..a425c106 100644 --- a/mcp/src/agents_remember/package_data/dashboard.fingerprint +++ b/mcp/src/agents_remember/package_data/dashboard.fingerprint @@ -1 +1 @@ -9c7015119ad08d3419057365ad8281b8128d0782af309aa543e4755c05ea8146 +2fcf70ecf05eea64c6d6df6bccbe71a260879950c6f6510811dd5d6dc5e25814 diff --git a/mcp/src/agents_remember/package_data/dashboard/assets/Terminal-CyrgkVUD.js b/mcp/src/agents_remember/package_data/dashboard/assets/Terminal-D94bjuVv.js similarity index 99% rename from mcp/src/agents_remember/package_data/dashboard/assets/Terminal-CyrgkVUD.js rename to mcp/src/agents_remember/package_data/dashboard/assets/Terminal-D94bjuVv.js index 754f40d0..1a794dba 100644 --- a/mcp/src/agents_remember/package_data/dashboard/assets/Terminal-CyrgkVUD.js +++ b/mcp/src/agents_remember/package_data/dashboard/assets/Terminal-D94bjuVv.js @@ -1,9 +1,9 @@ -import{J as e,Q as t,X as n,Y as r,Z as i,et as a,q as o}from"./index-CLJ0aycO.js";var s=t(((e,t)=>{(function(n,r){typeof e==`object`&&typeof t==`object`?t.exports=r():typeof define==`function`&&define.amd?define([],r):typeof e==`object`?e.FitAddon=r():n.FitAddon=r()})(self,(()=>(()=>{var e={};return(()=>{var t=e;Object.defineProperty(t,"__esModule",{value:!0}),t.FitAddon=void 0,t.FitAddon=class{activate(e){this._terminal=e}dispose(){}fit(){let e=this.proposeDimensions();if(!e||!this._terminal||isNaN(e.cols)||isNaN(e.rows))return;let t=this._terminal._core;this._terminal.rows===e.rows&&this._terminal.cols===e.cols||(t._renderService.clear(),this._terminal.resize(e.cols,e.rows))}proposeDimensions(){if(!this._terminal||!this._terminal.element||!this._terminal.element.parentElement)return;let e=this._terminal._core,t=e._renderService.dimensions;if(t.css.cell.width===0||t.css.cell.height===0)return;let n=this._terminal.options.scrollback===0?0:e.viewport.scrollBarWidth,r=window.getComputedStyle(this._terminal.element.parentElement),i=parseInt(r.getPropertyValue(`height`)),a=Math.max(0,parseInt(r.getPropertyValue(`width`))),o=window.getComputedStyle(this._terminal.element),s=i-(parseInt(o.getPropertyValue(`padding-top`))+parseInt(o.getPropertyValue(`padding-bottom`))),c=a-(parseInt(o.getPropertyValue(`padding-right`))+parseInt(o.getPropertyValue(`padding-left`)))-n;return{cols:Math.max(2,Math.floor(c/t.css.cell.width)),rows:Math.max(1,Math.floor(s/t.css.cell.height))}}}})(),e})()))})),c=t(((e,t)=>{(function(n,r){if(typeof e==`object`&&typeof t==`object`)t.exports=r();else if(typeof define==`function`&&define.amd)define([],r);else{var i=r();for(var a in i)(typeof e==`object`?e:n)[a]=i[a]}})(globalThis,(()=>(()=>{var e={4567:function(e,t,n){var r=this&&this.__decorate||function(e,t,n,r){var i,a=arguments.length,o=a<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r;if(typeof Reflect==`object`&&typeof Reflect.decorate==`function`)o=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(i=e[s])&&(o=(a<3?i(o):a>3?i(t,n,o):i(t,n))||o);return a>3&&o&&Object.defineProperty(t,n,o),o},i=this&&this.__param||function(e,t){return function(n,r){t(n,r,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.AccessibilityManager=void 0;let a=n(9042),o=n(9924),s=n(844),c=n(4725),l=n(2585),u=n(3656),d=t.AccessibilityManager=class extends s.Disposable{constructor(e,t,n,r){super(),this._terminal=e,this._coreBrowserService=n,this._renderService=r,this._rowColumns=new WeakMap,this._liveRegionLineCount=0,this._charsToConsume=[],this._charsToAnnounce=``,this._accessibilityContainer=this._coreBrowserService.mainDocument.createElement(`div`),this._accessibilityContainer.classList.add(`xterm-accessibility`),this._rowContainer=this._coreBrowserService.mainDocument.createElement(`div`),this._rowContainer.setAttribute(`role`,`list`),this._rowContainer.classList.add(`xterm-accessibility-tree`),this._rowElements=[];for(let e=0;ethis._handleBoundaryFocus(e,0),this._bottomBoundaryFocusListener=e=>this._handleBoundaryFocus(e,1),this._rowElements[0].addEventListener(`focus`,this._topBoundaryFocusListener),this._rowElements[this._rowElements.length-1].addEventListener(`focus`,this._bottomBoundaryFocusListener),this._refreshRowsDimensions(),this._accessibilityContainer.appendChild(this._rowContainer),this._liveRegion=this._coreBrowserService.mainDocument.createElement(`div`),this._liveRegion.classList.add(`live-region`),this._liveRegion.setAttribute(`aria-live`,`assertive`),this._accessibilityContainer.appendChild(this._liveRegion),this._liveRegionDebouncer=this.register(new o.TimeBasedDebouncer(this._renderRows.bind(this))),!this._terminal.element)throw Error(`Cannot enable accessibility before Terminal.open`);this._terminal.element.insertAdjacentElement(`afterbegin`,this._accessibilityContainer),this.register(this._terminal.onResize((e=>this._handleResize(e.rows)))),this.register(this._terminal.onRender((e=>this._refreshRows(e.start,e.end)))),this.register(this._terminal.onScroll((()=>this._refreshRows()))),this.register(this._terminal.onA11yChar((e=>this._handleChar(e)))),this.register(this._terminal.onLineFeed((()=>this._handleChar(` +import{J as e,Q as t,X as n,Y as r,Z as i,et as a,q as o}from"./index-DPVcGf1z.js";var s=t(((e,t)=>{(function(n,r){typeof e==`object`&&typeof t==`object`?t.exports=r():typeof define==`function`&&define.amd?define([],r):typeof e==`object`?e.FitAddon=r():n.FitAddon=r()})(self,(()=>(()=>{var e={};return(()=>{var t=e;Object.defineProperty(t,"__esModule",{value:!0}),t.FitAddon=void 0,t.FitAddon=class{activate(e){this._terminal=e}dispose(){}fit(){let e=this.proposeDimensions();if(!e||!this._terminal||isNaN(e.cols)||isNaN(e.rows))return;let t=this._terminal._core;this._terminal.rows===e.rows&&this._terminal.cols===e.cols||(t._renderService.clear(),this._terminal.resize(e.cols,e.rows))}proposeDimensions(){if(!this._terminal||!this._terminal.element||!this._terminal.element.parentElement)return;let e=this._terminal._core,t=e._renderService.dimensions;if(t.css.cell.width===0||t.css.cell.height===0)return;let n=this._terminal.options.scrollback===0?0:e.viewport.scrollBarWidth,r=window.getComputedStyle(this._terminal.element.parentElement),i=parseInt(r.getPropertyValue(`height`)),a=Math.max(0,parseInt(r.getPropertyValue(`width`))),o=window.getComputedStyle(this._terminal.element),s=i-(parseInt(o.getPropertyValue(`padding-top`))+parseInt(o.getPropertyValue(`padding-bottom`))),c=a-(parseInt(o.getPropertyValue(`padding-right`))+parseInt(o.getPropertyValue(`padding-left`)))-n;return{cols:Math.max(2,Math.floor(c/t.css.cell.width)),rows:Math.max(1,Math.floor(s/t.css.cell.height))}}}})(),e})()))})),c=t(((e,t)=>{(function(n,r){if(typeof e==`object`&&typeof t==`object`)t.exports=r();else if(typeof define==`function`&&define.amd)define([],r);else{var i=r();for(var a in i)(typeof e==`object`?e:n)[a]=i[a]}})(globalThis,(()=>(()=>{var e={4567:function(e,t,n){var r=this&&this.__decorate||function(e,t,n,r){var i,a=arguments.length,o=a<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r;if(typeof Reflect==`object`&&typeof Reflect.decorate==`function`)o=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(i=e[s])&&(o=(a<3?i(o):a>3?i(t,n,o):i(t,n))||o);return a>3&&o&&Object.defineProperty(t,n,o),o},i=this&&this.__param||function(e,t){return function(n,r){t(n,r,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.AccessibilityManager=void 0;let a=n(9042),o=n(9924),s=n(844),c=n(4725),l=n(2585),u=n(3656),d=t.AccessibilityManager=class extends s.Disposable{constructor(e,t,n,r){super(),this._terminal=e,this._coreBrowserService=n,this._renderService=r,this._rowColumns=new WeakMap,this._liveRegionLineCount=0,this._charsToConsume=[],this._charsToAnnounce=``,this._accessibilityContainer=this._coreBrowserService.mainDocument.createElement(`div`),this._accessibilityContainer.classList.add(`xterm-accessibility`),this._rowContainer=this._coreBrowserService.mainDocument.createElement(`div`),this._rowContainer.setAttribute(`role`,`list`),this._rowContainer.classList.add(`xterm-accessibility-tree`),this._rowElements=[];for(let e=0;ethis._handleBoundaryFocus(e,0),this._bottomBoundaryFocusListener=e=>this._handleBoundaryFocus(e,1),this._rowElements[0].addEventListener(`focus`,this._topBoundaryFocusListener),this._rowElements[this._rowElements.length-1].addEventListener(`focus`,this._bottomBoundaryFocusListener),this._refreshRowsDimensions(),this._accessibilityContainer.appendChild(this._rowContainer),this._liveRegion=this._coreBrowserService.mainDocument.createElement(`div`),this._liveRegion.classList.add(`live-region`),this._liveRegion.setAttribute(`aria-live`,`assertive`),this._accessibilityContainer.appendChild(this._liveRegion),this._liveRegionDebouncer=this.register(new o.TimeBasedDebouncer(this._renderRows.bind(this))),!this._terminal.element)throw Error(`Cannot enable accessibility before Terminal.open`);this._terminal.element.insertAdjacentElement(`afterbegin`,this._accessibilityContainer),this.register(this._terminal.onResize((e=>this._handleResize(e.rows)))),this.register(this._terminal.onRender((e=>this._refreshRows(e.start,e.end)))),this.register(this._terminal.onScroll((()=>this._refreshRows()))),this.register(this._terminal.onA11yChar((e=>this._handleChar(e)))),this.register(this._terminal.onLineFeed((()=>this._handleChar(` `)))),this.register(this._terminal.onA11yTab((e=>this._handleTab(e)))),this.register(this._terminal.onKey((e=>this._handleKey(e.key)))),this.register(this._terminal.onBlur((()=>this._clearLiveRegion()))),this.register(this._renderService.onDimensionsChange((()=>this._refreshRowsDimensions()))),this.register((0,u.addDisposableDomListener)(document,`selectionchange`,(()=>this._handleSelectionChange()))),this.register(this._coreBrowserService.onDprChange((()=>this._refreshRowsDimensions()))),this._refreshRows(),this.register((0,s.toDisposable)((()=>{this._accessibilityContainer.remove(),this._rowElements.length=0})))}_handleTab(e){for(let t=0;t0?this._charsToConsume.shift()!==e&&(this._charsToAnnounce+=e):this._charsToAnnounce+=e,e===` `&&(this._liveRegionLineCount++,this._liveRegionLineCount===21&&(this._liveRegion.textContent+=a.tooMuchOutput)))}_clearLiveRegion(){this._liveRegion.textContent=``,this._liveRegionLineCount=0}_handleKey(e){this._clearLiveRegion(),/\p{Control}/u.test(e)||this._charsToConsume.push(e)}_refreshRows(e,t){this._liveRegionDebouncer.refresh(e,t,this._terminal.rows)}_renderRows(e,t){let n=this._terminal.buffer,r=n.lines.length.toString();for(let i=e;i<=t;i++){let e=n.lines.get(n.ydisp+i),t=[],a=e?.translateToString(!0,void 0,void 0,t)||``,o=(n.ydisp+i+1).toString(),s=this._rowElements[i];s&&(a.length===0?(s.innerText=`\xA0`,this._rowColumns.set(s,[0,1])):(s.textContent=a,this._rowColumns.set(s,t)),s.setAttribute(`aria-posinset`,o),s.setAttribute(`aria-setsize`,r))}this._announceCharacters()}_announceCharacters(){this._charsToAnnounce.length!==0&&(this._liveRegion.textContent+=this._charsToAnnounce,this._charsToAnnounce=``)}_handleBoundaryFocus(e,t){let n=e.target,r=this._rowElements[t===0?1:this._rowElements.length-2];if(n.getAttribute(`aria-posinset`)===(t===0?`1`:`${this._terminal.buffer.lines.length}`)||e.relatedTarget!==r)return;let i,a;if(t===0?(i=n,a=this._rowElements.pop(),this._rowContainer.removeChild(a)):(i=this._rowElements.shift(),a=n,this._rowContainer.removeChild(i)),i.removeEventListener(`focus`,this._topBoundaryFocusListener),a.removeEventListener(`focus`,this._bottomBoundaryFocusListener),t===0){let e=this._createAccessibilityTreeNode();this._rowElements.unshift(e),this._rowContainer.insertAdjacentElement(`afterbegin`,e)}else{let e=this._createAccessibilityTreeNode();this._rowElements.push(e),this._rowContainer.appendChild(e)}this._rowElements[0].addEventListener(`focus`,this._topBoundaryFocusListener),this._rowElements[this._rowElements.length-1].addEventListener(`focus`,this._bottomBoundaryFocusListener),this._terminal.scrollLines(t===0?-1:1),this._rowElements[t===0?1:this._rowElements.length-2].focus(),e.preventDefault(),e.stopImmediatePropagation()}_handleSelectionChange(){if(this._rowElements.length===0)return;let e=document.getSelection();if(!e)return;if(e.isCollapsed)return void(this._rowContainer.contains(e.anchorNode)&&this._terminal.clearSelection());if(!e.anchorNode||!e.focusNode)return void console.error(`anchorNode and/or focusNode are null`);let t={node:e.anchorNode,offset:e.anchorOffset},n={node:e.focusNode,offset:e.focusOffset};if((t.node.compareDocumentPosition(n.node)&Node.DOCUMENT_POSITION_PRECEDING||t.node===n.node&&t.offset>n.offset)&&([t,n]=[n,t]),t.node.compareDocumentPosition(this._rowElements[0])&(Node.DOCUMENT_POSITION_CONTAINED_BY|Node.DOCUMENT_POSITION_FOLLOWING)&&(t={node:this._rowElements[0].childNodes[0],offset:0}),!this._rowContainer.contains(t.node))return;let r=this._rowElements.slice(-1)[0];if(n.node.compareDocumentPosition(r)&(Node.DOCUMENT_POSITION_CONTAINED_BY|Node.DOCUMENT_POSITION_PRECEDING)&&(n={node:r,offset:r.textContent?.length??0}),!this._rowContainer.contains(n.node))return;let i=({node:e,offset:t})=>{let n=e instanceof Text?e.parentNode:e,r=parseInt(n?.getAttribute(`aria-posinset`),10)-1;if(isNaN(r))return console.warn(`row is invalid. Race condition?`),null;let i=this._rowColumns.get(n);if(!i)return console.warn(`columns is null. Race condition?`),null;let a=t=this._terminal.cols&&(++r,a=0),{row:r,column:a}},a=i(t),o=i(n);if(a&&o){if(a.row>o.row||a.row===o.row&&a.column>=o.column)throw Error(`invalid range`);this._terminal.select(a.column,a.row,(o.row-a.row)*this._terminal.cols-a.column+o.column)}}_handleResize(e){this._rowElements[this._rowElements.length-1].removeEventListener(`focus`,this._bottomBoundaryFocusListener);for(let e=this._rowContainer.children.length;ee;)this._rowContainer.removeChild(this._rowElements.pop());this._rowElements[this._rowElements.length-1].addEventListener(`focus`,this._bottomBoundaryFocusListener),this._refreshRowsDimensions()}_createAccessibilityTreeNode(){let e=this._coreBrowserService.mainDocument.createElement(`div`);return e.setAttribute(`role`,`listitem`),e.tabIndex=-1,this._refreshRowDimensions(e),e}_refreshRowsDimensions(){if(this._renderService.dimensions.css.cell.height){this._accessibilityContainer.style.width=`${this._renderService.dimensions.css.canvas.width}px`,this._rowElements.length!==this._terminal.rows&&this._handleResize(this._terminal.rows);for(let e=0;e{function n(e){return e.replace(/\r?\n/g,`\r`)}function r(e,t){return t?`\x1B[200~`+e+`\x1B[201~`:e}function i(e,t,i,a){e=r(e=n(e),i.decPrivateModes.bracketedPasteMode&&!0!==a.rawOptions.ignoreBracketedPasteMode),i.triggerDataEvent(e,!0),t.value=``}function a(e,t,n){let r=n.getBoundingClientRect(),i=e.clientX-r.left-10,a=e.clientY-r.top-10;t.style.width=`20px`,t.style.height=`20px`,t.style.left=`${i}px`,t.style.top=`${a}px`,t.style.zIndex=`1000`,t.focus()}Object.defineProperty(t,"__esModule",{value:!0}),t.rightClickHandler=t.moveTextAreaUnderMouseCursor=t.paste=t.handlePasteEvent=t.copyHandler=t.bracketTextForPaste=t.prepareTextForTerminal=void 0,t.prepareTextForTerminal=n,t.bracketTextForPaste=r,t.copyHandler=function(e,t){e.clipboardData&&e.clipboardData.setData(`text/plain`,t.selectionText),e.preventDefault()},t.handlePasteEvent=function(e,t,n,r){e.stopPropagation(),e.clipboardData&&i(e.clipboardData.getData(`text/plain`),t,n,r)},t.paste=i,t.moveTextAreaUnderMouseCursor=a,t.rightClickHandler=function(e,t,n,r,i){a(e,t,n),i&&r.rightClickSelect(e),t.value=r.selectionText,t.select()}},7239:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ColorContrastCache=void 0;let r=n(1505);t.ColorContrastCache=class{constructor(){this._color=new r.TwoKeyMap,this._css=new r.TwoKeyMap}setCss(e,t,n){this._css.set(e,t,n)}getCss(e,t){return this._css.get(e,t)}setColor(e,t,n){this._color.set(e,t,n)}getColor(e,t){return this._color.get(e,t)}clear(){this._color.clear(),this._css.clear()}}},3656:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.addDisposableDomListener=void 0,t.addDisposableDomListener=function(e,t,n,r){e.addEventListener(t,n,r);let i=!1;return{dispose:()=>{i||(i=!0,e.removeEventListener(t,n,r))}}}},3551:function(e,t,n){var r=this&&this.__decorate||function(e,t,n,r){var i,a=arguments.length,o=a<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r;if(typeof Reflect==`object`&&typeof Reflect.decorate==`function`)o=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(i=e[s])&&(o=(a<3?i(o):a>3?i(t,n,o):i(t,n))||o);return a>3&&o&&Object.defineProperty(t,n,o),o},i=this&&this.__param||function(e,t){return function(n,r){t(n,r,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.Linkifier=void 0;let a=n(3656),o=n(8460),s=n(844),c=n(2585),l=n(4725),u=t.Linkifier=class extends s.Disposable{get currentLink(){return this._currentLink}constructor(e,t,n,r,i){super(),this._element=e,this._mouseService=t,this._renderService=n,this._bufferService=r,this._linkProviderService=i,this._linkCacheDisposables=[],this._isMouseOut=!0,this._wasResized=!1,this._activeLine=-1,this._onShowLinkUnderline=this.register(new o.EventEmitter),this.onShowLinkUnderline=this._onShowLinkUnderline.event,this._onHideLinkUnderline=this.register(new o.EventEmitter),this.onHideLinkUnderline=this._onHideLinkUnderline.event,this.register((0,s.getDisposeArrayDisposable)(this._linkCacheDisposables)),this.register((0,s.toDisposable)((()=>{this._lastMouseEvent=void 0,this._activeProviderReplies?.clear()}))),this.register(this._bufferService.onResize((()=>{this._clearCurrentLink(),this._wasResized=!0}))),this.register((0,a.addDisposableDomListener)(this._element,`mouseleave`,(()=>{this._isMouseOut=!0,this._clearCurrentLink()}))),this.register((0,a.addDisposableDomListener)(this._element,`mousemove`,this._handleMouseMove.bind(this))),this.register((0,a.addDisposableDomListener)(this._element,`mousedown`,this._handleMouseDown.bind(this))),this.register((0,a.addDisposableDomListener)(this._element,`mouseup`,this._handleMouseUp.bind(this)))}_handleMouseMove(e){this._lastMouseEvent=e;let t=this._positionFromMouseEvent(e,this._element,this._mouseService);if(!t)return;this._isMouseOut=!1;let n=e.composedPath();for(let e=0;e{e?.forEach((e=>{e.link.dispose&&e.link.dispose()}))})),this._activeProviderReplies=new Map,this._activeLine=e.y);let n=!1;for(let[r,i]of this._linkProviderService.linkProviders.entries())t?this._activeProviderReplies?.get(r)&&(n=this._checkLinkProviderResult(r,e,n)):i.provideLinks(e.y,(t=>{if(this._isMouseOut)return;let i=t?.map((e=>({link:e})));this._activeProviderReplies?.set(r,i),n=this._checkLinkProviderResult(r,e,n),this._activeProviderReplies?.size===this._linkProviderService.linkProviders.length&&this._removeIntersectingLinks(e.y,this._activeProviderReplies)}))}_removeIntersectingLinks(e,t){let n=new Set;for(let r=0;re?this._bufferService.cols:r.link.range.end.x;for(let e=a;e<=o;e++){if(n.has(e)){i.splice(t--,1);break}n.add(e)}}}}_checkLinkProviderResult(e,t,n){if(!this._activeProviderReplies)return n;let r=this._activeProviderReplies.get(e),i=!1;for(let t=0;tthis._linkAtPosition(e.link,t)));e&&(n=!0,this._handleNewLink(e))}if(this._activeProviderReplies.size===this._linkProviderService.linkProviders.length&&!n)for(let e=0;ethis._linkAtPosition(e.link,t)));if(r){n=!0,this._handleNewLink(r);break}}return n}_handleMouseDown(){this._mouseDownLink=this._currentLink}_handleMouseUp(e){if(!this._currentLink)return;let t=this._positionFromMouseEvent(e,this._element,this._mouseService);t&&this._mouseDownLink===this._currentLink&&this._linkAtPosition(this._currentLink.link,t)&&this._currentLink.link.activate(e,this._currentLink.link.text)}_clearCurrentLink(e,t){this._currentLink&&this._lastMouseEvent&&(!e||!t||this._currentLink.link.range.start.y>=e&&this._currentLink.link.range.end.y<=t)&&(this._linkLeave(this._element,this._currentLink.link,this._lastMouseEvent),this._currentLink=void 0,(0,s.disposeArray)(this._linkCacheDisposables))}_handleNewLink(e){if(!this._lastMouseEvent)return;let t=this._positionFromMouseEvent(this._lastMouseEvent,this._element,this._mouseService);t&&this._linkAtPosition(e.link,t)&&(this._currentLink=e,this._currentLink.state={decorations:{underline:e.link.decorations===void 0||e.link.decorations.underline,pointerCursor:e.link.decorations===void 0||e.link.decorations.pointerCursor},isHovered:!0},this._linkHover(this._element,e.link,this._lastMouseEvent),e.link.decorations={},Object.defineProperties(e.link.decorations,{pointerCursor:{get:()=>this._currentLink?.state?.decorations.pointerCursor,set:e=>{this._currentLink?.state&&this._currentLink.state.decorations.pointerCursor!==e&&(this._currentLink.state.decorations.pointerCursor=e,this._currentLink.state.isHovered&&this._element.classList.toggle(`xterm-cursor-pointer`,e))}},underline:{get:()=>this._currentLink?.state?.decorations.underline,set:t=>{this._currentLink?.state&&this._currentLink?.state?.decorations.underline!==t&&(this._currentLink.state.decorations.underline=t,this._currentLink.state.isHovered&&this._fireUnderlineEvent(e.link,t))}}}),this._linkCacheDisposables.push(this._renderService.onRenderedViewportChange((e=>{if(!this._currentLink)return;let t=e.start===0?0:e.start+1+this._bufferService.buffer.ydisp,n=this._bufferService.buffer.ydisp+1+e.end;if(this._currentLink.link.range.start.y>=t&&this._currentLink.link.range.end.y<=n&&(this._clearCurrentLink(t,n),this._lastMouseEvent)){let e=this._positionFromMouseEvent(this._lastMouseEvent,this._element,this._mouseService);e&&this._askForLink(e,!1)}}))))}_linkHover(e,t,n){this._currentLink?.state&&(this._currentLink.state.isHovered=!0,this._currentLink.state.decorations.underline&&this._fireUnderlineEvent(t,!0),this._currentLink.state.decorations.pointerCursor&&e.classList.add(`xterm-cursor-pointer`)),t.hover&&t.hover(n,t.text)}_fireUnderlineEvent(e,t){let n=e.range,r=this._bufferService.buffer.ydisp,i=this._createLinkUnderlineEvent(n.start.x-1,n.start.y-r-1,n.end.x,n.end.y-r-1,void 0);(t?this._onShowLinkUnderline:this._onHideLinkUnderline).fire(i)}_linkLeave(e,t,n){this._currentLink?.state&&(this._currentLink.state.isHovered=!1,this._currentLink.state.decorations.underline&&this._fireUnderlineEvent(t,!1),this._currentLink.state.decorations.pointerCursor&&e.classList.remove(`xterm-cursor-pointer`)),t.leave&&t.leave(n,t.text)}_linkAtPosition(e,t){let n=e.range.start.y*this._bufferService.cols+e.range.start.x,r=e.range.end.y*this._bufferService.cols+e.range.end.x,i=t.y*this._bufferService.cols+t.x;return n<=i&&i<=r}_positionFromMouseEvent(e,t,n){let r=n.getCoords(e,t,this._bufferService.cols,this._bufferService.rows);if(r)return{x:r[0],y:r[1]+this._bufferService.buffer.ydisp}}_createLinkUnderlineEvent(e,t,n,r,i){return{x1:e,y1:t,x2:n,y2:r,cols:this._bufferService.cols,fg:i}}};t.Linkifier=u=r([i(1,l.IMouseService),i(2,l.IRenderService),i(3,c.IBufferService),i(4,l.ILinkProviderService)],u)},9042:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.tooMuchOutput=t.promptLabel=void 0,t.promptLabel=`Terminal input`,t.tooMuchOutput=`Too much output to announce, navigate to rows manually to read`},3730:function(e,t,n){var r=this&&this.__decorate||function(e,t,n,r){var i,a=arguments.length,o=a<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r;if(typeof Reflect==`object`&&typeof Reflect.decorate==`function`)o=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(i=e[s])&&(o=(a<3?i(o):a>3?i(t,n,o):i(t,n))||o);return a>3&&o&&Object.defineProperty(t,n,o),o},i=this&&this.__param||function(e,t){return function(n,r){t(n,r,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.OscLinkProvider=void 0;let a=n(511),o=n(2585),s=t.OscLinkProvider=class{constructor(e,t,n){this._bufferService=e,this._optionsService=t,this._oscLinkService=n}provideLinks(e,t){let n=this._bufferService.buffer.lines.get(e-1);if(!n)return void t(void 0);let r=[],i=this._optionsService.rawOptions.linkHandler,o=new a.CellData,s=n.getTrimmedLength(),l=-1,u=-1,d=!1;for(let t=0;ti?i.activate(e,t,a):c(0,t),hover:(e,t)=>i?.hover?.(e,t,a),leave:(e,t)=>i?.leave?.(e,t,a)})}d=!1,o.hasExtendedAttrs()&&o.extended.urlId?(u=t,l=o.extended.urlId):(u=-1,l=-1)}}t(r)}};function c(e,t){if(confirm(`Do you want to navigate to ${t}?\n\nWARNING: This link could potentially be dangerous`)){let e=window.open();if(e){try{e.opener=null}catch{}e.location.href=t}else console.warn(`Opening link blocked as opener could not be cleared`)}}t.OscLinkProvider=s=r([i(0,o.IBufferService),i(1,o.IOptionsService),i(2,o.IOscLinkService)],s)},6193:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.RenderDebouncer=void 0,t.RenderDebouncer=class{constructor(e,t){this._renderCallback=e,this._coreBrowserService=t,this._refreshCallbacks=[]}dispose(){this._animationFrame&&=(this._coreBrowserService.window.cancelAnimationFrame(this._animationFrame),void 0)}addRefreshCallback(e){return this._refreshCallbacks.push(e),this._animationFrame||=this._coreBrowserService.window.requestAnimationFrame((()=>this._innerRefresh())),this._animationFrame}refresh(e,t,n){this._rowCount=n,e=e===void 0?0:e,t=t===void 0?this._rowCount-1:t,this._rowStart=this._rowStart===void 0?e:Math.min(this._rowStart,e),this._rowEnd=this._rowEnd===void 0?t:Math.max(this._rowEnd,t),this._animationFrame||=this._coreBrowserService.window.requestAnimationFrame((()=>this._innerRefresh()))}_innerRefresh(){if(this._animationFrame=void 0,this._rowStart===void 0||this._rowEnd===void 0||this._rowCount===void 0)return void this._runRefreshCallbacks();let e=Math.max(this._rowStart,0),t=Math.min(this._rowEnd,this._rowCount-1);this._rowStart=void 0,this._rowEnd=void 0,this._renderCallback(e,t),this._runRefreshCallbacks()}_runRefreshCallbacks(){for(let e of this._refreshCallbacks)e(0);this._refreshCallbacks=[]}}},3236:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Terminal=void 0;let r=n(3614),i=n(3656),a=n(3551),o=n(9042),s=n(3730),c=n(1680),l=n(3107),u=n(5744),d=n(2950),f=n(1296),p=n(428),m=n(4269),h=n(5114),g=n(8934),_=n(3230),v=n(9312),y=n(4725),b=n(6731),x=n(8055),S=n(8969),C=n(8460),w=n(844),T=n(6114),E=n(8437),D=n(2584),O=n(7399),k=n(5941),A=n(9074),j=n(2585),M=n(5435),N=n(4567),P=n(779);class F extends S.CoreTerminal{get onFocus(){return this._onFocus.event}get onBlur(){return this._onBlur.event}get onA11yChar(){return this._onA11yCharEmitter.event}get onA11yTab(){return this._onA11yTabEmitter.event}get onWillOpen(){return this._onWillOpen.event}constructor(e={}){super(e),this.browser=T,this._keyDownHandled=!1,this._keyDownSeen=!1,this._keyPressHandled=!1,this._unprocessedDeadKey=!1,this._accessibilityManager=this.register(new w.MutableDisposable),this._onCursorMove=this.register(new C.EventEmitter),this.onCursorMove=this._onCursorMove.event,this._onKey=this.register(new C.EventEmitter),this.onKey=this._onKey.event,this._onRender=this.register(new C.EventEmitter),this.onRender=this._onRender.event,this._onSelectionChange=this.register(new C.EventEmitter),this.onSelectionChange=this._onSelectionChange.event,this._onTitleChange=this.register(new C.EventEmitter),this.onTitleChange=this._onTitleChange.event,this._onBell=this.register(new C.EventEmitter),this.onBell=this._onBell.event,this._onFocus=this.register(new C.EventEmitter),this._onBlur=this.register(new C.EventEmitter),this._onA11yCharEmitter=this.register(new C.EventEmitter),this._onA11yTabEmitter=this.register(new C.EventEmitter),this._onWillOpen=this.register(new C.EventEmitter),this._setup(),this._decorationService=this._instantiationService.createInstance(A.DecorationService),this._instantiationService.setService(j.IDecorationService,this._decorationService),this._linkProviderService=this._instantiationService.createInstance(P.LinkProviderService),this._instantiationService.setService(y.ILinkProviderService,this._linkProviderService),this._linkProviderService.registerLinkProvider(this._instantiationService.createInstance(s.OscLinkProvider)),this.register(this._inputHandler.onRequestBell((()=>this._onBell.fire()))),this.register(this._inputHandler.onRequestRefreshRows(((e,t)=>this.refresh(e,t)))),this.register(this._inputHandler.onRequestSendFocus((()=>this._reportFocus()))),this.register(this._inputHandler.onRequestReset((()=>this.reset()))),this.register(this._inputHandler.onRequestWindowsOptionsReport((e=>this._reportWindowsOptions(e)))),this.register(this._inputHandler.onColor((e=>this._handleColorEvent(e)))),this.register((0,C.forwardEvent)(this._inputHandler.onCursorMove,this._onCursorMove)),this.register((0,C.forwardEvent)(this._inputHandler.onTitleChange,this._onTitleChange)),this.register((0,C.forwardEvent)(this._inputHandler.onA11yChar,this._onA11yCharEmitter)),this.register((0,C.forwardEvent)(this._inputHandler.onA11yTab,this._onA11yTabEmitter)),this.register(this._bufferService.onResize((e=>this._afterResize(e.cols,e.rows)))),this.register((0,w.toDisposable)((()=>{this._customKeyEventHandler=void 0,this.element?.parentNode?.removeChild(this.element)})))}_handleColorEvent(e){if(this._themeService)for(let t of e){let e,n=``;switch(t.index){case 256:e=`foreground`,n=`10`;break;case 257:e=`background`,n=`11`;break;case 258:e=`cursor`,n=`12`;break;default:e=`ansi`,n=`4;`+t.index}switch(t.type){case 0:let r=x.color.toColorRGB(e===`ansi`?this._themeService.colors.ansi[t.index]:this._themeService.colors[e]);this.coreService.triggerDataEvent(`${D.C0.ESC}]${n};${(0,k.toRgbString)(r)}${D.C1_ESCAPED.ST}`);break;case 1:if(e===`ansi`)this._themeService.modifyColors((e=>e.ansi[t.index]=x.channels.toColor(...t.color)));else{let n=e;this._themeService.modifyColors((e=>e[n]=x.channels.toColor(...t.color)))}break;case 2:this._themeService.restoreColor(t.index)}}}_setup(){super._setup(),this._customKeyEventHandler=void 0}get buffer(){return this.buffers.active}focus(){this.textarea&&this.textarea.focus({preventScroll:!0})}_handleScreenReaderModeOptionChange(e){e?!this._accessibilityManager.value&&this._renderService&&(this._accessibilityManager.value=this._instantiationService.createInstance(N.AccessibilityManager,this)):this._accessibilityManager.clear()}_handleTextAreaFocus(e){this.coreService.decPrivateModes.sendFocus&&this.coreService.triggerDataEvent(D.C0.ESC+`[I`),this.element.classList.add(`focus`),this._showCursor(),this._onFocus.fire()}blur(){return this.textarea?.blur()}_handleTextAreaBlur(){this.textarea.value=``,this.refresh(this.buffer.y,this.buffer.y),this.coreService.decPrivateModes.sendFocus&&this.coreService.triggerDataEvent(D.C0.ESC+`[O`),this.element.classList.remove(`focus`),this._onBlur.fire()}_syncTextArea(){if(!this.textarea||!this.buffer.isCursorInViewport||this._compositionHelper.isComposing||!this._renderService)return;let e=this.buffer.ybase+this.buffer.y,t=this.buffer.lines.get(e);if(!t)return;let n=Math.min(this.buffer.x,this.cols-1),r=this._renderService.dimensions.css.cell.height,i=t.getWidth(n),a=this._renderService.dimensions.css.cell.width*i,o=this.buffer.y*this._renderService.dimensions.css.cell.height,s=n*this._renderService.dimensions.css.cell.width;this.textarea.style.left=s+`px`,this.textarea.style.top=o+`px`,this.textarea.style.width=a+`px`,this.textarea.style.height=r+`px`,this.textarea.style.lineHeight=r+`px`,this.textarea.style.zIndex=`-5`}_initGlobal(){this._bindKeys(),this.register((0,i.addDisposableDomListener)(this.element,`copy`,(e=>{this.hasSelection()&&(0,r.copyHandler)(e,this._selectionService)})));let e=e=>(0,r.handlePasteEvent)(e,this.textarea,this.coreService,this.optionsService);this.register((0,i.addDisposableDomListener)(this.textarea,`paste`,e)),this.register((0,i.addDisposableDomListener)(this.element,`paste`,e)),T.isFirefox?this.register((0,i.addDisposableDomListener)(this.element,`mousedown`,(e=>{e.button===2&&(0,r.rightClickHandler)(e,this.textarea,this.screenElement,this._selectionService,this.options.rightClickSelectsWord)}))):this.register((0,i.addDisposableDomListener)(this.element,`contextmenu`,(e=>{(0,r.rightClickHandler)(e,this.textarea,this.screenElement,this._selectionService,this.options.rightClickSelectsWord)}))),T.isLinux&&this.register((0,i.addDisposableDomListener)(this.element,`auxclick`,(e=>{e.button===1&&(0,r.moveTextAreaUnderMouseCursor)(e,this.textarea,this.screenElement)})))}_bindKeys(){this.register((0,i.addDisposableDomListener)(this.textarea,`keyup`,(e=>this._keyUp(e)),!0)),this.register((0,i.addDisposableDomListener)(this.textarea,`keydown`,(e=>this._keyDown(e)),!0)),this.register((0,i.addDisposableDomListener)(this.textarea,`keypress`,(e=>this._keyPress(e)),!0)),this.register((0,i.addDisposableDomListener)(this.textarea,`compositionstart`,(()=>this._compositionHelper.compositionstart()))),this.register((0,i.addDisposableDomListener)(this.textarea,`compositionupdate`,(e=>this._compositionHelper.compositionupdate(e)))),this.register((0,i.addDisposableDomListener)(this.textarea,`compositionend`,(()=>this._compositionHelper.compositionend()))),this.register((0,i.addDisposableDomListener)(this.textarea,`input`,(e=>this._inputEvent(e)),!0)),this.register(this.onRender((()=>this._compositionHelper.updateCompositionElements())))}open(e){if(!e)throw Error(`Terminal requires a parent element.`);if(e.isConnected||this._logService.debug(`Terminal.open was called on an element that was not attached to the DOM`),this.element?.ownerDocument.defaultView&&this._coreBrowserService)return void(this.element.ownerDocument.defaultView!==this._coreBrowserService.window&&(this._coreBrowserService.window=this.element.ownerDocument.defaultView));this._document=e.ownerDocument,this.options.documentOverride&&this.options.documentOverride instanceof Document&&(this._document=this.optionsService.rawOptions.documentOverride),this.element=this._document.createElement(`div`),this.element.dir=`ltr`,this.element.classList.add(`terminal`),this.element.classList.add(`xterm`),e.appendChild(this.element);let t=this._document.createDocumentFragment();this._viewportElement=this._document.createElement(`div`),this._viewportElement.classList.add(`xterm-viewport`),t.appendChild(this._viewportElement),this._viewportScrollArea=this._document.createElement(`div`),this._viewportScrollArea.classList.add(`xterm-scroll-area`),this._viewportElement.appendChild(this._viewportScrollArea),this.screenElement=this._document.createElement(`div`),this.screenElement.classList.add(`xterm-screen`),this.register((0,i.addDisposableDomListener)(this.screenElement,`mousemove`,(e=>this.updateCursorStyle(e)))),this._helperContainer=this._document.createElement(`div`),this._helperContainer.classList.add(`xterm-helpers`),this.screenElement.appendChild(this._helperContainer),t.appendChild(this.screenElement),this.textarea=this._document.createElement(`textarea`),this.textarea.classList.add(`xterm-helper-textarea`),this.textarea.setAttribute(`aria-label`,o.promptLabel),T.isChromeOS||this.textarea.setAttribute(`aria-multiline`,`false`),this.textarea.setAttribute(`autocorrect`,`off`),this.textarea.setAttribute(`autocapitalize`,`off`),this.textarea.setAttribute(`spellcheck`,`false`),this.textarea.tabIndex=0,this._coreBrowserService=this.register(this._instantiationService.createInstance(h.CoreBrowserService,this.textarea,e.ownerDocument.defaultView??window,this._document??typeof window<`u`?window.document:null)),this._instantiationService.setService(y.ICoreBrowserService,this._coreBrowserService),this.register((0,i.addDisposableDomListener)(this.textarea,`focus`,(e=>this._handleTextAreaFocus(e)))),this.register((0,i.addDisposableDomListener)(this.textarea,`blur`,(()=>this._handleTextAreaBlur()))),this._helperContainer.appendChild(this.textarea),this._charSizeService=this._instantiationService.createInstance(p.CharSizeService,this._document,this._helperContainer),this._instantiationService.setService(y.ICharSizeService,this._charSizeService),this._themeService=this._instantiationService.createInstance(b.ThemeService),this._instantiationService.setService(y.IThemeService,this._themeService),this._characterJoinerService=this._instantiationService.createInstance(m.CharacterJoinerService),this._instantiationService.setService(y.ICharacterJoinerService,this._characterJoinerService),this._renderService=this.register(this._instantiationService.createInstance(_.RenderService,this.rows,this.screenElement)),this._instantiationService.setService(y.IRenderService,this._renderService),this.register(this._renderService.onRenderedViewportChange((e=>this._onRender.fire(e)))),this.onResize((e=>this._renderService.resize(e.cols,e.rows))),this._compositionView=this._document.createElement(`div`),this._compositionView.classList.add(`composition-view`),this._compositionHelper=this._instantiationService.createInstance(d.CompositionHelper,this.textarea,this._compositionView),this._helperContainer.appendChild(this._compositionView),this._mouseService=this._instantiationService.createInstance(g.MouseService),this._instantiationService.setService(y.IMouseService,this._mouseService),this.linkifier=this.register(this._instantiationService.createInstance(a.Linkifier,this.screenElement)),this.element.appendChild(t);try{this._onWillOpen.fire(this.element)}catch{}this._renderService.hasRenderer()||this._renderService.setRenderer(this._createRenderer()),this.viewport=this._instantiationService.createInstance(c.Viewport,this._viewportElement,this._viewportScrollArea),this.viewport.onRequestScrollLines((e=>this.scrollLines(e.amount,e.suppressScrollEvent,1))),this.register(this._inputHandler.onRequestSyncScrollBar((()=>this.viewport.syncScrollArea()))),this.register(this.viewport),this.register(this.onCursorMove((()=>{this._renderService.handleCursorMove(),this._syncTextArea()}))),this.register(this.onResize((()=>this._renderService.handleResize(this.cols,this.rows)))),this.register(this.onBlur((()=>this._renderService.handleBlur()))),this.register(this.onFocus((()=>this._renderService.handleFocus()))),this.register(this._renderService.onDimensionsChange((()=>this.viewport.syncScrollArea()))),this._selectionService=this.register(this._instantiationService.createInstance(v.SelectionService,this.element,this.screenElement,this.linkifier)),this._instantiationService.setService(y.ISelectionService,this._selectionService),this.register(this._selectionService.onRequestScrollLines((e=>this.scrollLines(e.amount,e.suppressScrollEvent)))),this.register(this._selectionService.onSelectionChange((()=>this._onSelectionChange.fire()))),this.register(this._selectionService.onRequestRedraw((e=>this._renderService.handleSelectionChanged(e.start,e.end,e.columnSelectMode)))),this.register(this._selectionService.onLinuxMouseSelection((e=>{this.textarea.value=e,this.textarea.focus(),this.textarea.select()}))),this.register(this._onScroll.event((e=>{this.viewport.syncScrollArea(),this._selectionService.refresh()}))),this.register((0,i.addDisposableDomListener)(this._viewportElement,`scroll`,(()=>this._selectionService.refresh()))),this.register(this._instantiationService.createInstance(l.BufferDecorationRenderer,this.screenElement)),this.register((0,i.addDisposableDomListener)(this.element,`mousedown`,(e=>this._selectionService.handleMouseDown(e)))),this.coreMouseService.areMouseEventsActive?(this._selectionService.disable(),this.element.classList.add(`enable-mouse-events`)):this._selectionService.enable(),this.options.screenReaderMode&&(this._accessibilityManager.value=this._instantiationService.createInstance(N.AccessibilityManager,this)),this.register(this.optionsService.onSpecificOptionChange(`screenReaderMode`,(e=>this._handleScreenReaderModeOptionChange(e)))),this.options.overviewRulerWidth&&(this._overviewRulerRenderer=this.register(this._instantiationService.createInstance(u.OverviewRulerRenderer,this._viewportElement,this.screenElement))),this.optionsService.onSpecificOptionChange(`overviewRulerWidth`,(e=>{!this._overviewRulerRenderer&&e&&this._viewportElement&&this.screenElement&&(this._overviewRulerRenderer=this.register(this._instantiationService.createInstance(u.OverviewRulerRenderer,this._viewportElement,this.screenElement)))})),this._charSizeService.measure(),this.refresh(0,this.rows-1),this._initGlobal(),this.bindMouse()}_createRenderer(){return this._instantiationService.createInstance(f.DomRenderer,this,this._document,this.element,this.screenElement,this._viewportElement,this._helperContainer,this.linkifier)}bindMouse(){let e=this,t=this.element;function n(t){let n=e._mouseService.getMouseReportCoords(t,e.screenElement);if(!n)return!1;let r,i;switch(t.overrideType||t.type){case`mousemove`:i=32,t.buttons===void 0?(r=3,t.button!==void 0&&(r=t.button<3?t.button:3)):r=1&t.buttons?0:4&t.buttons?1:2&t.buttons?2:3;break;case`mouseup`:i=0,r=t.button<3?t.button:3;break;case`mousedown`:i=1,r=t.button<3?t.button:3;break;case`wheel`:if(e._customWheelEventHandler&&!1===e._customWheelEventHandler(t)||e.viewport.getLinesScrolled(t)===0)return!1;i=t.deltaY<0?0:1,r=4;break;default:return!1}return!(i===void 0||r===void 0||r>4)&&e.coreMouseService.triggerMouseEvent({col:n.col,row:n.row,x:n.x,y:n.y,button:r,action:i,ctrl:t.ctrlKey,alt:t.altKey,shift:t.shiftKey})}let r={mouseup:null,wheel:null,mousedrag:null,mousemove:null},a={mouseup:e=>(n(e),e.buttons||(this._document.removeEventListener(`mouseup`,r.mouseup),r.mousedrag&&this._document.removeEventListener(`mousemove`,r.mousedrag)),this.cancel(e)),wheel:e=>(n(e),this.cancel(e,!0)),mousedrag:e=>{e.buttons&&n(e)},mousemove:e=>{e.buttons||n(e)}};this.register(this.coreMouseService.onProtocolChange((e=>{e?(this.optionsService.rawOptions.logLevel===`debug`&&this._logService.debug(`Binding to mouse events:`,this.coreMouseService.explainEvents(e)),this.element.classList.add(`enable-mouse-events`),this._selectionService.disable()):(this._logService.debug(`Unbinding from mouse events.`),this.element.classList.remove(`enable-mouse-events`),this._selectionService.enable()),8&e?r.mousemove||=(t.addEventListener(`mousemove`,a.mousemove),a.mousemove):(t.removeEventListener(`mousemove`,r.mousemove),r.mousemove=null),16&e?r.wheel||=(t.addEventListener(`wheel`,a.wheel,{passive:!1}),a.wheel):(t.removeEventListener(`wheel`,r.wheel),r.wheel=null),2&e?r.mouseup||=a.mouseup:(this._document.removeEventListener(`mouseup`,r.mouseup),r.mouseup=null),4&e?r.mousedrag||=a.mousedrag:(this._document.removeEventListener(`mousemove`,r.mousedrag),r.mousedrag=null)}))),this.coreMouseService.activeProtocol=this.coreMouseService.activeProtocol,this.register((0,i.addDisposableDomListener)(t,`mousedown`,(e=>{if(e.preventDefault(),this.focus(),this.coreMouseService.areMouseEventsActive&&!this._selectionService.shouldForceSelection(e))return n(e),r.mouseup&&this._document.addEventListener(`mouseup`,r.mouseup),r.mousedrag&&this._document.addEventListener(`mousemove`,r.mousedrag),this.cancel(e)}))),this.register((0,i.addDisposableDomListener)(t,`wheel`,(e=>{if(!r.wheel){if(this._customWheelEventHandler&&!1===this._customWheelEventHandler(e))return!1;if(!this.buffer.hasScrollback){let t=this.viewport.getLinesScrolled(e);if(t===0)return;let n=D.C0.ESC+(this.coreService.decPrivateModes.applicationCursorKeys?`O`:`[`)+(e.deltaY<0?`A`:`B`),r=``;for(let e=0;e{if(!this.coreMouseService.areMouseEventsActive)return this.viewport.handleTouchStart(e),this.cancel(e)}),{passive:!0})),this.register((0,i.addDisposableDomListener)(t,`touchmove`,(e=>{if(!this.coreMouseService.areMouseEventsActive)return this.viewport.handleTouchMove(e)?void 0:this.cancel(e)}),{passive:!1}))}refresh(e,t){this._renderService?.refreshRows(e,t)}updateCursorStyle(e){this._selectionService?.shouldColumnSelect(e)?this.element.classList.add(`column-select`):this.element.classList.remove(`column-select`)}_showCursor(){this.coreService.isCursorInitialized||(this.coreService.isCursorInitialized=!0,this.refresh(this.buffer.y,this.buffer.y))}scrollLines(e,t,n=0){n===1?(super.scrollLines(e,t,n),this.refresh(0,this.rows-1)):this.viewport?.scrollLines(e)}paste(e){(0,r.paste)(e,this.textarea,this.coreService,this.optionsService)}attachCustomKeyEventHandler(e){this._customKeyEventHandler=e}attachCustomWheelEventHandler(e){this._customWheelEventHandler=e}registerLinkProvider(e){return this._linkProviderService.registerLinkProvider(e)}registerCharacterJoiner(e){if(!this._characterJoinerService)throw Error(`Terminal must be opened first`);let t=this._characterJoinerService.register(e);return this.refresh(0,this.rows-1),t}deregisterCharacterJoiner(e){if(!this._characterJoinerService)throw Error(`Terminal must be opened first`);this._characterJoinerService.deregister(e)&&this.refresh(0,this.rows-1)}get markers(){return this.buffer.markers}registerMarker(e){return this.buffer.addMarker(this.buffer.ybase+this.buffer.y+e)}registerDecoration(e){return this._decorationService.registerDecoration(e)}hasSelection(){return!!this._selectionService&&this._selectionService.hasSelection}select(e,t,n){this._selectionService.setSelection(e,t,n)}getSelection(){return this._selectionService?this._selectionService.selectionText:``}getSelectionPosition(){if(this._selectionService&&this._selectionService.hasSelection)return{start:{x:this._selectionService.selectionStart[0],y:this._selectionService.selectionStart[1]},end:{x:this._selectionService.selectionEnd[0],y:this._selectionService.selectionEnd[1]}}}clearSelection(){this._selectionService?.clearSelection()}selectAll(){this._selectionService?.selectAll()}selectLines(e,t){this._selectionService?.selectLines(e,t)}_keyDown(e){if(this._keyDownHandled=!1,this._keyDownSeen=!0,this._customKeyEventHandler&&!1===this._customKeyEventHandler(e))return!1;let t=this.browser.isMac&&this.options.macOptionIsMeta&&e.altKey;if(!t&&!this._compositionHelper.keydown(e))return this.options.scrollOnUserInput&&this.buffer.ybase!==this.buffer.ydisp&&this.scrollToBottom(),!1;t||e.key!==`Dead`&&e.key!==`AltGraph`||(this._unprocessedDeadKey=!0);let n=(0,O.evaluateKeyboardEvent)(e,this.coreService.decPrivateModes.applicationCursorKeys,this.browser.isMac,this.options.macOptionIsMeta);if(this.updateCursorStyle(e),n.type===3||n.type===2){let t=this.rows-1;return this.scrollLines(n.type===2?-t:t),this.cancel(e,!0)}return n.type===1&&this.selectAll(),!!this._isThirdLevelShift(this.browser,e)||(n.cancel&&this.cancel(e,!0),!n.key||!!(e.key&&!e.ctrlKey&&!e.altKey&&!e.metaKey&&e.key.length===1&&e.key.charCodeAt(0)>=65&&e.key.charCodeAt(0)<=90)||(this._unprocessedDeadKey?(this._unprocessedDeadKey=!1,!0):(n.key!==D.C0.ETX&&n.key!==D.C0.CR||(this.textarea.value=``),this._onKey.fire({key:n.key,domEvent:e}),this._showCursor(),this.coreService.triggerDataEvent(n.key,!0),!this.optionsService.rawOptions.screenReaderMode||e.altKey||e.ctrlKey?this.cancel(e,!0):void(this._keyDownHandled=!0))))}_isThirdLevelShift(e,t){let n=e.isMac&&!this.options.macOptionIsMeta&&t.altKey&&!t.ctrlKey&&!t.metaKey||e.isWindows&&t.altKey&&t.ctrlKey&&!t.metaKey||e.isWindows&&t.getModifierState(`AltGraph`);return t.type===`keypress`?n:n&&(!t.keyCode||t.keyCode>47)}_keyUp(e){this._keyDownSeen=!1,this._customKeyEventHandler&&!1===this._customKeyEventHandler(e)||(function(e){return e.keyCode===16||e.keyCode===17||e.keyCode===18}(e)||this.focus(),this.updateCursorStyle(e),this._keyPressHandled=!1)}_keyPress(e){let t;if(this._keyPressHandled=!1,this._keyDownHandled||this._customKeyEventHandler&&!1===this._customKeyEventHandler(e))return!1;if(this.cancel(e),e.charCode)t=e.charCode;else if(e.which===null||e.which===void 0)t=e.keyCode;else{if(e.which===0||e.charCode===0)return!1;t=e.which}return!(!t||(e.altKey||e.ctrlKey||e.metaKey)&&!this._isThirdLevelShift(this.browser,e)||(t=String.fromCharCode(t),this._onKey.fire({key:t,domEvent:e}),this._showCursor(),this.coreService.triggerDataEvent(t,!0),this._keyPressHandled=!0,this._unprocessedDeadKey=!1,0))}_inputEvent(e){if(e.data&&e.inputType===`insertText`&&(!e.composed||!this._keyDownSeen)&&!this.optionsService.rawOptions.screenReaderMode){if(this._keyPressHandled)return!1;this._unprocessedDeadKey=!1;let t=e.data;return this.coreService.triggerDataEvent(t,!0),this.cancel(e),!0}return!1}resize(e,t){e!==this.cols||t!==this.rows?super.resize(e,t):this._charSizeService&&!this._charSizeService.hasValidSize&&this._charSizeService.measure()}_afterResize(e,t){this._charSizeService?.measure(),this.viewport?.syncScrollArea(!0)}clear(){if(this.buffer.ybase!==0||this.buffer.y!==0){this.buffer.clearAllMarkers(),this.buffer.lines.set(0,this.buffer.lines.get(this.buffer.ybase+this.buffer.y)),this.buffer.lines.length=1,this.buffer.ydisp=0,this.buffer.ybase=0,this.buffer.y=0;for(let e=1;e{Object.defineProperty(t,"__esModule",{value:!0}),t.TimeBasedDebouncer=void 0,t.TimeBasedDebouncer=class{constructor(e,t=1e3){this._renderCallback=e,this._debounceThresholdMS=t,this._lastRefreshMs=0,this._additionalRefreshRequested=!1}dispose(){this._refreshTimeoutID&&clearTimeout(this._refreshTimeoutID)}refresh(e,t,n){this._rowCount=n,e=e===void 0?0:e,t=t===void 0?this._rowCount-1:t,this._rowStart=this._rowStart===void 0?e:Math.min(this._rowStart,e),this._rowEnd=this._rowEnd===void 0?t:Math.max(this._rowEnd,t);let r=Date.now();if(r-this._lastRefreshMs>=this._debounceThresholdMS)this._lastRefreshMs=r,this._innerRefresh();else if(!this._additionalRefreshRequested){let e=r-this._lastRefreshMs,t=this._debounceThresholdMS-e;this._additionalRefreshRequested=!0,this._refreshTimeoutID=window.setTimeout((()=>{this._lastRefreshMs=Date.now(),this._innerRefresh(),this._additionalRefreshRequested=!1,this._refreshTimeoutID=void 0}),t)}}_innerRefresh(){if(this._rowStart===void 0||this._rowEnd===void 0||this._rowCount===void 0)return;let e=Math.max(this._rowStart,0),t=Math.min(this._rowEnd,this._rowCount-1);this._rowStart=void 0,this._rowEnd=void 0,this._renderCallback(e,t)}}},1680:function(e,t,n){var r=this&&this.__decorate||function(e,t,n,r){var i,a=arguments.length,o=a<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r;if(typeof Reflect==`object`&&typeof Reflect.decorate==`function`)o=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(i=e[s])&&(o=(a<3?i(o):a>3?i(t,n,o):i(t,n))||o);return a>3&&o&&Object.defineProperty(t,n,o),o},i=this&&this.__param||function(e,t){return function(n,r){t(n,r,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.Viewport=void 0;let a=n(3656),o=n(4725),s=n(8460),c=n(844),l=n(2585),u=t.Viewport=class extends c.Disposable{constructor(e,t,n,r,i,o,c,l){super(),this._viewportElement=e,this._scrollArea=t,this._bufferService=n,this._optionsService=r,this._charSizeService=i,this._renderService=o,this._coreBrowserService=c,this.scrollBarWidth=0,this._currentRowHeight=0,this._currentDeviceCellHeight=0,this._lastRecordedBufferLength=0,this._lastRecordedViewportHeight=0,this._lastRecordedBufferHeight=0,this._lastTouchY=0,this._lastScrollTop=0,this._wheelPartialScroll=0,this._refreshAnimationFrame=null,this._ignoreNextScrollEvent=!1,this._smoothScrollState={startTime:0,origin:-1,target:-1},this._onRequestScrollLines=this.register(new s.EventEmitter),this.onRequestScrollLines=this._onRequestScrollLines.event,this.scrollBarWidth=this._viewportElement.offsetWidth-this._scrollArea.offsetWidth||15,this.register((0,a.addDisposableDomListener)(this._viewportElement,`scroll`,this._handleScroll.bind(this))),this._activeBuffer=this._bufferService.buffer,this.register(this._bufferService.buffers.onBufferActivate((e=>this._activeBuffer=e.activeBuffer))),this._renderDimensions=this._renderService.dimensions,this.register(this._renderService.onDimensionsChange((e=>this._renderDimensions=e))),this._handleThemeChange(l.colors),this.register(l.onChangeColors((e=>this._handleThemeChange(e)))),this.register(this._optionsService.onSpecificOptionChange(`scrollback`,(()=>this.syncScrollArea()))),setTimeout((()=>this.syncScrollArea()))}_handleThemeChange(e){this._viewportElement.style.backgroundColor=e.background.css}reset(){this._currentRowHeight=0,this._currentDeviceCellHeight=0,this._lastRecordedBufferLength=0,this._lastRecordedViewportHeight=0,this._lastRecordedBufferHeight=0,this._lastTouchY=0,this._lastScrollTop=0,this._coreBrowserService.window.requestAnimationFrame((()=>this.syncScrollArea()))}_refresh(e){if(e)return this._innerRefresh(),void(this._refreshAnimationFrame!==null&&this._coreBrowserService.window.cancelAnimationFrame(this._refreshAnimationFrame));this._refreshAnimationFrame===null&&(this._refreshAnimationFrame=this._coreBrowserService.window.requestAnimationFrame((()=>this._innerRefresh())))}_innerRefresh(){if(this._charSizeService.height>0){this._currentRowHeight=this._renderDimensions.device.cell.height/this._coreBrowserService.dpr,this._currentDeviceCellHeight=this._renderDimensions.device.cell.height,this._lastRecordedViewportHeight=this._viewportElement.offsetHeight;let e=Math.round(this._currentRowHeight*this._lastRecordedBufferLength)+(this._lastRecordedViewportHeight-this._renderDimensions.css.canvas.height);this._lastRecordedBufferHeight!==e&&(this._lastRecordedBufferHeight=e,this._scrollArea.style.height=this._lastRecordedBufferHeight+`px`)}let e=this._bufferService.buffer.ydisp*this._currentRowHeight;this._viewportElement.scrollTop!==e&&(this._ignoreNextScrollEvent=!0,this._viewportElement.scrollTop=e),this._refreshAnimationFrame=null}syncScrollArea(e=!1){if(this._lastRecordedBufferLength!==this._bufferService.buffer.lines.length)return this._lastRecordedBufferLength=this._bufferService.buffer.lines.length,void this._refresh(e);this._lastRecordedViewportHeight===this._renderService.dimensions.css.canvas.height&&this._lastScrollTop===this._activeBuffer.ydisp*this._currentRowHeight&&this._renderDimensions.device.cell.height===this._currentDeviceCellHeight||this._refresh(e)}_handleScroll(e){if(this._lastScrollTop=this._viewportElement.scrollTop,!this._viewportElement.offsetParent)return;if(this._ignoreNextScrollEvent)return this._ignoreNextScrollEvent=!1,void this._onRequestScrollLines.fire({amount:0,suppressScrollEvent:!0});let t=Math.round(this._lastScrollTop/this._currentRowHeight)-this._bufferService.buffer.ydisp;this._onRequestScrollLines.fire({amount:t,suppressScrollEvent:!0})}_smoothScroll(){if(this._isDisposed||this._smoothScrollState.origin===-1||this._smoothScrollState.target===-1)return;let e=this._smoothScrollPercent();this._viewportElement.scrollTop=this._smoothScrollState.origin+Math.round(e*(this._smoothScrollState.target-this._smoothScrollState.origin)),e<1?this._coreBrowserService.window.requestAnimationFrame((()=>this._smoothScroll())):this._clearSmoothScrollState()}_smoothScrollPercent(){return this._optionsService.rawOptions.smoothScrollDuration&&this._smoothScrollState.startTime?Math.max(Math.min((Date.now()-this._smoothScrollState.startTime)/this._optionsService.rawOptions.smoothScrollDuration,1),0):1}_clearSmoothScrollState(){this._smoothScrollState.startTime=0,this._smoothScrollState.origin=-1,this._smoothScrollState.target=-1}_bubbleScroll(e,t){let n=this._viewportElement.scrollTop+this._lastRecordedViewportHeight;return!(t<0&&this._viewportElement.scrollTop!==0||t>0&&n0&&(n=e),r=``}}return{bufferElements:i,cursorElement:n}}getLinesScrolled(e){if(e.deltaY===0||e.shiftKey)return 0;let t=this._applyScrollModifier(e.deltaY,e);return e.deltaMode===WheelEvent.DOM_DELTA_PIXEL?(t/=this._currentRowHeight+0,this._wheelPartialScroll+=t,t=Math.floor(Math.abs(this._wheelPartialScroll))*(this._wheelPartialScroll>0?1:-1),this._wheelPartialScroll%=1):e.deltaMode===WheelEvent.DOM_DELTA_PAGE&&(t*=this._bufferService.rows),t}_applyScrollModifier(e,t){let n=this._optionsService.rawOptions.fastScrollModifier;return n===`alt`&&t.altKey||n===`ctrl`&&t.ctrlKey||n===`shift`&&t.shiftKey?e*this._optionsService.rawOptions.fastScrollSensitivity*this._optionsService.rawOptions.scrollSensitivity:e*this._optionsService.rawOptions.scrollSensitivity}handleTouchStart(e){this._lastTouchY=e.touches[0].pageY}handleTouchMove(e){let t=this._lastTouchY-e.touches[0].pageY;return this._lastTouchY=e.touches[0].pageY,t!==0&&(this._viewportElement.scrollTop+=t,this._bubbleScroll(e,t))}};t.Viewport=u=r([i(2,l.IBufferService),i(3,l.IOptionsService),i(4,o.ICharSizeService),i(5,o.IRenderService),i(6,o.ICoreBrowserService),i(7,o.IThemeService)],u)},3107:function(e,t,n){var r=this&&this.__decorate||function(e,t,n,r){var i,a=arguments.length,o=a<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r;if(typeof Reflect==`object`&&typeof Reflect.decorate==`function`)o=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(i=e[s])&&(o=(a<3?i(o):a>3?i(t,n,o):i(t,n))||o);return a>3&&o&&Object.defineProperty(t,n,o),o},i=this&&this.__param||function(e,t){return function(n,r){t(n,r,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.BufferDecorationRenderer=void 0;let a=n(4725),o=n(844),s=n(2585),c=t.BufferDecorationRenderer=class extends o.Disposable{constructor(e,t,n,r,i){super(),this._screenElement=e,this._bufferService=t,this._coreBrowserService=n,this._decorationService=r,this._renderService=i,this._decorationElements=new Map,this._altBufferIsActive=!1,this._dimensionsChanged=!1,this._container=document.createElement(`div`),this._container.classList.add(`xterm-decoration-container`),this._screenElement.appendChild(this._container),this.register(this._renderService.onRenderedViewportChange((()=>this._doRefreshDecorations()))),this.register(this._renderService.onDimensionsChange((()=>{this._dimensionsChanged=!0,this._queueRefresh()}))),this.register(this._coreBrowserService.onDprChange((()=>this._queueRefresh()))),this.register(this._bufferService.buffers.onBufferActivate((()=>{this._altBufferIsActive=this._bufferService.buffer===this._bufferService.buffers.alt}))),this.register(this._decorationService.onDecorationRegistered((()=>this._queueRefresh()))),this.register(this._decorationService.onDecorationRemoved((e=>this._removeDecoration(e)))),this.register((0,o.toDisposable)((()=>{this._container.remove(),this._decorationElements.clear()})))}_queueRefresh(){this._animationFrame===void 0&&(this._animationFrame=this._renderService.addRefreshCallback((()=>{this._doRefreshDecorations(),this._animationFrame=void 0})))}_doRefreshDecorations(){for(let e of this._decorationService.decorations)this._renderDecoration(e);this._dimensionsChanged=!1}_renderDecoration(e){this._refreshStyle(e),this._dimensionsChanged&&this._refreshXPosition(e)}_createElement(e){let t=this._coreBrowserService.mainDocument.createElement(`div`);t.classList.add(`xterm-decoration`),t.classList.toggle(`xterm-decoration-top-layer`,e?.options?.layer===`top`),t.style.width=`${Math.round((e.options.width||1)*this._renderService.dimensions.css.cell.width)}px`,t.style.height=(e.options.height||1)*this._renderService.dimensions.css.cell.height+`px`,t.style.top=(e.marker.line-this._bufferService.buffers.active.ydisp)*this._renderService.dimensions.css.cell.height+`px`,t.style.lineHeight=`${this._renderService.dimensions.css.cell.height}px`;let n=e.options.x??0;return n&&n>this._bufferService.cols&&(t.style.display=`none`),this._refreshXPosition(e,t),t}_refreshStyle(e){let t=e.marker.line-this._bufferService.buffers.active.ydisp;if(t<0||t>=this._bufferService.rows)e.element&&(e.element.style.display=`none`,e.onRenderEmitter.fire(e.element));else{let n=this._decorationElements.get(e);n||(n=this._createElement(e),e.element=n,this._decorationElements.set(e,n),this._container.appendChild(n),e.onDispose((()=>{this._decorationElements.delete(e),n.remove()}))),n.style.top=t*this._renderService.dimensions.css.cell.height+`px`,n.style.display=this._altBufferIsActive?`none`:`block`,e.onRenderEmitter.fire(n)}}_refreshXPosition(e,t=e.element){if(!t)return;let n=e.options.x??0;(e.options.anchor||`left`)===`right`?t.style.right=n?n*this._renderService.dimensions.css.cell.width+`px`:``:t.style.left=n?n*this._renderService.dimensions.css.cell.width+`px`:``}_removeDecoration(e){this._decorationElements.get(e)?.remove(),this._decorationElements.delete(e),e.dispose()}};t.BufferDecorationRenderer=c=r([i(1,s.IBufferService),i(2,a.ICoreBrowserService),i(3,s.IDecorationService),i(4,a.IRenderService)],c)},5871:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ColorZoneStore=void 0,t.ColorZoneStore=class{constructor(){this._zones=[],this._zonePool=[],this._zonePoolIndex=0,this._linePadding={full:0,left:0,center:0,right:0}}get zones(){return this._zonePool.length=Math.min(this._zonePool.length,this._zones.length),this._zones}clear(){this._zones.length=0,this._zonePoolIndex=0}addDecoration(e){if(e.options.overviewRulerOptions){for(let t of this._zones)if(t.color===e.options.overviewRulerOptions.color&&t.position===e.options.overviewRulerOptions.position){if(this._lineIntersectsZone(t,e.marker.line))return;if(this._lineAdjacentToZone(t,e.marker.line,e.options.overviewRulerOptions.position))return void this._addLineToZone(t,e.marker.line)}if(this._zonePoolIndex=e.startBufferLine&&t<=e.endBufferLine}_lineAdjacentToZone(e,t,n){return t>=e.startBufferLine-this._linePadding[n||`full`]&&t<=e.endBufferLine+this._linePadding[n||`full`]}_addLineToZone(e,t){e.startBufferLine=Math.min(e.startBufferLine,t),e.endBufferLine=Math.max(e.endBufferLine,t)}}},5744:function(e,t,n){var r=this&&this.__decorate||function(e,t,n,r){var i,a=arguments.length,o=a<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r;if(typeof Reflect==`object`&&typeof Reflect.decorate==`function`)o=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(i=e[s])&&(o=(a<3?i(o):a>3?i(t,n,o):i(t,n))||o);return a>3&&o&&Object.defineProperty(t,n,o),o},i=this&&this.__param||function(e,t){return function(n,r){t(n,r,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.OverviewRulerRenderer=void 0;let a=n(5871),o=n(4725),s=n(844),c=n(2585),l={full:0,left:0,center:0,right:0},u={full:0,left:0,center:0,right:0},d={full:0,left:0,center:0,right:0},f=t.OverviewRulerRenderer=class extends s.Disposable{get _width(){return this._optionsService.options.overviewRulerWidth||0}constructor(e,t,n,r,i,o,c){super(),this._viewportElement=e,this._screenElement=t,this._bufferService=n,this._decorationService=r,this._renderService=i,this._optionsService=o,this._coreBrowserService=c,this._colorZoneStore=new a.ColorZoneStore,this._shouldUpdateDimensions=!0,this._shouldUpdateAnchor=!0,this._lastKnownBufferLength=0,this._canvas=this._coreBrowserService.mainDocument.createElement(`canvas`),this._canvas.classList.add(`xterm-decoration-overview-ruler`),this._refreshCanvasDimensions(),this._viewportElement.parentElement?.insertBefore(this._canvas,this._viewportElement);let l=this._canvas.getContext(`2d`);if(!l)throw Error(`Ctx cannot be null`);this._ctx=l,this._registerDecorationListeners(),this._registerBufferChangeListeners(),this._registerDimensionChangeListeners(),this.register((0,s.toDisposable)((()=>{this._canvas?.remove()})))}_registerDecorationListeners(){this.register(this._decorationService.onDecorationRegistered((()=>this._queueRefresh(void 0,!0)))),this.register(this._decorationService.onDecorationRemoved((()=>this._queueRefresh(void 0,!0))))}_registerBufferChangeListeners(){this.register(this._renderService.onRenderedViewportChange((()=>this._queueRefresh()))),this.register(this._bufferService.buffers.onBufferActivate((()=>{this._canvas.style.display=this._bufferService.buffer===this._bufferService.buffers.alt?`none`:`block`}))),this.register(this._bufferService.onScroll((()=>{this._lastKnownBufferLength!==this._bufferService.buffers.normal.lines.length&&(this._refreshDrawHeightConstants(),this._refreshColorZonePadding())})))}_registerDimensionChangeListeners(){this.register(this._renderService.onRender((()=>{this._containerHeight&&this._containerHeight===this._screenElement.clientHeight||(this._queueRefresh(!0),this._containerHeight=this._screenElement.clientHeight)}))),this.register(this._optionsService.onSpecificOptionChange(`overviewRulerWidth`,(()=>this._queueRefresh(!0)))),this.register(this._coreBrowserService.onDprChange((()=>this._queueRefresh(!0)))),this._queueRefresh(!0)}_refreshDrawConstants(){let e=Math.floor(this._canvas.width/3),t=Math.ceil(this._canvas.width/3);u.full=this._canvas.width,u.left=e,u.center=t,u.right=e,this._refreshDrawHeightConstants(),d.full=0,d.left=0,d.center=u.left,d.right=u.left+u.center}_refreshDrawHeightConstants(){l.full=Math.round(2*this._coreBrowserService.dpr);let e=this._canvas.height/this._bufferService.buffer.lines.length,t=Math.round(Math.max(Math.min(e,12),6)*this._coreBrowserService.dpr);l.left=t,l.center=t,l.right=t}_refreshColorZonePadding(){this._colorZoneStore.setPadding({full:Math.floor(this._bufferService.buffers.active.lines.length/(this._canvas.height-1)*l.full),left:Math.floor(this._bufferService.buffers.active.lines.length/(this._canvas.height-1)*l.left),center:Math.floor(this._bufferService.buffers.active.lines.length/(this._canvas.height-1)*l.center),right:Math.floor(this._bufferService.buffers.active.lines.length/(this._canvas.height-1)*l.right)}),this._lastKnownBufferLength=this._bufferService.buffers.normal.lines.length}_refreshCanvasDimensions(){this._canvas.style.width=`${this._width}px`,this._canvas.width=Math.round(this._width*this._coreBrowserService.dpr),this._canvas.style.height=`${this._screenElement.clientHeight}px`,this._canvas.height=Math.round(this._screenElement.clientHeight*this._coreBrowserService.dpr),this._refreshDrawConstants(),this._refreshColorZonePadding()}_refreshDecorations(){this._shouldUpdateDimensions&&this._refreshCanvasDimensions(),this._ctx.clearRect(0,0,this._canvas.width,this._canvas.height),this._colorZoneStore.clear();for(let e of this._decorationService.decorations)this._colorZoneStore.addDecoration(e);this._ctx.lineWidth=1;let e=this._colorZoneStore.zones;for(let t of e)t.position!==`full`&&this._renderColorZone(t);for(let t of e)t.position===`full`&&this._renderColorZone(t);this._shouldUpdateDimensions=!1,this._shouldUpdateAnchor=!1}_renderColorZone(e){this._ctx.fillStyle=e.color,this._ctx.fillRect(d[e.position||`full`],Math.round((this._canvas.height-1)*(e.startBufferLine/this._bufferService.buffers.active.lines.length)-l[e.position||`full`]/2),u[e.position||`full`],Math.round((this._canvas.height-1)*((e.endBufferLine-e.startBufferLine)/this._bufferService.buffers.active.lines.length)+l[e.position||`full`]))}_queueRefresh(e,t){this._shouldUpdateDimensions=e||this._shouldUpdateDimensions,this._shouldUpdateAnchor=t||this._shouldUpdateAnchor,this._animationFrame===void 0&&(this._animationFrame=this._coreBrowserService.window.requestAnimationFrame((()=>{this._refreshDecorations(),this._animationFrame=void 0})))}};t.OverviewRulerRenderer=f=r([i(2,c.IBufferService),i(3,c.IDecorationService),i(4,o.IRenderService),i(5,c.IOptionsService),i(6,o.ICoreBrowserService)],f)},2950:function(e,t,n){var r=this&&this.__decorate||function(e,t,n,r){var i,a=arguments.length,o=a<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r;if(typeof Reflect==`object`&&typeof Reflect.decorate==`function`)o=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(i=e[s])&&(o=(a<3?i(o):a>3?i(t,n,o):i(t,n))||o);return a>3&&o&&Object.defineProperty(t,n,o),o},i=this&&this.__param||function(e,t){return function(n,r){t(n,r,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.CompositionHelper=void 0;let a=n(4725),o=n(2585),s=n(2584),c=t.CompositionHelper=class{get isComposing(){return this._isComposing}constructor(e,t,n,r,i,a){this._textarea=e,this._compositionView=t,this._bufferService=n,this._optionsService=r,this._coreService=i,this._renderService=a,this._isComposing=!1,this._isSendingComposition=!1,this._compositionPosition={start:0,end:0},this._dataAlreadySent=``}compositionstart(){this._isComposing=!0,this._compositionPosition.start=this._textarea.value.length,this._compositionView.textContent=``,this._dataAlreadySent=``,this._compositionView.classList.add(`active`)}compositionupdate(e){this._compositionView.textContent=e.data,this.updateCompositionElements(),setTimeout((()=>{this._compositionPosition.end=this._textarea.value.length}),0)}compositionend(){this._finalizeComposition(!0)}keydown(e){if(this._isComposing||this._isSendingComposition){if(e.keyCode===229||e.keyCode===16||e.keyCode===17||e.keyCode===18)return!1;this._finalizeComposition(!1)}return e.keyCode!==229||(this._handleAnyTextareaChanges(),!1)}_finalizeComposition(e){if(this._compositionView.classList.remove(`active`),this._isComposing=!1,e){let e={start:this._compositionPosition.start,end:this._compositionPosition.end};this._isSendingComposition=!0,setTimeout((()=>{if(this._isSendingComposition){let t;this._isSendingComposition=!1,e.start+=this._dataAlreadySent.length,t=this._isComposing?this._textarea.value.substring(e.start,e.end):this._textarea.value.substring(e.start),t.length>0&&this._coreService.triggerDataEvent(t,!0)}}),0)}else{this._isSendingComposition=!1;let e=this._textarea.value.substring(this._compositionPosition.start,this._compositionPosition.end);this._coreService.triggerDataEvent(e,!0)}}_handleAnyTextareaChanges(){let e=this._textarea.value;setTimeout((()=>{if(!this._isComposing){let t=this._textarea.value,n=t.replace(e,``);this._dataAlreadySent=n,t.length>e.length?this._coreService.triggerDataEvent(n,!0):t.lengththis.updateCompositionElements(!0)),0)}}};t.CompositionHelper=c=r([i(2,o.IBufferService),i(3,o.IOptionsService),i(4,o.ICoreService),i(5,a.IRenderService)],c)},9806:(e,t)=>{function n(e,t,n){let r=n.getBoundingClientRect(),i=e.getComputedStyle(n),a=parseInt(i.getPropertyValue(`padding-left`)),o=parseInt(i.getPropertyValue(`padding-top`));return[t.clientX-r.left-a,t.clientY-r.top-o]}Object.defineProperty(t,"__esModule",{value:!0}),t.getCoords=t.getCoordsRelativeToElement=void 0,t.getCoordsRelativeToElement=n,t.getCoords=function(e,t,r,i,a,o,s,c,l){if(!o)return;let u=n(e,t,r);return u?(u[0]=Math.ceil((u[0]+(l?s/2:0))/s),u[1]=Math.ceil(u[1]/c),u[0]=Math.min(Math.max(u[0],1),i+ +!!l),u[1]=Math.min(Math.max(u[1],1),a),u):void 0}},9504:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.moveToCellSequence=void 0;let r=n(2584);function i(e,t,n,r){let i=e-a(e,n),s=t-a(t,n);return l(Math.abs(i-s)-function(e,t,n){let r=0,i=e-a(e,n),s=t-a(t,n);for(let a=0;a=0&&et?`A`:`B`}function s(e,t,n,r,i,a){let o=e,s=t,c=``;for(;o!==n||s!==r;)o+=i?1:-1,i&&o>a.cols-1?(c+=a.buffer.translateBufferLineToString(s,!1,e,o),o=0,e=0,s++):!i&&o<0&&(c+=a.buffer.translateBufferLineToString(s,!1,0,e+1),o=a.cols-1,e=o,s--);return c+a.buffer.translateBufferLineToString(s,!1,e,o)}function c(e,t){let n=t?`O`:`[`;return r.C0.ESC+n+e}function l(e,t){e=Math.floor(e);let n=``;for(let r=0;r0?r-a(r,o):t;let f=r,p=function(e,t,n,r,o,s){let c;return c=i(n,r,o,s).length>0?r-a(r,o):t,e=n&&ce?`D`:`C`,l(Math.abs(o-e),c(d,r));d=u>t?`D`:`C`;let f=Math.abs(u-t);return l(function(e,t){return t.cols-e}(u>t?e:o,n)+(f-1)*n.cols+1+((u>t?o:e)-1),c(d,r))}},1296:function(e,t,n){var r=this&&this.__decorate||function(e,t,n,r){var i,a=arguments.length,o=a<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r;if(typeof Reflect==`object`&&typeof Reflect.decorate==`function`)o=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(i=e[s])&&(o=(a<3?i(o):a>3?i(t,n,o):i(t,n))||o);return a>3&&o&&Object.defineProperty(t,n,o),o},i=this&&this.__param||function(e,t){return function(n,r){t(n,r,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.DomRenderer=void 0;let a=n(3787),o=n(2550),s=n(2223),c=n(6171),l=n(6052),u=n(4725),d=n(8055),f=n(8460),p=n(844),m=n(2585),h=`xterm-dom-renderer-owner-`,g=`xterm-rows`,_=`xterm-fg-`,v=`xterm-bg-`,y=`xterm-focus`,b=`xterm-selection`,x=1,S=t.DomRenderer=class extends p.Disposable{constructor(e,t,n,r,i,s,u,d,m,_,v,y,S){super(),this._terminal=e,this._document=t,this._element=n,this._screenElement=r,this._viewportElement=i,this._helperContainer=s,this._linkifier2=u,this._charSizeService=m,this._optionsService=_,this._bufferService=v,this._coreBrowserService=y,this._themeService=S,this._terminalClass=x++,this._rowElements=[],this._selectionRenderModel=(0,l.createSelectionRenderModel)(),this.onRequestRedraw=this.register(new f.EventEmitter).event,this._rowContainer=this._document.createElement(`div`),this._rowContainer.classList.add(g),this._rowContainer.style.lineHeight=`normal`,this._rowContainer.setAttribute(`aria-hidden`,`true`),this._refreshRowElements(this._bufferService.cols,this._bufferService.rows),this._selectionContainer=this._document.createElement(`div`),this._selectionContainer.classList.add(b),this._selectionContainer.setAttribute(`aria-hidden`,`true`),this.dimensions=(0,c.createRenderDimensions)(),this._updateDimensions(),this.register(this._optionsService.onOptionChange((()=>this._handleOptionsChanged()))),this.register(this._themeService.onChangeColors((e=>this._injectCss(e)))),this._injectCss(this._themeService.colors),this._rowFactory=d.createInstance(a.DomRendererRowFactory,document),this._element.classList.add(h+this._terminalClass),this._screenElement.appendChild(this._rowContainer),this._screenElement.appendChild(this._selectionContainer),this.register(this._linkifier2.onShowLinkUnderline((e=>this._handleLinkHover(e)))),this.register(this._linkifier2.onHideLinkUnderline((e=>this._handleLinkLeave(e)))),this.register((0,p.toDisposable)((()=>{this._element.classList.remove(h+this._terminalClass),this._rowContainer.remove(),this._selectionContainer.remove(),this._widthCache.dispose(),this._themeStyleElement.remove(),this._dimensionsStyleElement.remove()}))),this._widthCache=new o.WidthCache(this._document,this._helperContainer),this._widthCache.setFont(this._optionsService.rawOptions.fontFamily,this._optionsService.rawOptions.fontSize,this._optionsService.rawOptions.fontWeight,this._optionsService.rawOptions.fontWeightBold),this._setDefaultSpacing()}_updateDimensions(){let e=this._coreBrowserService.dpr;this.dimensions.device.char.width=this._charSizeService.width*e,this.dimensions.device.char.height=Math.ceil(this._charSizeService.height*e),this.dimensions.device.cell.width=this.dimensions.device.char.width+Math.round(this._optionsService.rawOptions.letterSpacing),this.dimensions.device.cell.height=Math.floor(this.dimensions.device.char.height*this._optionsService.rawOptions.lineHeight),this.dimensions.device.char.left=0,this.dimensions.device.char.top=0,this.dimensions.device.canvas.width=this.dimensions.device.cell.width*this._bufferService.cols,this.dimensions.device.canvas.height=this.dimensions.device.cell.height*this._bufferService.rows,this.dimensions.css.canvas.width=Math.round(this.dimensions.device.canvas.width/e),this.dimensions.css.canvas.height=Math.round(this.dimensions.device.canvas.height/e),this.dimensions.css.cell.width=this.dimensions.css.canvas.width/this._bufferService.cols,this.dimensions.css.cell.height=this.dimensions.css.canvas.height/this._bufferService.rows;for(let e of this._rowElements)e.style.width=`${this.dimensions.css.canvas.width}px`,e.style.height=`${this.dimensions.css.cell.height}px`,e.style.lineHeight=`${this.dimensions.css.cell.height}px`,e.style.overflow=`hidden`;this._dimensionsStyleElement||(this._dimensionsStyleElement=this._document.createElement(`style`),this._screenElement.appendChild(this._dimensionsStyleElement));let t=`${this._terminalSelector} .${g} span { display: inline-block; height: 100%; vertical-align: top;}`;this._dimensionsStyleElement.textContent=t,this._selectionContainer.style.height=this._viewportElement.style.height,this._screenElement.style.width=`${this.dimensions.css.canvas.width}px`,this._screenElement.style.height=`${this.dimensions.css.canvas.height}px`}_injectCss(e){this._themeStyleElement||(this._themeStyleElement=this._document.createElement(`style`),this._screenElement.appendChild(this._themeStyleElement));let t=`${this._terminalSelector} .${g} { color: ${e.foreground.css}; font-family: ${this._optionsService.rawOptions.fontFamily}; font-size: ${this._optionsService.rawOptions.fontSize}px; font-kerning: none; white-space: pre}`;t+=`${this._terminalSelector} .${g} .xterm-dim { color: ${d.color.multiplyOpacity(e.foreground,.5).css};}`,t+=`${this._terminalSelector} span:not(.xterm-bold) { font-weight: ${this._optionsService.rawOptions.fontWeight};}${this._terminalSelector} span.xterm-bold { font-weight: ${this._optionsService.rawOptions.fontWeightBold};}${this._terminalSelector} span.xterm-italic { font-style: italic;}`;let n=`blink_underline_${this._terminalClass}`,r=`blink_bar_${this._terminalClass}`,i=`blink_block_${this._terminalClass}`;t+=`@keyframes ${n} { 50% { border-bottom-style: hidden; }}`,t+=`@keyframes ${r} { 50% { box-shadow: none; }}`,t+=`@keyframes ${i} { 0% { background-color: ${e.cursor.css}; color: ${e.cursorAccent.css}; } 50% { background-color: inherit; color: ${e.cursor.css}; }}`,t+=`${this._terminalSelector} .${g}.${y} .xterm-cursor.xterm-cursor-blink.xterm-cursor-underline { animation: ${n} 1s step-end infinite;}${this._terminalSelector} .${g}.${y} .xterm-cursor.xterm-cursor-blink.xterm-cursor-bar { animation: ${r} 1s step-end infinite;}${this._terminalSelector} .${g}.${y} .xterm-cursor.xterm-cursor-blink.xterm-cursor-block { animation: ${i} 1s step-end infinite;}${this._terminalSelector} .${g} .xterm-cursor.xterm-cursor-block { background-color: ${e.cursor.css}; color: ${e.cursorAccent.css};}${this._terminalSelector} .${g} .xterm-cursor.xterm-cursor-block:not(.xterm-cursor-blink) { background-color: ${e.cursor.css} !important; color: ${e.cursorAccent.css} !important;}${this._terminalSelector} .${g} .xterm-cursor.xterm-cursor-outline { outline: 1px solid ${e.cursor.css}; outline-offset: -1px;}${this._terminalSelector} .${g} .xterm-cursor.xterm-cursor-bar { box-shadow: ${this._optionsService.rawOptions.cursorWidth}px 0 0 ${e.cursor.css} inset;}${this._terminalSelector} .${g} .xterm-cursor.xterm-cursor-underline { border-bottom: 1px ${e.cursor.css}; border-bottom-style: solid; height: calc(100% - 1px);}`,t+=`${this._terminalSelector} .${b} { position: absolute; top: 0; left: 0; z-index: 1; pointer-events: none;}${this._terminalSelector}.focus .${b} div { position: absolute; background-color: ${e.selectionBackgroundOpaque.css};}${this._terminalSelector} .${b} div { position: absolute; background-color: ${e.selectionInactiveBackgroundOpaque.css};}`;for(let[n,r]of e.ansi.entries())t+=`${this._terminalSelector} .${_}${n} { color: ${r.css}; }${this._terminalSelector} .${_}${n}.xterm-dim { color: ${d.color.multiplyOpacity(r,.5).css}; }${this._terminalSelector} .${v}${n} { background-color: ${r.css}; }`;t+=`${this._terminalSelector} .${_}${s.INVERTED_DEFAULT_COLOR} { color: ${d.color.opaque(e.background).css}; }${this._terminalSelector} .${_}${s.INVERTED_DEFAULT_COLOR}.xterm-dim { color: ${d.color.multiplyOpacity(d.color.opaque(e.background),.5).css}; }${this._terminalSelector} .${v}${s.INVERTED_DEFAULT_COLOR} { background-color: ${e.foreground.css}; }`,this._themeStyleElement.textContent=t}_setDefaultSpacing(){let e=this.dimensions.css.cell.width-this._widthCache.get(`W`,!1,!1);this._rowContainer.style.letterSpacing=`${e}px`,this._rowFactory.defaultSpacing=e}handleDevicePixelRatioChange(){this._updateDimensions(),this._widthCache.clear(),this._setDefaultSpacing()}_refreshRowElements(e,t){for(let e=this._rowElements.length;e<=t;e++){let e=this._document.createElement(`div`);this._rowContainer.appendChild(e),this._rowElements.push(e)}for(;this._rowElements.length>t;)this._rowContainer.removeChild(this._rowElements.pop())}handleResize(e,t){this._refreshRowElements(e,t),this._updateDimensions(),this.handleSelectionChanged(this._selectionRenderModel.selectionStart,this._selectionRenderModel.selectionEnd,this._selectionRenderModel.columnSelectMode)}handleCharSizeChanged(){this._updateDimensions(),this._widthCache.clear(),this._setDefaultSpacing()}handleBlur(){this._rowContainer.classList.remove(y),this.renderRows(0,this._bufferService.rows-1)}handleFocus(){this._rowContainer.classList.add(y),this.renderRows(this._bufferService.buffer.y,this._bufferService.buffer.y)}handleSelectionChanged(e,t,n){if(this._selectionContainer.replaceChildren(),this._rowFactory.handleSelectionChanged(e,t,n),this.renderRows(0,this._bufferService.rows-1),!e||!t)return;this._selectionRenderModel.update(this._terminal,e,t,n);let r=this._selectionRenderModel.viewportStartRow,i=this._selectionRenderModel.viewportEndRow,a=this._selectionRenderModel.viewportCappedStartRow,o=this._selectionRenderModel.viewportCappedEndRow;if(a>=this._bufferService.rows||o<0)return;let s=this._document.createDocumentFragment();if(n){let n=e[0]>t[0];s.appendChild(this._createSelectionElement(a,n?t[0]:e[0],n?e[0]:t[0],o-a+1))}else{let n=r===a?e[0]:0,c=a===i?t[0]:this._bufferService.cols;s.appendChild(this._createSelectionElement(a,n,c));let l=o-a-1;if(s.appendChild(this._createSelectionElement(a+1,0,this._bufferService.cols,l)),a!==o){let e=i===o?t[0]:this._bufferService.cols;s.appendChild(this._createSelectionElement(o,0,e))}}this._selectionContainer.appendChild(s)}_createSelectionElement(e,t,n,r=1){let i=this._document.createElement(`div`),a=t*this.dimensions.css.cell.width,o=this.dimensions.css.cell.width*(n-t);return a+o>this.dimensions.css.canvas.width&&(o=this.dimensions.css.canvas.width-a),i.style.height=r*this.dimensions.css.cell.height+`px`,i.style.top=e*this.dimensions.css.cell.height+`px`,i.style.left=`${a}px`,i.style.width=`${o}px`,i}handleCursorMove(){}_handleOptionsChanged(){this._updateDimensions(),this._injectCss(this._themeService.colors),this._widthCache.setFont(this._optionsService.rawOptions.fontFamily,this._optionsService.rawOptions.fontSize,this._optionsService.rawOptions.fontWeight,this._optionsService.rawOptions.fontWeightBold),this._setDefaultSpacing()}clear(){for(let e of this._rowElements)e.replaceChildren()}renderRows(e,t){let n=this._bufferService.buffer,r=n.ybase+n.y,i=Math.min(n.x,this._bufferService.cols-1),a=this._optionsService.rawOptions.cursorBlink,o=this._optionsService.rawOptions.cursorStyle,s=this._optionsService.rawOptions.cursorInactiveStyle;for(let c=e;c<=t;c++){let e=c+n.ydisp,t=this._rowElements[c],l=n.lines.get(e);if(!t||!l)break;t.replaceChildren(...this._rowFactory.createRow(l,e,e===r,o,s,i,a,this.dimensions.css.cell.width,this._widthCache,-1,-1))}}get _terminalSelector(){return`.${h}${this._terminalClass}`}_handleLinkHover(e){this._setCellUnderline(e.x1,e.x2,e.y1,e.y2,e.cols,!0)}_handleLinkLeave(e){this._setCellUnderline(e.x1,e.x2,e.y1,e.y2,e.cols,!1)}_setCellUnderline(e,t,n,r,i,a){n<0&&(e=0),r<0&&(t=0);let o=this._bufferService.rows-1;n=Math.max(Math.min(n,o),0),r=Math.max(Math.min(r,o),0),i=Math.min(i,this._bufferService.cols);let s=this._bufferService.buffer,c=s.ybase+s.y,l=Math.min(s.x,i-1),u=this._optionsService.rawOptions.cursorBlink,d=this._optionsService.rawOptions.cursorStyle,f=this._optionsService.rawOptions.cursorInactiveStyle;for(let o=n;o<=r;++o){let p=o+s.ydisp,m=this._rowElements[o],h=s.lines.get(p);if(!m||!h)break;m.replaceChildren(...this._rowFactory.createRow(h,p,p===c,d,f,l,u,this.dimensions.css.cell.width,this._widthCache,a?o===n?e:0:-1,a?(o===r?t:i)-1:-1))}}};t.DomRenderer=S=r([i(7,m.IInstantiationService),i(8,u.ICharSizeService),i(9,m.IOptionsService),i(10,m.IBufferService),i(11,u.ICoreBrowserService),i(12,u.IThemeService)],S)},3787:function(e,t,n){var r=this&&this.__decorate||function(e,t,n,r){var i,a=arguments.length,o=a<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r;if(typeof Reflect==`object`&&typeof Reflect.decorate==`function`)o=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(i=e[s])&&(o=(a<3?i(o):a>3?i(t,n,o):i(t,n))||o);return a>3&&o&&Object.defineProperty(t,n,o),o},i=this&&this.__param||function(e,t){return function(n,r){t(n,r,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.DomRendererRowFactory=void 0;let a=n(2223),o=n(643),s=n(511),c=n(2585),l=n(8055),u=n(4725),d=n(4269),f=n(6171),p=n(3734),m=t.DomRendererRowFactory=class{constructor(e,t,n,r,i,a,o){this._document=e,this._characterJoinerService=t,this._optionsService=n,this._coreBrowserService=r,this._coreService=i,this._decorationService=a,this._themeService=o,this._workCell=new s.CellData,this._columnSelectMode=!1,this.defaultSpacing=0}handleSelectionChanged(e,t,n){this._selectionStart=e,this._selectionEnd=t,this._columnSelectMode=n}createRow(e,t,n,r,i,s,c,u,f,m,g){let _=[],v=this._characterJoinerService.getJoinedCharacters(t),y=this._themeService.colors,b,x=e.getNoBgTrimmedLength();n&&x0&&N===v[0][0]){P=!0;let t=v.shift();I=new d.JoinedCellData(this._workCell,e.translateToString(!0,t[0],t[1]),t[1]-t[0]),F=t[1]-1,x=I.getWidth()}let L=this._isCellInSelection(N,t),R=n&&N===s,z=M&&N>=m&&N<=g,B=!1;this._decorationService.forEachDecorationAtCell(N,t,void 0,(e=>{B=!0}));let V=I.getChars()||o.WHITESPACE_CELL_CHAR;if(V===` `&&(I.isUnderline()||I.isOverline())&&(V=`\xA0`),A=x*u-f.get(V,I.isBold(),I.isItalic()),b){if(S&&(L&&k||!L&&!k&&I.bg===w)&&(L&&k&&y.selectionForeground||I.fg===T)&&I.extended.ext===E&&z===D&&A===O&&!R&&!P&&!B){I.isInvisible()?C+=o.WHITESPACE_CELL_CHAR:C+=V,S++;continue}S&&(b.textContent=C),b=this._document.createElement(`span`),S=0,C=``}else b=this._document.createElement(`span`);if(w=I.bg,T=I.fg,E=I.extended.ext,D=z,O=A,k=L,P&&s>=N&&s<=F&&(s=N),!this._coreService.isCursorHidden&&R&&this._coreService.isCursorInitialized){if(j.push(`xterm-cursor`),this._coreBrowserService.isFocused)c&&j.push(`xterm-cursor-blink`),j.push(r===`bar`?`xterm-cursor-bar`:r===`underline`?`xterm-cursor-underline`:`xterm-cursor-block`);else if(i)switch(i){case`outline`:j.push(`xterm-cursor-outline`);break;case`block`:j.push(`xterm-cursor-block`);break;case`bar`:j.push(`xterm-cursor-bar`);break;case`underline`:j.push(`xterm-cursor-underline`)}}if(I.isBold()&&j.push(`xterm-bold`),I.isItalic()&&j.push(`xterm-italic`),I.isDim()&&j.push(`xterm-dim`),C=I.isInvisible()?o.WHITESPACE_CELL_CHAR:I.getChars()||o.WHITESPACE_CELL_CHAR,I.isUnderline()&&(j.push(`xterm-underline-${I.extended.underlineStyle}`),C===` `&&(C=`\xA0`),!I.isUnderlineColorDefault()))if(I.isUnderlineColorRGB())b.style.textDecorationColor=`rgb(${p.AttributeData.toColorRGB(I.getUnderlineColor()).join(`,`)})`;else{let e=I.getUnderlineColor();this._optionsService.rawOptions.drawBoldTextInBrightColors&&I.isBold()&&e<8&&(e+=8),b.style.textDecorationColor=y.ansi[e].css}I.isOverline()&&(j.push(`xterm-overline`),C===` `&&(C=`\xA0`)),I.isStrikethrough()&&j.push(`xterm-strikethrough`),z&&(b.style.textDecoration=`underline`);let H=I.getFgColor(),U=I.getFgColorMode(),W=I.getBgColor(),G=I.getBgColorMode(),K=!!I.isInverse();if(K){let e=H;H=W,W=e;let t=U;U=G,G=t}let q,J,Y,X=!1;switch(this._decorationService.forEachDecorationAtCell(N,t,void 0,(e=>{e.options.layer!==`top`&&X||(e.backgroundColorRGB&&(G=50331648,W=e.backgroundColorRGB.rgba>>8&16777215,q=e.backgroundColorRGB),e.foregroundColorRGB&&(U=50331648,H=e.foregroundColorRGB.rgba>>8&16777215,J=e.foregroundColorRGB),X=e.options.layer===`top`)})),!X&&L&&(q=this._coreBrowserService.isFocused?y.selectionBackgroundOpaque:y.selectionInactiveBackgroundOpaque,W=q.rgba>>8&16777215,G=50331648,X=!0,y.selectionForeground&&(U=50331648,H=y.selectionForeground.rgba>>8&16777215,J=y.selectionForeground)),X&&j.push(`xterm-decoration-top`),G){case 16777216:case 33554432:Y=y.ansi[W],j.push(`xterm-bg-${W}`);break;case 50331648:Y=l.channels.toColor(W>>16,W>>8&255,255&W),this._addStyle(b,`background-color:#${h((W>>>0).toString(16),`0`,6)}`);break;default:K?(Y=y.foreground,j.push(`xterm-bg-${a.INVERTED_DEFAULT_COLOR}`)):Y=y.background}switch(q||I.isDim()&&(q=l.color.multiplyOpacity(Y,.5)),U){case 16777216:case 33554432:I.isBold()&&H<8&&this._optionsService.rawOptions.drawBoldTextInBrightColors&&(H+=8),this._applyMinimumContrast(b,Y,y.ansi[H],I,q,void 0)||j.push(`xterm-fg-${H}`);break;case 50331648:let e=l.channels.toColor(H>>16&255,H>>8&255,255&H);this._applyMinimumContrast(b,Y,e,I,q,J)||this._addStyle(b,`color:#${h(H.toString(16),`0`,6)}`);break;default:this._applyMinimumContrast(b,Y,y.foreground,I,q,J)||K&&j.push(`xterm-fg-${a.INVERTED_DEFAULT_COLOR}`)}j.length&&=(b.className=j.join(` `),0),R||P||B?b.textContent=C:S++,A!==this.defaultSpacing&&(b.style.letterSpacing=`${A}px`),_.push(b),N=F}return b&&S&&(b.textContent=C),_}_applyMinimumContrast(e,t,n,r,i,a){if(this._optionsService.rawOptions.minimumContrastRatio===1||(0,f.treatGlyphAsBackgroundColor)(r.getCode()))return!1;let o=this._getContrastCache(r),s;if(i||a||(s=o.getColor(t.rgba,n.rgba)),s===void 0){let e=this._optionsService.rawOptions.minimumContrastRatio/(r.isDim()?2:1);s=l.color.ensureContrastRatio(i||t,a||n,e),o.setColor((i||t).rgba,(a||n).rgba,s??null)}return!!s&&(this._addStyle(e,`color:${s.css}`),!0)}_getContrastCache(e){return e.isDim()?this._themeService.colors.halfContrastCache:this._themeService.colors.contrastCache}_addStyle(e,t){e.setAttribute(`style`,`${e.getAttribute(`style`)||``}${t};`)}_isCellInSelection(e,t){let n=this._selectionStart,r=this._selectionEnd;return!(!n||!r)&&(this._columnSelectMode?n[0]<=r[0]?e>=n[0]&&t>=n[1]&&e=n[1]&&e>=r[0]&&t<=r[1]:t>n[1]&&t=n[0]&&e=n[0])}};function h(e,t,n){for(;e.length{Object.defineProperty(t,"__esModule",{value:!0}),t.WidthCache=void 0,t.WidthCache=class{constructor(e,t){this._flat=new Float32Array(256),this._font=``,this._fontSize=0,this._weight=`normal`,this._weightBold=`bold`,this._measureElements=[],this._container=e.createElement(`div`),this._container.classList.add(`xterm-width-cache-measure-container`),this._container.setAttribute(`aria-hidden`,`true`),this._container.style.whiteSpace=`pre`,this._container.style.fontKerning=`none`;let n=e.createElement(`span`);n.classList.add(`xterm-char-measure-element`);let r=e.createElement(`span`);r.classList.add(`xterm-char-measure-element`),r.style.fontWeight=`bold`;let i=e.createElement(`span`);i.classList.add(`xterm-char-measure-element`),i.style.fontStyle=`italic`;let a=e.createElement(`span`);a.classList.add(`xterm-char-measure-element`),a.style.fontWeight=`bold`,a.style.fontStyle=`italic`,this._measureElements=[n,r,i,a],this._container.appendChild(n),this._container.appendChild(r),this._container.appendChild(i),this._container.appendChild(a),t.appendChild(this._container),this.clear()}dispose(){this._container.remove(),this._measureElements.length=0,this._holey=void 0}clear(){this._flat.fill(-9999),this._holey=new Map}setFont(e,t,n,r){e===this._font&&t===this._fontSize&&n===this._weight&&r===this._weightBold||(this._font=e,this._fontSize=t,this._weight=n,this._weightBold=r,this._container.style.fontFamily=this._font,this._container.style.fontSize=`${this._fontSize}px`,this._measureElements[0].style.fontWeight=`${n}`,this._measureElements[1].style.fontWeight=`${r}`,this._measureElements[2].style.fontWeight=`${n}`,this._measureElements[3].style.fontWeight=`${r}`,this.clear())}get(e,t,n){let r=0;if(!t&&!n&&e.length===1&&(r=e.charCodeAt(0))<256){if(this._flat[r]!==-9999)return this._flat[r];let t=this._measure(e,0);return t>0&&(this._flat[r]=t),t}let i=e;t&&(i+=`B`),n&&(i+=`I`);let a=this._holey.get(i);if(a===void 0){let r=0;t&&(r|=1),n&&(r|=2),a=this._measure(e,r),a>0&&this._holey.set(i,a)}return a}_measure(e,t){let n=this._measureElements[t];return n.textContent=e.repeat(32),n.offsetWidth/32}}},2223:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.TEXT_BASELINE=t.DIM_OPACITY=t.INVERTED_DEFAULT_COLOR=void 0;let r=n(6114);t.INVERTED_DEFAULT_COLOR=257,t.DIM_OPACITY=.5,t.TEXT_BASELINE=r.isFirefox||r.isLegacyEdge?`bottom`:`ideographic`},6171:(e,t)=>{function n(e){return 57508<=e&&e<=57558}function r(e){return e>=128512&&e<=128591||e>=127744&&e<=128511||e>=128640&&e<=128767||e>=9728&&e<=9983||e>=9984&&e<=10175||e>=65024&&e<=65039||e>=129280&&e<=129535||e>=127462&&e<=127487}Object.defineProperty(t,"__esModule",{value:!0}),t.computeNextVariantOffset=t.createRenderDimensions=t.treatGlyphAsBackgroundColor=t.allowRescaling=t.isEmoji=t.isRestrictedPowerlineGlyph=t.isPowerlineGlyph=t.throwIfFalsy=void 0,t.throwIfFalsy=function(e){if(!e)throw Error(`value must not be falsy`);return e},t.isPowerlineGlyph=n,t.isRestrictedPowerlineGlyph=function(e){return 57520<=e&&e<=57527},t.isEmoji=r,t.allowRescaling=function(e,t,i,a){return t===1&&i>Math.ceil(1.5*a)&&e!==void 0&&e>255&&!r(e)&&!n(e)&&!function(e){return 57344<=e&&e<=63743}(e)},t.treatGlyphAsBackgroundColor=function(e){return n(e)||function(e){return 9472<=e&&e<=9631}(e)},t.createRenderDimensions=function(){return{css:{canvas:{width:0,height:0},cell:{width:0,height:0}},device:{canvas:{width:0,height:0},cell:{width:0,height:0},char:{width:0,height:0,left:0,top:0}}}},t.computeNextVariantOffset=function(e,t,n=0){return(e-(2*Math.round(t)-n))%(2*Math.round(t))}},6052:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.createSelectionRenderModel=void 0;class n{constructor(){this.clear()}clear(){this.hasSelection=!1,this.columnSelectMode=!1,this.viewportStartRow=0,this.viewportEndRow=0,this.viewportCappedStartRow=0,this.viewportCappedEndRow=0,this.startCol=0,this.endCol=0,this.selectionStart=void 0,this.selectionEnd=void 0}update(e,t,n,r=!1){if(this.selectionStart=t,this.selectionEnd=n,!t||!n||t[0]===n[0]&&t[1]===n[1])return void this.clear();let i=e.buffers.active.ydisp,a=t[1]-i,o=n[1]-i,s=Math.max(a,0),c=Math.min(o,e.rows-1);s>=e.rows||c<0?this.clear():(this.hasSelection=!0,this.columnSelectMode=r,this.viewportStartRow=a,this.viewportEndRow=o,this.viewportCappedStartRow=s,this.viewportCappedEndRow=c,this.startCol=t[0],this.endCol=n[0])}isCellSelected(e,t,n){return!!this.hasSelection&&(n-=e.buffer.active.viewportY,this.columnSelectMode?this.startCol<=this.endCol?t>=this.startCol&&n>=this.viewportCappedStartRow&&t=this.viewportCappedStartRow&&t>=this.endCol&&n<=this.viewportCappedEndRow:n>this.viewportStartRow&&n=this.startCol&&t=this.startCol)}}t.createSelectionRenderModel=function(){return new n}},456:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.SelectionModel=void 0,t.SelectionModel=class{constructor(e){this._bufferService=e,this.isSelectAllActive=!1,this.selectionStartLength=0}clearSelection(){this.selectionStart=void 0,this.selectionEnd=void 0,this.isSelectAllActive=!1,this.selectionStartLength=0}get finalSelectionStart(){return this.isSelectAllActive?[0,0]:this.selectionEnd&&this.selectionStart&&this.areSelectionValuesReversed()?this.selectionEnd:this.selectionStart}get finalSelectionEnd(){if(this.isSelectAllActive)return[this._bufferService.cols,this._bufferService.buffer.ybase+this._bufferService.rows-1];if(this.selectionStart){if(!this.selectionEnd||this.areSelectionValuesReversed()){let e=this.selectionStart[0]+this.selectionStartLength;return e>this._bufferService.cols?e%this._bufferService.cols==0?[this._bufferService.cols,this.selectionStart[1]+Math.floor(e/this._bufferService.cols)-1]:[e%this._bufferService.cols,this.selectionStart[1]+Math.floor(e/this._bufferService.cols)]:[e,this.selectionStart[1]]}if(this.selectionStartLength&&this.selectionEnd[1]===this.selectionStart[1]){let e=this.selectionStart[0]+this.selectionStartLength;return e>this._bufferService.cols?[e%this._bufferService.cols,this.selectionStart[1]+Math.floor(e/this._bufferService.cols)]:[Math.max(e,this.selectionEnd[0]),this.selectionEnd[1]]}return this.selectionEnd}}areSelectionValuesReversed(){let e=this.selectionStart,t=this.selectionEnd;return!(!e||!t)&&(e[1]>t[1]||e[1]===t[1]&&e[0]>t[0])}handleTrim(e){return this.selectionStart&&(this.selectionStart[1]-=e),this.selectionEnd&&(this.selectionEnd[1]-=e),this.selectionEnd&&this.selectionEnd[1]<0?(this.clearSelection(),!0):(this.selectionStart&&this.selectionStart[1]<0&&(this.selectionStart[1]=0),!1)}}},428:function(e,t,n){var r=this&&this.__decorate||function(e,t,n,r){var i,a=arguments.length,o=a<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r;if(typeof Reflect==`object`&&typeof Reflect.decorate==`function`)o=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(i=e[s])&&(o=(a<3?i(o):a>3?i(t,n,o):i(t,n))||o);return a>3&&o&&Object.defineProperty(t,n,o),o},i=this&&this.__param||function(e,t){return function(n,r){t(n,r,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.CharSizeService=void 0;let a=n(2585),o=n(8460),s=n(844),c=t.CharSizeService=class extends s.Disposable{get hasValidSize(){return this.width>0&&this.height>0}constructor(e,t,n){super(),this._optionsService=n,this.width=0,this.height=0,this._onCharSizeChange=this.register(new o.EventEmitter),this.onCharSizeChange=this._onCharSizeChange.event;try{this._measureStrategy=this.register(new d(this._optionsService))}catch{this._measureStrategy=this.register(new u(e,t,this._optionsService))}this.register(this._optionsService.onMultipleOptionChange([`fontFamily`,`fontSize`],(()=>this.measure())))}measure(){let e=this._measureStrategy.measure();e.width===this.width&&e.height===this.height||(this.width=e.width,this.height=e.height,this._onCharSizeChange.fire())}};t.CharSizeService=c=r([i(2,a.IOptionsService)],c);class l extends s.Disposable{constructor(){super(...arguments),this._result={width:0,height:0}}_validateAndSet(e,t){e!==void 0&&e>0&&t!==void 0&&t>0&&(this._result.width=e,this._result.height=t)}}class u extends l{constructor(e,t,n){super(),this._document=e,this._parentElement=t,this._optionsService=n,this._measureElement=this._document.createElement(`span`),this._measureElement.classList.add(`xterm-char-measure-element`),this._measureElement.textContent=`W`.repeat(32),this._measureElement.setAttribute(`aria-hidden`,`true`),this._measureElement.style.whiteSpace=`pre`,this._measureElement.style.fontKerning=`none`,this._parentElement.appendChild(this._measureElement)}measure(){return this._measureElement.style.fontFamily=this._optionsService.rawOptions.fontFamily,this._measureElement.style.fontSize=`${this._optionsService.rawOptions.fontSize}px`,this._validateAndSet(Number(this._measureElement.offsetWidth)/32,Number(this._measureElement.offsetHeight)),this._result}}class d extends l{constructor(e){super(),this._optionsService=e,this._canvas=new OffscreenCanvas(100,100),this._ctx=this._canvas.getContext(`2d`);let t=this._ctx.measureText(`W`);if(!(`width`in t&&`fontBoundingBoxAscent`in t&&`fontBoundingBoxDescent`in t))throw Error(`Required font metrics not supported`)}measure(){this._ctx.font=`${this._optionsService.rawOptions.fontSize}px ${this._optionsService.rawOptions.fontFamily}`;let e=this._ctx.measureText(`W`);return this._validateAndSet(e.width,e.fontBoundingBoxAscent+e.fontBoundingBoxDescent),this._result}}},4269:function(e,t,n){var r=this&&this.__decorate||function(e,t,n,r){var i,a=arguments.length,o=a<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r;if(typeof Reflect==`object`&&typeof Reflect.decorate==`function`)o=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(i=e[s])&&(o=(a<3?i(o):a>3?i(t,n,o):i(t,n))||o);return a>3&&o&&Object.defineProperty(t,n,o),o},i=this&&this.__param||function(e,t){return function(n,r){t(n,r,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.CharacterJoinerService=t.JoinedCellData=void 0;let a=n(3734),o=n(643),s=n(511),c=n(2585);class l extends a.AttributeData{constructor(e,t,n){super(),this.content=0,this.combinedData=``,this.fg=e.fg,this.bg=e.bg,this.combinedData=t,this._width=n}isCombined(){return 2097152}getWidth(){return this._width}getChars(){return this.combinedData}getCode(){return 2097151}setFromCharData(e){throw Error(`not implemented`)}getAsCharData(){return[this.fg,this.getChars(),this.getWidth(),this.getCode()]}}t.JoinedCellData=l;let u=t.CharacterJoinerService=class e{constructor(e){this._bufferService=e,this._characterJoiners=[],this._nextCharacterJoinerId=0,this._workCell=new s.CellData}register(e){let t={id:this._nextCharacterJoinerId++,handler:e};return this._characterJoiners.push(t),t.id}deregister(e){for(let t=0;t1){let e=this._getJoinedRanges(r,s,a,t,i);for(let t=0;t1){let e=this._getJoinedRanges(r,s,a,t,i);for(let t=0;t{Object.defineProperty(t,"__esModule",{value:!0}),t.CoreBrowserService=void 0;let r=n(844),i=n(8460),a=n(3656);class o extends r.Disposable{constructor(e,t,n){super(),this._textarea=e,this._window=t,this.mainDocument=n,this._isFocused=!1,this._cachedIsFocused=void 0,this._screenDprMonitor=new s(this._window),this._onDprChange=this.register(new i.EventEmitter),this.onDprChange=this._onDprChange.event,this._onWindowChange=this.register(new i.EventEmitter),this.onWindowChange=this._onWindowChange.event,this.register(this.onWindowChange((e=>this._screenDprMonitor.setWindow(e)))),this.register((0,i.forwardEvent)(this._screenDprMonitor.onDprChange,this._onDprChange)),this._textarea.addEventListener(`focus`,(()=>this._isFocused=!0)),this._textarea.addEventListener(`blur`,(()=>this._isFocused=!1))}get window(){return this._window}set window(e){this._window!==e&&(this._window=e,this._onWindowChange.fire(this._window))}get dpr(){return this.window.devicePixelRatio}get isFocused(){return this._cachedIsFocused===void 0&&(this._cachedIsFocused=this._isFocused&&this._textarea.ownerDocument.hasFocus(),queueMicrotask((()=>this._cachedIsFocused=void 0))),this._cachedIsFocused}}t.CoreBrowserService=o;class s extends r.Disposable{constructor(e){super(),this._parentWindow=e,this._windowResizeListener=this.register(new r.MutableDisposable),this._onDprChange=this.register(new i.EventEmitter),this.onDprChange=this._onDprChange.event,this._outerListener=()=>this._setDprAndFireIfDiffers(),this._currentDevicePixelRatio=this._parentWindow.devicePixelRatio,this._updateDpr(),this._setWindowResizeListener(),this.register((0,r.toDisposable)((()=>this.clearListener())))}setWindow(e){this._parentWindow=e,this._setWindowResizeListener(),this._setDprAndFireIfDiffers()}_setWindowResizeListener(){this._windowResizeListener.value=(0,a.addDisposableDomListener)(this._parentWindow,`resize`,(()=>this._setDprAndFireIfDiffers()))}_setDprAndFireIfDiffers(){this._parentWindow.devicePixelRatio!==this._currentDevicePixelRatio&&this._onDprChange.fire(this._parentWindow.devicePixelRatio),this._updateDpr()}_updateDpr(){this._outerListener&&(this._resolutionMediaMatchList?.removeListener(this._outerListener),this._currentDevicePixelRatio=this._parentWindow.devicePixelRatio,this._resolutionMediaMatchList=this._parentWindow.matchMedia(`screen and (resolution: ${this._parentWindow.devicePixelRatio}dppx)`),this._resolutionMediaMatchList.addListener(this._outerListener))}clearListener(){this._resolutionMediaMatchList&&this._outerListener&&(this._resolutionMediaMatchList.removeListener(this._outerListener),this._resolutionMediaMatchList=void 0,this._outerListener=void 0)}}},779:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.LinkProviderService=void 0;let r=n(844);class i extends r.Disposable{constructor(){super(),this.linkProviders=[],this.register((0,r.toDisposable)((()=>this.linkProviders.length=0)))}registerLinkProvider(e){return this.linkProviders.push(e),{dispose:()=>{let t=this.linkProviders.indexOf(e);t!==-1&&this.linkProviders.splice(t,1)}}}}t.LinkProviderService=i},8934:function(e,t,n){var r=this&&this.__decorate||function(e,t,n,r){var i,a=arguments.length,o=a<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r;if(typeof Reflect==`object`&&typeof Reflect.decorate==`function`)o=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(i=e[s])&&(o=(a<3?i(o):a>3?i(t,n,o):i(t,n))||o);return a>3&&o&&Object.defineProperty(t,n,o),o},i=this&&this.__param||function(e,t){return function(n,r){t(n,r,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.MouseService=void 0;let a=n(4725),o=n(9806),s=t.MouseService=class{constructor(e,t){this._renderService=e,this._charSizeService=t}getCoords(e,t,n,r,i){return(0,o.getCoords)(window,e,t,n,r,this._charSizeService.hasValidSize,this._renderService.dimensions.css.cell.width,this._renderService.dimensions.css.cell.height,i)}getMouseReportCoords(e,t){let n=(0,o.getCoordsRelativeToElement)(window,e,t);if(this._charSizeService.hasValidSize)return n[0]=Math.min(Math.max(n[0],0),this._renderService.dimensions.css.canvas.width-1),n[1]=Math.min(Math.max(n[1],0),this._renderService.dimensions.css.canvas.height-1),{col:Math.floor(n[0]/this._renderService.dimensions.css.cell.width),row:Math.floor(n[1]/this._renderService.dimensions.css.cell.height),x:Math.floor(n[0]),y:Math.floor(n[1])}}};t.MouseService=s=r([i(0,a.IRenderService),i(1,a.ICharSizeService)],s)},3230:function(e,t,n){var r=this&&this.__decorate||function(e,t,n,r){var i,a=arguments.length,o=a<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r;if(typeof Reflect==`object`&&typeof Reflect.decorate==`function`)o=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(i=e[s])&&(o=(a<3?i(o):a>3?i(t,n,o):i(t,n))||o);return a>3&&o&&Object.defineProperty(t,n,o),o},i=this&&this.__param||function(e,t){return function(n,r){t(n,r,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.RenderService=void 0;let a=n(6193),o=n(4725),s=n(8460),c=n(844),l=n(7226),u=n(2585),d=t.RenderService=class extends c.Disposable{get dimensions(){return this._renderer.value.dimensions}constructor(e,t,n,r,i,o,u,d){super(),this._rowCount=e,this._charSizeService=r,this._renderer=this.register(new c.MutableDisposable),this._pausedResizeTask=new l.DebouncedIdleTask,this._observerDisposable=this.register(new c.MutableDisposable),this._isPaused=!1,this._needsFullRefresh=!1,this._isNextRenderRedrawOnly=!0,this._needsSelectionRefresh=!1,this._canvasWidth=0,this._canvasHeight=0,this._selectionState={start:void 0,end:void 0,columnSelectMode:!1},this._onDimensionsChange=this.register(new s.EventEmitter),this.onDimensionsChange=this._onDimensionsChange.event,this._onRenderedViewportChange=this.register(new s.EventEmitter),this.onRenderedViewportChange=this._onRenderedViewportChange.event,this._onRender=this.register(new s.EventEmitter),this.onRender=this._onRender.event,this._onRefreshRequest=this.register(new s.EventEmitter),this.onRefreshRequest=this._onRefreshRequest.event,this._renderDebouncer=new a.RenderDebouncer(((e,t)=>this._renderRows(e,t)),u),this.register(this._renderDebouncer),this.register(u.onDprChange((()=>this.handleDevicePixelRatioChange()))),this.register(o.onResize((()=>this._fullRefresh()))),this.register(o.buffers.onBufferActivate((()=>this._renderer.value?.clear()))),this.register(n.onOptionChange((()=>this._handleOptionsChanged()))),this.register(this._charSizeService.onCharSizeChange((()=>this.handleCharSizeChanged()))),this.register(i.onDecorationRegistered((()=>this._fullRefresh()))),this.register(i.onDecorationRemoved((()=>this._fullRefresh()))),this.register(n.onMultipleOptionChange([`customGlyphs`,`drawBoldTextInBrightColors`,`letterSpacing`,`lineHeight`,`fontFamily`,`fontSize`,`fontWeight`,`fontWeightBold`,`minimumContrastRatio`,`rescaleOverlappingGlyphs`],(()=>{this.clear(),this.handleResize(o.cols,o.rows),this._fullRefresh()}))),this.register(n.onMultipleOptionChange([`cursorBlink`,`cursorStyle`],(()=>this.refreshRows(o.buffer.y,o.buffer.y,!0)))),this.register(d.onChangeColors((()=>this._fullRefresh()))),this._registerIntersectionObserver(u.window,t),this.register(u.onWindowChange((e=>this._registerIntersectionObserver(e,t))))}_registerIntersectionObserver(e,t){if(`IntersectionObserver`in e){let n=new e.IntersectionObserver((e=>this._handleIntersectionChange(e[e.length-1])),{threshold:0});n.observe(t),this._observerDisposable.value=(0,c.toDisposable)((()=>n.disconnect()))}}_handleIntersectionChange(e){this._isPaused=e.isIntersecting===void 0?e.intersectionRatio===0:!e.isIntersecting,this._isPaused||this._charSizeService.hasValidSize||this._charSizeService.measure(),!this._isPaused&&this._needsFullRefresh&&(this._pausedResizeTask.flush(),this.refreshRows(0,this._rowCount-1),this._needsFullRefresh=!1)}refreshRows(e,t,n=!1){this._isPaused?this._needsFullRefresh=!0:(n||(this._isNextRenderRedrawOnly=!1),this._renderDebouncer.refresh(e,t,this._rowCount))}_renderRows(e,t){this._renderer.value&&(e=Math.min(e,this._rowCount-1),t=Math.min(t,this._rowCount-1),this._renderer.value.renderRows(e,t),this._needsSelectionRefresh&&=(this._renderer.value.handleSelectionChanged(this._selectionState.start,this._selectionState.end,this._selectionState.columnSelectMode),!1),this._isNextRenderRedrawOnly||this._onRenderedViewportChange.fire({start:e,end:t}),this._onRender.fire({start:e,end:t}),this._isNextRenderRedrawOnly=!0)}resize(e,t){this._rowCount=t,this._fireOnCanvasResize()}_handleOptionsChanged(){this._renderer.value&&(this.refreshRows(0,this._rowCount-1),this._fireOnCanvasResize())}_fireOnCanvasResize(){this._renderer.value&&(this._renderer.value.dimensions.css.canvas.width===this._canvasWidth&&this._renderer.value.dimensions.css.canvas.height===this._canvasHeight||this._onDimensionsChange.fire(this._renderer.value.dimensions))}hasRenderer(){return!!this._renderer.value}setRenderer(e){this._renderer.value=e,this._renderer.value&&(this._renderer.value.onRequestRedraw((e=>this.refreshRows(e.start,e.end,!0))),this._needsSelectionRefresh=!0,this._fullRefresh())}addRefreshCallback(e){return this._renderDebouncer.addRefreshCallback(e)}_fullRefresh(){this._isPaused?this._needsFullRefresh=!0:this.refreshRows(0,this._rowCount-1)}clearTextureAtlas(){this._renderer.value&&(this._renderer.value.clearTextureAtlas?.(),this._fullRefresh())}handleDevicePixelRatioChange(){this._charSizeService.measure(),this._renderer.value&&(this._renderer.value.handleDevicePixelRatioChange(),this.refreshRows(0,this._rowCount-1))}handleResize(e,t){this._renderer.value&&(this._isPaused?this._pausedResizeTask.set((()=>this._renderer.value?.handleResize(e,t))):this._renderer.value.handleResize(e,t),this._fullRefresh())}handleCharSizeChanged(){this._renderer.value?.handleCharSizeChanged()}handleBlur(){this._renderer.value?.handleBlur()}handleFocus(){this._renderer.value?.handleFocus()}handleSelectionChanged(e,t,n){this._selectionState.start=e,this._selectionState.end=t,this._selectionState.columnSelectMode=n,this._renderer.value?.handleSelectionChanged(e,t,n)}handleCursorMove(){this._renderer.value?.handleCursorMove()}clear(){this._renderer.value?.clear()}};t.RenderService=d=r([i(2,u.IOptionsService),i(3,o.ICharSizeService),i(4,u.IDecorationService),i(5,u.IBufferService),i(6,o.ICoreBrowserService),i(7,o.IThemeService)],d)},9312:function(e,t,n){var r=this&&this.__decorate||function(e,t,n,r){var i,a=arguments.length,o=a<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r;if(typeof Reflect==`object`&&typeof Reflect.decorate==`function`)o=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(i=e[s])&&(o=(a<3?i(o):a>3?i(t,n,o):i(t,n))||o);return a>3&&o&&Object.defineProperty(t,n,o),o},i=this&&this.__param||function(e,t){return function(n,r){t(n,r,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.SelectionService=void 0;let a=n(9806),o=n(9504),s=n(456),c=n(4725),l=n(8460),u=n(844),d=n(6114),f=n(4841),p=n(511),m=n(2585),h=RegExp(`\xA0`,`g`),g=t.SelectionService=class extends u.Disposable{constructor(e,t,n,r,i,a,o,c,d){super(),this._element=e,this._screenElement=t,this._linkifier=n,this._bufferService=r,this._coreService=i,this._mouseService=a,this._optionsService=o,this._renderService=c,this._coreBrowserService=d,this._dragScrollAmount=0,this._enabled=!0,this._workCell=new p.CellData,this._mouseDownTimeStamp=0,this._oldHasSelection=!1,this._oldSelectionStart=void 0,this._oldSelectionEnd=void 0,this._onLinuxMouseSelection=this.register(new l.EventEmitter),this.onLinuxMouseSelection=this._onLinuxMouseSelection.event,this._onRedrawRequest=this.register(new l.EventEmitter),this.onRequestRedraw=this._onRedrawRequest.event,this._onSelectionChange=this.register(new l.EventEmitter),this.onSelectionChange=this._onSelectionChange.event,this._onRequestScrollLines=this.register(new l.EventEmitter),this.onRequestScrollLines=this._onRequestScrollLines.event,this._mouseMoveListener=e=>this._handleMouseMove(e),this._mouseUpListener=e=>this._handleMouseUp(e),this._coreService.onUserInput((()=>{this.hasSelection&&this.clearSelection()})),this._trimListener=this._bufferService.buffer.lines.onTrim((e=>this._handleTrim(e))),this.register(this._bufferService.buffers.onBufferActivate((e=>this._handleBufferActivate(e)))),this.enable(),this._model=new s.SelectionModel(this._bufferService),this._activeSelectionMode=0,this.register((0,u.toDisposable)((()=>{this._removeMouseDownListeners()})))}reset(){this.clearSelection()}disable(){this.clearSelection(),this._enabled=!1}enable(){this._enabled=!0}get selectionStart(){return this._model.finalSelectionStart}get selectionEnd(){return this._model.finalSelectionEnd}get hasSelection(){let e=this._model.finalSelectionStart,t=this._model.finalSelectionEnd;return!(!e||!t||e[0]===t[0]&&e[1]===t[1])}get selectionText(){let e=this._model.finalSelectionStart,t=this._model.finalSelectionEnd;if(!e||!t)return``;let n=this._bufferService.buffer,r=[];if(this._activeSelectionMode===3){if(e[0]===t[0])return``;let i=e[0]e.replace(h,` `))).join(d.isWindows?`\r `:` `)}clearSelection(){this._model.clearSelection(),this._removeMouseDownListeners(),this.refresh(),this._onSelectionChange.fire()}refresh(e){this._refreshAnimationFrame||=this._coreBrowserService.window.requestAnimationFrame((()=>this._refresh())),d.isLinux&&e&&this.selectionText.length&&this._onLinuxMouseSelection.fire(this.selectionText)}_refresh(){this._refreshAnimationFrame=void 0,this._onRedrawRequest.fire({start:this._model.finalSelectionStart,end:this._model.finalSelectionEnd,columnSelectMode:this._activeSelectionMode===3})}_isClickInSelection(e){let t=this._getMouseBufferCoords(e),n=this._model.finalSelectionStart,r=this._model.finalSelectionEnd;return!!(n&&r&&t)&&this._areCoordsInSelection(t,n,r)}isCellInSelection(e,t){let n=this._model.finalSelectionStart,r=this._model.finalSelectionEnd;return!(!n||!r)&&this._areCoordsInSelection([e,t],n,r)}_areCoordsInSelection(e,t,n){return e[1]>t[1]&&e[1]=t[0]&&e[0]=t[0]}_selectWordAtCursor(e,t){let n=this._linkifier.currentLink?.link?.range;if(n)return this._model.selectionStart=[n.start.x-1,n.start.y-1],this._model.selectionStartLength=(0,f.getRangeLength)(n,this._bufferService.cols),this._model.selectionEnd=void 0,!0;let r=this._getMouseBufferCoords(e);return!!r&&(this._selectWordAt(r,t),this._model.selectionEnd=void 0,!0)}selectAll(){this._model.isSelectAllActive=!0,this.refresh(),this._onSelectionChange.fire()}selectLines(e,t){this._model.clearSelection(),e=Math.max(e,0),t=Math.min(t,this._bufferService.buffer.lines.length-1),this._model.selectionStart=[0,e],this._model.selectionEnd=[this._bufferService.cols,t],this.refresh(),this._onSelectionChange.fire()}_handleTrim(e){this._model.handleTrim(e)&&this.refresh()}_getMouseBufferCoords(e){let t=this._mouseService.getCoords(e,this._screenElement,this._bufferService.cols,this._bufferService.rows,!0);if(t)return t[0]--,t[1]--,t[1]+=this._bufferService.buffer.ydisp,t}_getMouseEventScrollAmount(e){let t=(0,a.getCoordsRelativeToElement)(this._coreBrowserService.window,e,this._screenElement)[1],n=this._renderService.dimensions.css.canvas.height;return t>=0&&t<=n?0:(t>n&&(t-=n),t=Math.min(Math.max(t,-50),50),t/=50,t/Math.abs(t)+Math.round(14*t))}shouldForceSelection(e){return d.isMac?e.altKey&&this._optionsService.rawOptions.macOptionClickForcesSelection:e.shiftKey}handleMouseDown(e){if(this._mouseDownTimeStamp=e.timeStamp,(e.button!==2||!this.hasSelection)&&e.button===0){if(!this._enabled){if(!this.shouldForceSelection(e))return;e.stopPropagation()}e.preventDefault(),this._dragScrollAmount=0,this._enabled&&e.shiftKey?this._handleIncrementalClick(e):e.detail===1?this._handleSingleClick(e):e.detail===2?this._handleDoubleClick(e):e.detail===3&&this._handleTripleClick(e),this._addMouseDownListeners(),this.refresh(!0)}}_addMouseDownListeners(){this._screenElement.ownerDocument&&(this._screenElement.ownerDocument.addEventListener(`mousemove`,this._mouseMoveListener),this._screenElement.ownerDocument.addEventListener(`mouseup`,this._mouseUpListener)),this._dragScrollIntervalTimer=this._coreBrowserService.window.setInterval((()=>this._dragScroll()),50)}_removeMouseDownListeners(){this._screenElement.ownerDocument&&(this._screenElement.ownerDocument.removeEventListener(`mousemove`,this._mouseMoveListener),this._screenElement.ownerDocument.removeEventListener(`mouseup`,this._mouseUpListener)),this._coreBrowserService.window.clearInterval(this._dragScrollIntervalTimer),this._dragScrollIntervalTimer=void 0}_handleIncrementalClick(e){this._model.selectionStart&&(this._model.selectionEnd=this._getMouseBufferCoords(e))}_handleSingleClick(e){if(this._model.selectionStartLength=0,this._model.isSelectAllActive=!1,this._activeSelectionMode=this.shouldColumnSelect(e)?3:0,this._model.selectionStart=this._getMouseBufferCoords(e),!this._model.selectionStart)return;this._model.selectionEnd=void 0;let t=this._bufferService.buffer.lines.get(this._model.selectionStart[1]);t&&t.length!==this._model.selectionStart[0]&&t.hasWidth(this._model.selectionStart[0])===0&&this._model.selectionStart[0]++}_handleDoubleClick(e){this._selectWordAtCursor(e,!0)&&(this._activeSelectionMode=1)}_handleTripleClick(e){let t=this._getMouseBufferCoords(e);t&&(this._activeSelectionMode=2,this._selectLineAt(t[1]))}shouldColumnSelect(e){return e.altKey&&!(d.isMac&&this._optionsService.rawOptions.macOptionClickForcesSelection)}_handleMouseMove(e){if(e.stopImmediatePropagation(),!this._model.selectionStart)return;let t=this._model.selectionEnd?[this._model.selectionEnd[0],this._model.selectionEnd[1]]:null;if(this._model.selectionEnd=this._getMouseBufferCoords(e),!this._model.selectionEnd)return void this.refresh(!0);this._activeSelectionMode===2?this._model.selectionEnd[1]0?this._model.selectionEnd[0]=this._bufferService.cols:this._dragScrollAmount<0&&(this._model.selectionEnd[0]=0));let n=this._bufferService.buffer;if(this._model.selectionEnd[1]0?(this._activeSelectionMode!==3&&(this._model.selectionEnd[0]=this._bufferService.cols),this._model.selectionEnd[1]=Math.min(e.ydisp+this._bufferService.rows,e.lines.length-1)):(this._activeSelectionMode!==3&&(this._model.selectionEnd[0]=0),this._model.selectionEnd[1]=e.ydisp),this.refresh()}}_handleMouseUp(e){let t=e.timeStamp-this._mouseDownTimeStamp;if(this._removeMouseDownListeners(),this.selectionText.length<=1&&t<500&&e.altKey&&this._optionsService.rawOptions.altClickMovesCursor){if(this._bufferService.buffer.ybase===this._bufferService.buffer.ydisp){let t=this._mouseService.getCoords(e,this._element,this._bufferService.cols,this._bufferService.rows,!1);if(t&&t[0]!==void 0&&t[1]!==void 0){let e=(0,o.moveToCellSequence)(t[0]-1,t[1]-1,this._bufferService,this._coreService.decPrivateModes.applicationCursorKeys);this._coreService.triggerDataEvent(e,!0)}}}else this._fireEventIfSelectionChanged()}_fireEventIfSelectionChanged(){let e=this._model.finalSelectionStart,t=this._model.finalSelectionEnd,n=!(!e||!t||e[0]===t[0]&&e[1]===t[1]);n?e&&t&&(this._oldSelectionStart&&this._oldSelectionEnd&&e[0]===this._oldSelectionStart[0]&&e[1]===this._oldSelectionStart[1]&&t[0]===this._oldSelectionEnd[0]&&t[1]===this._oldSelectionEnd[1]||this._fireOnSelectionChange(e,t,n)):this._oldHasSelection&&this._fireOnSelectionChange(e,t,n)}_fireOnSelectionChange(e,t,n){this._oldSelectionStart=e,this._oldSelectionEnd=t,this._oldHasSelection=n,this._onSelectionChange.fire()}_handleBufferActivate(e){this.clearSelection(),this._trimListener.dispose(),this._trimListener=e.activeBuffer.lines.onTrim((e=>this._handleTrim(e)))}_convertViewportColToCharacterIndex(e,t){let n=t;for(let r=0;t>=r;r++){let i=e.loadCell(r,this._workCell).getChars().length;this._workCell.getWidth()===0?n--:i>1&&t!==r&&(n+=i-1)}return n}setSelection(e,t,n){this._model.clearSelection(),this._removeMouseDownListeners(),this._model.selectionStart=[e,t],this._model.selectionStartLength=n,this.refresh(),this._fireEventIfSelectionChanged()}rightClickSelect(e){this._isClickInSelection(e)||(this._selectWordAtCursor(e,!1)&&this.refresh(!0),this._fireEventIfSelectionChanged())}_getWordAt(e,t,n=!0,r=!0){if(e[0]>=this._bufferService.cols)return;let i=this._bufferService.buffer,a=i.lines.get(e[1]);if(!a)return;let o=i.translateBufferLineToString(e[1],!1),s=this._convertViewportColToCharacterIndex(a,e[0]),c=s,l=e[0]-s,u=0,d=0,f=0,p=0;if(o.charAt(s)===` `){for(;s>0&&o.charAt(s-1)===` `;)s--;for(;c1&&(p+=r-1,c+=r-1);t>0&&s>0&&!this._isCharWordSeparator(a.loadCell(t-1,this._workCell));){a.loadCell(t-1,this._workCell);let e=this._workCell.getChars().length;this._workCell.getWidth()===0?(u++,t--):e>1&&(f+=e-1,s-=e-1),s--,t--}for(;n1&&(p+=e-1,c+=e-1),c++,n++}}c++;let m=s+l-u+f,h=Math.min(this._bufferService.cols,c-s+u+d-f-p);if(t||o.slice(s,c).trim()!==``){if(n&&m===0&&a.getCodePoint(0)!==32){let t=i.lines.get(e[1]-1);if(t&&a.isWrapped&&t.getCodePoint(this._bufferService.cols-1)!==32){let t=this._getWordAt([this._bufferService.cols-1,e[1]-1],!1,!0,!1);if(t){let e=this._bufferService.cols-t.start;m-=e,h+=e}}}if(r&&m+h===this._bufferService.cols&&a.getCodePoint(this._bufferService.cols-1)!==32){let t=i.lines.get(e[1]+1);if(t?.isWrapped&&t.getCodePoint(0)!==32){let t=this._getWordAt([0,e[1]+1],!1,!1,!0);t&&(h+=t.length)}}return{start:m,length:h}}}_selectWordAt(e,t){let n=this._getWordAt(e,t);if(n){for(;n.start<0;)n.start+=this._bufferService.cols,e[1]--;this._model.selectionStart=[n.start,e[1]],this._model.selectionStartLength=n.length}}_selectToWordAt(e){let t=this._getWordAt(e,!0);if(t){let n=e[1];for(;t.start<0;)t.start+=this._bufferService.cols,n--;if(!this._model.areSelectionValuesReversed())for(;t.start+t.length>this._bufferService.cols;)t.length-=this._bufferService.cols,n++;this._model.selectionEnd=[this._model.areSelectionValuesReversed()?t.start:t.start+t.length,n]}}_isCharWordSeparator(e){return e.getWidth()!==0&&this._optionsService.rawOptions.wordSeparator.indexOf(e.getChars())>=0}_selectLineAt(e){let t=this._bufferService.buffer.getWrappedRangeForLine(e),n={start:{x:0,y:t.first},end:{x:this._bufferService.cols-1,y:t.last}};this._model.selectionStart=[0,t.first],this._model.selectionEnd=void 0,this._model.selectionStartLength=(0,f.getRangeLength)(n,this._bufferService.cols)}};t.SelectionService=g=r([i(3,m.IBufferService),i(4,m.ICoreService),i(5,c.IMouseService),i(6,m.IOptionsService),i(7,c.IRenderService),i(8,c.ICoreBrowserService)],g)},4725:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ILinkProviderService=t.IThemeService=t.ICharacterJoinerService=t.ISelectionService=t.IRenderService=t.IMouseService=t.ICoreBrowserService=t.ICharSizeService=void 0;let r=n(8343);t.ICharSizeService=(0,r.createDecorator)(`CharSizeService`),t.ICoreBrowserService=(0,r.createDecorator)(`CoreBrowserService`),t.IMouseService=(0,r.createDecorator)(`MouseService`),t.IRenderService=(0,r.createDecorator)(`RenderService`),t.ISelectionService=(0,r.createDecorator)(`SelectionService`),t.ICharacterJoinerService=(0,r.createDecorator)(`CharacterJoinerService`),t.IThemeService=(0,r.createDecorator)(`ThemeService`),t.ILinkProviderService=(0,r.createDecorator)(`LinkProviderService`)},6731:function(e,t,n){var r=this&&this.__decorate||function(e,t,n,r){var i,a=arguments.length,o=a<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r;if(typeof Reflect==`object`&&typeof Reflect.decorate==`function`)o=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(i=e[s])&&(o=(a<3?i(o):a>3?i(t,n,o):i(t,n))||o);return a>3&&o&&Object.defineProperty(t,n,o),o},i=this&&this.__param||function(e,t){return function(n,r){t(n,r,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.ThemeService=t.DEFAULT_ANSI_COLORS=void 0;let a=n(7239),o=n(8055),s=n(8460),c=n(844),l=n(2585),u=o.css.toColor(`#ffffff`),d=o.css.toColor(`#000000`),f=o.css.toColor(`#ffffff`),p=o.css.toColor(`#000000`),m={css:`rgba(255, 255, 255, 0.3)`,rgba:4294967117};t.DEFAULT_ANSI_COLORS=Object.freeze((()=>{let e=[o.css.toColor(`#2e3436`),o.css.toColor(`#cc0000`),o.css.toColor(`#4e9a06`),o.css.toColor(`#c4a000`),o.css.toColor(`#3465a4`),o.css.toColor(`#75507b`),o.css.toColor(`#06989a`),o.css.toColor(`#d3d7cf`),o.css.toColor(`#555753`),o.css.toColor(`#ef2929`),o.css.toColor(`#8ae234`),o.css.toColor(`#fce94f`),o.css.toColor(`#729fcf`),o.css.toColor(`#ad7fa8`),o.css.toColor(`#34e2e2`),o.css.toColor(`#eeeeec`)],t=[0,95,135,175,215,255];for(let n=0;n<216;n++){let r=t[n/36%6|0],i=t[n/6%6|0],a=t[n%6];e.push({css:o.channels.toCss(r,i,a),rgba:o.channels.toRgba(r,i,a)})}for(let t=0;t<24;t++){let n=8+10*t;e.push({css:o.channels.toCss(n,n,n),rgba:o.channels.toRgba(n,n,n)})}return e})());let h=t.ThemeService=class extends c.Disposable{get colors(){return this._colors}constructor(e){super(),this._optionsService=e,this._contrastCache=new a.ColorContrastCache,this._halfContrastCache=new a.ColorContrastCache,this._onChangeColors=this.register(new s.EventEmitter),this.onChangeColors=this._onChangeColors.event,this._colors={foreground:u,background:d,cursor:f,cursorAccent:p,selectionForeground:void 0,selectionBackgroundTransparent:m,selectionBackgroundOpaque:o.color.blend(d,m),selectionInactiveBackgroundTransparent:m,selectionInactiveBackgroundOpaque:o.color.blend(d,m),ansi:t.DEFAULT_ANSI_COLORS.slice(),contrastCache:this._contrastCache,halfContrastCache:this._halfContrastCache},this._updateRestoreColors(),this._setTheme(this._optionsService.rawOptions.theme),this.register(this._optionsService.onSpecificOptionChange(`minimumContrastRatio`,(()=>this._contrastCache.clear()))),this.register(this._optionsService.onSpecificOptionChange(`theme`,(()=>this._setTheme(this._optionsService.rawOptions.theme))))}_setTheme(e={}){let n=this._colors;if(n.foreground=g(e.foreground,u),n.background=g(e.background,d),n.cursor=g(e.cursor,f),n.cursorAccent=g(e.cursorAccent,p),n.selectionBackgroundTransparent=g(e.selectionBackground,m),n.selectionBackgroundOpaque=o.color.blend(n.background,n.selectionBackgroundTransparent),n.selectionInactiveBackgroundTransparent=g(e.selectionInactiveBackground,n.selectionBackgroundTransparent),n.selectionInactiveBackgroundOpaque=o.color.blend(n.background,n.selectionInactiveBackgroundTransparent),n.selectionForeground=e.selectionForeground?g(e.selectionForeground,o.NULL_COLOR):void 0,n.selectionForeground===o.NULL_COLOR&&(n.selectionForeground=void 0),o.color.isOpaque(n.selectionBackgroundTransparent)&&(n.selectionBackgroundTransparent=o.color.opacity(n.selectionBackgroundTransparent,.3)),o.color.isOpaque(n.selectionInactiveBackgroundTransparent)&&(n.selectionInactiveBackgroundTransparent=o.color.opacity(n.selectionInactiveBackgroundTransparent,.3)),n.ansi=t.DEFAULT_ANSI_COLORS.slice(),n.ansi[0]=g(e.black,t.DEFAULT_ANSI_COLORS[0]),n.ansi[1]=g(e.red,t.DEFAULT_ANSI_COLORS[1]),n.ansi[2]=g(e.green,t.DEFAULT_ANSI_COLORS[2]),n.ansi[3]=g(e.yellow,t.DEFAULT_ANSI_COLORS[3]),n.ansi[4]=g(e.blue,t.DEFAULT_ANSI_COLORS[4]),n.ansi[5]=g(e.magenta,t.DEFAULT_ANSI_COLORS[5]),n.ansi[6]=g(e.cyan,t.DEFAULT_ANSI_COLORS[6]),n.ansi[7]=g(e.white,t.DEFAULT_ANSI_COLORS[7]),n.ansi[8]=g(e.brightBlack,t.DEFAULT_ANSI_COLORS[8]),n.ansi[9]=g(e.brightRed,t.DEFAULT_ANSI_COLORS[9]),n.ansi[10]=g(e.brightGreen,t.DEFAULT_ANSI_COLORS[10]),n.ansi[11]=g(e.brightYellow,t.DEFAULT_ANSI_COLORS[11]),n.ansi[12]=g(e.brightBlue,t.DEFAULT_ANSI_COLORS[12]),n.ansi[13]=g(e.brightMagenta,t.DEFAULT_ANSI_COLORS[13]),n.ansi[14]=g(e.brightCyan,t.DEFAULT_ANSI_COLORS[14]),n.ansi[15]=g(e.brightWhite,t.DEFAULT_ANSI_COLORS[15]),e.extendedAnsi){let r=Math.min(n.ansi.length-16,e.extendedAnsi.length);for(let i=0;i{Object.defineProperty(t,"__esModule",{value:!0}),t.CircularList=void 0;let r=n(8460),i=n(844);class a extends i.Disposable{constructor(e){super(),this._maxLength=e,this.onDeleteEmitter=this.register(new r.EventEmitter),this.onDelete=this.onDeleteEmitter.event,this.onInsertEmitter=this.register(new r.EventEmitter),this.onInsert=this.onInsertEmitter.event,this.onTrimEmitter=this.register(new r.EventEmitter),this.onTrim=this.onTrimEmitter.event,this._array=Array(this._maxLength),this._startIndex=0,this._length=0}get maxLength(){return this._maxLength}set maxLength(e){if(this._maxLength===e)return;let t=Array(e);for(let n=0;nthis._length)for(let t=this._length;t=e;t--)this._array[this._getCyclicIndex(t+n.length)]=this._array[this._getCyclicIndex(t)];for(let t=0;tthis._maxLength){let e=this._length+n.length-this._maxLength;this._startIndex+=e,this._length=this._maxLength,this.onTrimEmitter.fire(e)}else this._length+=n.length}trimStart(e){e>this._length&&(e=this._length),this._startIndex+=e,this._length-=e,this.onTrimEmitter.fire(e)}shiftElements(e,t,n){if(!(t<=0)){if(e<0||e>=this._length)throw Error(`start argument out of range`);if(e+n<0)throw Error(`Cannot shift elements in list beyond index 0`);if(n>0){for(let r=t-1;r>=0;r--)this.set(e+r+n,this.get(e+r));let r=e+t+n-this._length;if(r>0)for(this._length+=r;this._length>this._maxLength;)this._length--,this._startIndex++,this.onTrimEmitter.fire(1)}else for(let r=0;r{Object.defineProperty(t,"__esModule",{value:!0}),t.clone=void 0,t.clone=function e(t,n=5){if(typeof t!=`object`)return t;let r=Array.isArray(t)?[]:{};for(let i in t)r[i]=n<=1?t[i]:t[i]&&e(t[i],n-1);return r}},8055:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.contrastRatio=t.toPaddedHex=t.rgba=t.rgb=t.css=t.color=t.channels=t.NULL_COLOR=void 0;let n=0,r=0,i=0,a=0;var o,s,c,l,u;function d(e){let t=e.toString(16);return t.length<2?`0`+t:t}function f(e,t){return e>>0},e.toColor=function(t,n,r,i){return{css:e.toCss(t,n,r,i),rgba:e.toRgba(t,n,r,i)}}}(o||(t.channels=o={})),function(e){function t(e,t){return a=Math.round(255*t),[n,r,i]=u.toChannels(e.rgba),{css:o.toCss(n,r,i,a),rgba:o.toRgba(n,r,i,a)}}e.blend=function(e,t){if(a=(255&t.rgba)/255,a===1)return{css:t.css,rgba:t.rgba};let s=t.rgba>>24&255,c=t.rgba>>16&255,l=t.rgba>>8&255,u=e.rgba>>24&255,d=e.rgba>>16&255,f=e.rgba>>8&255;return n=u+Math.round((s-u)*a),r=d+Math.round((c-d)*a),i=f+Math.round((l-f)*a),{css:o.toCss(n,r,i),rgba:o.toRgba(n,r,i)}},e.isOpaque=function(e){return(255&e.rgba)==255},e.ensureContrastRatio=function(e,t,n){let r=u.ensureContrastRatio(e.rgba,t.rgba,n);if(r)return o.toColor(r>>24&255,r>>16&255,r>>8&255)},e.opaque=function(e){let t=(255|e.rgba)>>>0;return[n,r,i]=u.toChannels(t),{css:o.toCss(n,r,i),rgba:t}},e.opacity=t,e.multiplyOpacity=function(e,n){return a=255&e.rgba,t(e,a*n/255)},e.toColorRGB=function(e){return[e.rgba>>24&255,e.rgba>>16&255,e.rgba>>8&255]}}(s||(t.color=s={})),function(e){let t,s;try{let e=document.createElement(`canvas`);e.width=1,e.height=1;let n=e.getContext(`2d`,{willReadFrequently:!0});n&&(t=n,t.globalCompositeOperation=`copy`,s=t.createLinearGradient(0,0,1,1))}catch{}e.toColor=function(e){if(e.match(/#[\da-f]{3,8}/i))switch(e.length){case 4:return n=parseInt(e.slice(1,2).repeat(2),16),r=parseInt(e.slice(2,3).repeat(2),16),i=parseInt(e.slice(3,4).repeat(2),16),o.toColor(n,r,i);case 5:return n=parseInt(e.slice(1,2).repeat(2),16),r=parseInt(e.slice(2,3).repeat(2),16),i=parseInt(e.slice(3,4).repeat(2),16),a=parseInt(e.slice(4,5).repeat(2),16),o.toColor(n,r,i,a);case 7:return{css:e,rgba:(parseInt(e.slice(1),16)<<8|255)>>>0};case 9:return{css:e,rgba:parseInt(e.slice(1),16)>>>0}}let c=e.match(/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(,\s*(0|1|\d?\.(\d+))\s*)?\)/);if(c)return n=parseInt(c[1]),r=parseInt(c[2]),i=parseInt(c[3]),a=Math.round(255*(c[5]===void 0?1:parseFloat(c[5]))),o.toColor(n,r,i,a);if(!t||!s||(t.fillStyle=s,t.fillStyle=e,typeof t.fillStyle!=`string`)||(t.fillRect(0,0,1,1),[n,r,i,a]=t.getImageData(0,0,1,1).data,a!==255))throw Error(`css.toColor: Unsupported css format`);return{rgba:o.toRgba(n,r,i,a),css:e}}}(c||(t.css=c={})),function(e){function t(e,t,n){let r=e/255,i=t/255,a=n/255;return .2126*(r<=.03928?r/12.92:((r+.055)/1.055)**2.4)+.7152*(i<=.03928?i/12.92:((i+.055)/1.055)**2.4)+.0722*(a<=.03928?a/12.92:((a+.055)/1.055)**2.4)}e.relativeLuminance=function(e){return t(e>>16&255,e>>8&255,255&e)},e.relativeLuminance2=t}(l||(t.rgb=l={})),function(e){function t(e,t,n){let r=e>>24&255,i=e>>16&255,a=e>>8&255,o=t>>24&255,s=t>>16&255,c=t>>8&255,u=f(l.relativeLuminance2(o,s,c),l.relativeLuminance2(r,i,a));for(;u0||s>0||c>0);)o-=Math.max(0,Math.ceil(.1*o)),s-=Math.max(0,Math.ceil(.1*s)),c-=Math.max(0,Math.ceil(.1*c)),u=f(l.relativeLuminance2(o,s,c),l.relativeLuminance2(r,i,a));return(o<<24|s<<16|c<<8|255)>>>0}function s(e,t,n){let r=e>>24&255,i=e>>16&255,a=e>>8&255,o=t>>24&255,s=t>>16&255,c=t>>8&255,u=f(l.relativeLuminance2(o,s,c),l.relativeLuminance2(r,i,a));for(;u>>0}e.blend=function(e,t){if(a=(255&t)/255,a===1)return t;let s=t>>24&255,c=t>>16&255,l=t>>8&255,u=e>>24&255,d=e>>16&255,f=e>>8&255;return n=u+Math.round((s-u)*a),r=d+Math.round((c-d)*a),i=f+Math.round((l-f)*a),o.toRgba(n,r,i)},e.ensureContrastRatio=function(e,n,r){let i=l.relativeLuminance(e>>8),a=l.relativeLuminance(n>>8);if(f(i,a)>8));if(of(i,l.relativeLuminance(t>>8))?a:t}return a}let o=s(e,n,r),c=f(i,l.relativeLuminance(o>>8));if(cf(i,l.relativeLuminance(a>>8))?o:a}return o}},e.reduceLuminance=t,e.increaseLuminance=s,e.toChannels=function(e){return[e>>24&255,e>>16&255,e>>8&255,255&e]}}(u||(t.rgba=u={})),t.toPaddedHex=d,t.contrastRatio=f},8969:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.CoreTerminal=void 0;let r=n(844),i=n(2585),a=n(4348),o=n(7866),s=n(744),c=n(7302),l=n(6975),u=n(8460),d=n(1753),f=n(1480),p=n(7994),m=n(9282),h=n(5435),g=n(5981),_=n(2660),v=!1;class y extends r.Disposable{get onScroll(){return this._onScrollApi||(this._onScrollApi=this.register(new u.EventEmitter),this._onScroll.event((e=>{this._onScrollApi?.fire(e.position)}))),this._onScrollApi.event}get cols(){return this._bufferService.cols}get rows(){return this._bufferService.rows}get buffers(){return this._bufferService.buffers}get options(){return this.optionsService.options}set options(e){for(let t in e)this.optionsService.options[t]=e[t]}constructor(e){super(),this._windowsWrappingHeuristics=this.register(new r.MutableDisposable),this._onBinary=this.register(new u.EventEmitter),this.onBinary=this._onBinary.event,this._onData=this.register(new u.EventEmitter),this.onData=this._onData.event,this._onLineFeed=this.register(new u.EventEmitter),this.onLineFeed=this._onLineFeed.event,this._onResize=this.register(new u.EventEmitter),this.onResize=this._onResize.event,this._onWriteParsed=this.register(new u.EventEmitter),this.onWriteParsed=this._onWriteParsed.event,this._onScroll=this.register(new u.EventEmitter),this._instantiationService=new a.InstantiationService,this.optionsService=this.register(new c.OptionsService(e)),this._instantiationService.setService(i.IOptionsService,this.optionsService),this._bufferService=this.register(this._instantiationService.createInstance(s.BufferService)),this._instantiationService.setService(i.IBufferService,this._bufferService),this._logService=this.register(this._instantiationService.createInstance(o.LogService)),this._instantiationService.setService(i.ILogService,this._logService),this.coreService=this.register(this._instantiationService.createInstance(l.CoreService)),this._instantiationService.setService(i.ICoreService,this.coreService),this.coreMouseService=this.register(this._instantiationService.createInstance(d.CoreMouseService)),this._instantiationService.setService(i.ICoreMouseService,this.coreMouseService),this.unicodeService=this.register(this._instantiationService.createInstance(f.UnicodeService)),this._instantiationService.setService(i.IUnicodeService,this.unicodeService),this._charsetService=this._instantiationService.createInstance(p.CharsetService),this._instantiationService.setService(i.ICharsetService,this._charsetService),this._oscLinkService=this._instantiationService.createInstance(_.OscLinkService),this._instantiationService.setService(i.IOscLinkService,this._oscLinkService),this._inputHandler=this.register(new h.InputHandler(this._bufferService,this._charsetService,this.coreService,this._logService,this.optionsService,this._oscLinkService,this.coreMouseService,this.unicodeService)),this.register((0,u.forwardEvent)(this._inputHandler.onLineFeed,this._onLineFeed)),this.register(this._inputHandler),this.register((0,u.forwardEvent)(this._bufferService.onResize,this._onResize)),this.register((0,u.forwardEvent)(this.coreService.onData,this._onData)),this.register((0,u.forwardEvent)(this.coreService.onBinary,this._onBinary)),this.register(this.coreService.onRequestScrollToBottom((()=>this.scrollToBottom()))),this.register(this.coreService.onUserInput((()=>this._writeBuffer.handleUserInput()))),this.register(this.optionsService.onMultipleOptionChange([`windowsMode`,`windowsPty`],(()=>this._handleWindowsPtyOptionChange()))),this.register(this._bufferService.onScroll((e=>{this._onScroll.fire({position:this._bufferService.buffer.ydisp,source:0}),this._inputHandler.markRangeDirty(this._bufferService.buffer.scrollTop,this._bufferService.buffer.scrollBottom)}))),this.register(this._inputHandler.onScroll((e=>{this._onScroll.fire({position:this._bufferService.buffer.ydisp,source:0}),this._inputHandler.markRangeDirty(this._bufferService.buffer.scrollTop,this._bufferService.buffer.scrollBottom)}))),this._writeBuffer=this.register(new g.WriteBuffer(((e,t)=>this._inputHandler.parse(e,t)))),this.register((0,u.forwardEvent)(this._writeBuffer.onWriteParsed,this._onWriteParsed))}write(e,t){this._writeBuffer.write(e,t)}writeSync(e,t){this._logService.logLevel<=i.LogLevelEnum.WARN&&!v&&(this._logService.warn(`writeSync is unreliable and will be removed soon.`),v=!0),this._writeBuffer.writeSync(e,t)}input(e,t=!0){this.coreService.triggerDataEvent(e,t)}resize(e,t){isNaN(e)||isNaN(t)||(e=Math.max(e,s.MINIMUM_COLS),t=Math.max(t,s.MINIMUM_ROWS),this._bufferService.resize(e,t))}scroll(e,t=!1){this._bufferService.scroll(e,t)}scrollLines(e,t,n){this._bufferService.scrollLines(e,t,n)}scrollPages(e){this.scrollLines(e*(this.rows-1))}scrollToTop(){this.scrollLines(-this._bufferService.buffer.ydisp)}scrollToBottom(){this.scrollLines(this._bufferService.buffer.ybase-this._bufferService.buffer.ydisp)}scrollToLine(e){let t=e-this._bufferService.buffer.ydisp;t!==0&&this.scrollLines(t)}registerEscHandler(e,t){return this._inputHandler.registerEscHandler(e,t)}registerDcsHandler(e,t){return this._inputHandler.registerDcsHandler(e,t)}registerCsiHandler(e,t){return this._inputHandler.registerCsiHandler(e,t)}registerOscHandler(e,t){return this._inputHandler.registerOscHandler(e,t)}_setup(){this._handleWindowsPtyOptionChange()}reset(){this._inputHandler.reset(),this._bufferService.reset(),this._charsetService.reset(),this.coreService.reset(),this.coreMouseService.reset()}_handleWindowsPtyOptionChange(){let e=!1,t=this.optionsService.rawOptions.windowsPty;t&&t.buildNumber!==void 0&&t.buildNumber!==void 0?e=t.backend===`conpty`&&t.buildNumber<21376:this.optionsService.rawOptions.windowsMode&&(e=!0),e?this._enableWindowsWrappingHeuristics():this._windowsWrappingHeuristics.clear()}_enableWindowsWrappingHeuristics(){if(!this._windowsWrappingHeuristics.value){let e=[];e.push(this.onLineFeed(m.updateWindowsModeWrappedState.bind(null,this._bufferService))),e.push(this.registerCsiHandler({final:`H`},(()=>((0,m.updateWindowsModeWrappedState)(this._bufferService),!1)))),this._windowsWrappingHeuristics.value=(0,r.toDisposable)((()=>{for(let t of e)t.dispose()}))}}}t.CoreTerminal=y},8460:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.runAndSubscribe=t.forwardEvent=t.EventEmitter=void 0,t.EventEmitter=class{constructor(){this._listeners=[],this._disposed=!1}get event(){return this._event||=e=>(this._listeners.push(e),{dispose:()=>{if(!this._disposed){for(let t=0;tt.fire(e)))},t.runAndSubscribe=function(e,t){return t(void 0),e((e=>t(e)))}},5435:function(e,t,n){var r=this&&this.__decorate||function(e,t,n,r){var i,a=arguments.length,o=a<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r;if(typeof Reflect==`object`&&typeof Reflect.decorate==`function`)o=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(i=e[s])&&(o=(a<3?i(o):a>3?i(t,n,o):i(t,n))||o);return a>3&&o&&Object.defineProperty(t,n,o),o},i=this&&this.__param||function(e,t){return function(n,r){t(n,r,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.InputHandler=t.WindowsOptionsReportType=void 0;let a=n(2584),o=n(7116),s=n(2015),c=n(844),l=n(482),u=n(8437),d=n(8460),f=n(643),p=n(511),m=n(3734),h=n(2585),g=n(1480),_=n(6242),v=n(6351),y=n(5941),b={"(":0,")":1,"*":2,"+":3,"-":1,".":2},x=131072;function S(e,t){if(e>24)return t.setWinLines||!1;switch(e){case 1:return!!t.restoreWin;case 2:return!!t.minimizeWin;case 3:return!!t.setWinPosition;case 4:return!!t.setWinSizePixels;case 5:return!!t.raiseWin;case 6:return!!t.lowerWin;case 7:return!!t.refreshWin;case 8:return!!t.setWinSizeChars;case 9:return!!t.maximizeWin;case 10:return!!t.fullscreenWin;case 11:return!!t.getWinState;case 13:return!!t.getWinPosition;case 14:return!!t.getWinSizePixels;case 15:return!!t.getScreenSizePixels;case 16:return!!t.getCellSizePixels;case 18:return!!t.getWinSizeChars;case 19:return!!t.getScreenSizeChars;case 20:return!!t.getIconTitle;case 21:return!!t.getWinTitle;case 22:return!!t.pushTitle;case 23:return!!t.popTitle;case 24:return!!t.setWinLines}return!1}var C;(function(e){e[e.GET_WIN_SIZE_PIXELS=0]=`GET_WIN_SIZE_PIXELS`,e[e.GET_CELL_SIZE_PIXELS=1]=`GET_CELL_SIZE_PIXELS`})(C||(t.WindowsOptionsReportType=C={}));let w=0;class T extends c.Disposable{getAttrData(){return this._curAttrData}constructor(e,t,n,r,i,c,f,m,h=new s.EscapeSequenceParser){super(),this._bufferService=e,this._charsetService=t,this._coreService=n,this._logService=r,this._optionsService=i,this._oscLinkService=c,this._coreMouseService=f,this._unicodeService=m,this._parser=h,this._parseBuffer=new Uint32Array(4096),this._stringDecoder=new l.StringToUtf32,this._utf8Decoder=new l.Utf8ToUtf32,this._workCell=new p.CellData,this._windowTitle=``,this._iconName=``,this._windowTitleStack=[],this._iconNameStack=[],this._curAttrData=u.DEFAULT_ATTR_DATA.clone(),this._eraseAttrDataInternal=u.DEFAULT_ATTR_DATA.clone(),this._onRequestBell=this.register(new d.EventEmitter),this.onRequestBell=this._onRequestBell.event,this._onRequestRefreshRows=this.register(new d.EventEmitter),this.onRequestRefreshRows=this._onRequestRefreshRows.event,this._onRequestReset=this.register(new d.EventEmitter),this.onRequestReset=this._onRequestReset.event,this._onRequestSendFocus=this.register(new d.EventEmitter),this.onRequestSendFocus=this._onRequestSendFocus.event,this._onRequestSyncScrollBar=this.register(new d.EventEmitter),this.onRequestSyncScrollBar=this._onRequestSyncScrollBar.event,this._onRequestWindowsOptionsReport=this.register(new d.EventEmitter),this.onRequestWindowsOptionsReport=this._onRequestWindowsOptionsReport.event,this._onA11yChar=this.register(new d.EventEmitter),this.onA11yChar=this._onA11yChar.event,this._onA11yTab=this.register(new d.EventEmitter),this.onA11yTab=this._onA11yTab.event,this._onCursorMove=this.register(new d.EventEmitter),this.onCursorMove=this._onCursorMove.event,this._onLineFeed=this.register(new d.EventEmitter),this.onLineFeed=this._onLineFeed.event,this._onScroll=this.register(new d.EventEmitter),this.onScroll=this._onScroll.event,this._onTitleChange=this.register(new d.EventEmitter),this.onTitleChange=this._onTitleChange.event,this._onColor=this.register(new d.EventEmitter),this.onColor=this._onColor.event,this._parseStack={paused:!1,cursorStartX:0,cursorStartY:0,decodedLength:0,position:0},this._specialColors=[256,257,258],this.register(this._parser),this._dirtyRowTracker=new E(this._bufferService),this._activeBuffer=this._bufferService.buffer,this.register(this._bufferService.buffers.onBufferActivate((e=>this._activeBuffer=e.activeBuffer))),this._parser.setCsiHandlerFallback(((e,t)=>{this._logService.debug(`Unknown CSI code: `,{identifier:this._parser.identToString(e),params:t.toArray()})})),this._parser.setEscHandlerFallback((e=>{this._logService.debug(`Unknown ESC code: `,{identifier:this._parser.identToString(e)})})),this._parser.setExecuteHandlerFallback((e=>{this._logService.debug(`Unknown EXECUTE code: `,{code:e})})),this._parser.setOscHandlerFallback(((e,t,n)=>{this._logService.debug(`Unknown OSC code: `,{identifier:e,action:t,data:n})})),this._parser.setDcsHandlerFallback(((e,t,n)=>{t===`HOOK`&&(n=n.toArray()),this._logService.debug(`Unknown DCS code: `,{identifier:this._parser.identToString(e),action:t,payload:n})})),this._parser.setPrintHandler(((e,t,n)=>this.print(e,t,n))),this._parser.registerCsiHandler({final:`@`},(e=>this.insertChars(e))),this._parser.registerCsiHandler({intermediates:` `,final:`@`},(e=>this.scrollLeft(e))),this._parser.registerCsiHandler({final:`A`},(e=>this.cursorUp(e))),this._parser.registerCsiHandler({intermediates:` `,final:`A`},(e=>this.scrollRight(e))),this._parser.registerCsiHandler({final:`B`},(e=>this.cursorDown(e))),this._parser.registerCsiHandler({final:`C`},(e=>this.cursorForward(e))),this._parser.registerCsiHandler({final:`D`},(e=>this.cursorBackward(e))),this._parser.registerCsiHandler({final:`E`},(e=>this.cursorNextLine(e))),this._parser.registerCsiHandler({final:`F`},(e=>this.cursorPrecedingLine(e))),this._parser.registerCsiHandler({final:`G`},(e=>this.cursorCharAbsolute(e))),this._parser.registerCsiHandler({final:`H`},(e=>this.cursorPosition(e))),this._parser.registerCsiHandler({final:`I`},(e=>this.cursorForwardTab(e))),this._parser.registerCsiHandler({final:`J`},(e=>this.eraseInDisplay(e,!1))),this._parser.registerCsiHandler({prefix:`?`,final:`J`},(e=>this.eraseInDisplay(e,!0))),this._parser.registerCsiHandler({final:`K`},(e=>this.eraseInLine(e,!1))),this._parser.registerCsiHandler({prefix:`?`,final:`K`},(e=>this.eraseInLine(e,!0))),this._parser.registerCsiHandler({final:`L`},(e=>this.insertLines(e))),this._parser.registerCsiHandler({final:`M`},(e=>this.deleteLines(e))),this._parser.registerCsiHandler({final:`P`},(e=>this.deleteChars(e))),this._parser.registerCsiHandler({final:`S`},(e=>this.scrollUp(e))),this._parser.registerCsiHandler({final:`T`},(e=>this.scrollDown(e))),this._parser.registerCsiHandler({final:`X`},(e=>this.eraseChars(e))),this._parser.registerCsiHandler({final:`Z`},(e=>this.cursorBackwardTab(e))),this._parser.registerCsiHandler({final:"`"},(e=>this.charPosAbsolute(e))),this._parser.registerCsiHandler({final:`a`},(e=>this.hPositionRelative(e))),this._parser.registerCsiHandler({final:`b`},(e=>this.repeatPrecedingCharacter(e))),this._parser.registerCsiHandler({final:`c`},(e=>this.sendDeviceAttributesPrimary(e))),this._parser.registerCsiHandler({prefix:`>`,final:`c`},(e=>this.sendDeviceAttributesSecondary(e))),this._parser.registerCsiHandler({final:`d`},(e=>this.linePosAbsolute(e))),this._parser.registerCsiHandler({final:`e`},(e=>this.vPositionRelative(e))),this._parser.registerCsiHandler({final:`f`},(e=>this.hVPosition(e))),this._parser.registerCsiHandler({final:`g`},(e=>this.tabClear(e))),this._parser.registerCsiHandler({final:`h`},(e=>this.setMode(e))),this._parser.registerCsiHandler({prefix:`?`,final:`h`},(e=>this.setModePrivate(e))),this._parser.registerCsiHandler({final:`l`},(e=>this.resetMode(e))),this._parser.registerCsiHandler({prefix:`?`,final:`l`},(e=>this.resetModePrivate(e))),this._parser.registerCsiHandler({final:`m`},(e=>this.charAttributes(e))),this._parser.registerCsiHandler({final:`n`},(e=>this.deviceStatus(e))),this._parser.registerCsiHandler({prefix:`?`,final:`n`},(e=>this.deviceStatusPrivate(e))),this._parser.registerCsiHandler({intermediates:`!`,final:`p`},(e=>this.softReset(e))),this._parser.registerCsiHandler({intermediates:` `,final:`q`},(e=>this.setCursorStyle(e))),this._parser.registerCsiHandler({final:`r`},(e=>this.setScrollRegion(e))),this._parser.registerCsiHandler({final:`s`},(e=>this.saveCursor(e))),this._parser.registerCsiHandler({final:`t`},(e=>this.windowOptions(e))),this._parser.registerCsiHandler({final:`u`},(e=>this.restoreCursor(e))),this._parser.registerCsiHandler({intermediates:`'`,final:`}`},(e=>this.insertColumns(e))),this._parser.registerCsiHandler({intermediates:`'`,final:`~`},(e=>this.deleteColumns(e))),this._parser.registerCsiHandler({intermediates:`"`,final:`q`},(e=>this.selectProtected(e))),this._parser.registerCsiHandler({intermediates:`$`,final:`p`},(e=>this.requestMode(e,!0))),this._parser.registerCsiHandler({prefix:`?`,intermediates:`$`,final:`p`},(e=>this.requestMode(e,!1))),this._parser.setExecuteHandler(a.C0.BEL,(()=>this.bell())),this._parser.setExecuteHandler(a.C0.LF,(()=>this.lineFeed())),this._parser.setExecuteHandler(a.C0.VT,(()=>this.lineFeed())),this._parser.setExecuteHandler(a.C0.FF,(()=>this.lineFeed())),this._parser.setExecuteHandler(a.C0.CR,(()=>this.carriageReturn())),this._parser.setExecuteHandler(a.C0.BS,(()=>this.backspace())),this._parser.setExecuteHandler(a.C0.HT,(()=>this.tab())),this._parser.setExecuteHandler(a.C0.SO,(()=>this.shiftOut())),this._parser.setExecuteHandler(a.C0.SI,(()=>this.shiftIn())),this._parser.setExecuteHandler(a.C1.IND,(()=>this.index())),this._parser.setExecuteHandler(a.C1.NEL,(()=>this.nextLine())),this._parser.setExecuteHandler(a.C1.HTS,(()=>this.tabSet())),this._parser.registerOscHandler(0,new _.OscHandler((e=>(this.setTitle(e),this.setIconName(e),!0)))),this._parser.registerOscHandler(1,new _.OscHandler((e=>this.setIconName(e)))),this._parser.registerOscHandler(2,new _.OscHandler((e=>this.setTitle(e)))),this._parser.registerOscHandler(4,new _.OscHandler((e=>this.setOrReportIndexedColor(e)))),this._parser.registerOscHandler(8,new _.OscHandler((e=>this.setHyperlink(e)))),this._parser.registerOscHandler(10,new _.OscHandler((e=>this.setOrReportFgColor(e)))),this._parser.registerOscHandler(11,new _.OscHandler((e=>this.setOrReportBgColor(e)))),this._parser.registerOscHandler(12,new _.OscHandler((e=>this.setOrReportCursorColor(e)))),this._parser.registerOscHandler(104,new _.OscHandler((e=>this.restoreIndexedColor(e)))),this._parser.registerOscHandler(110,new _.OscHandler((e=>this.restoreFgColor(e)))),this._parser.registerOscHandler(111,new _.OscHandler((e=>this.restoreBgColor(e)))),this._parser.registerOscHandler(112,new _.OscHandler((e=>this.restoreCursorColor(e)))),this._parser.registerEscHandler({final:`7`},(()=>this.saveCursor())),this._parser.registerEscHandler({final:`8`},(()=>this.restoreCursor())),this._parser.registerEscHandler({final:`D`},(()=>this.index())),this._parser.registerEscHandler({final:`E`},(()=>this.nextLine())),this._parser.registerEscHandler({final:`H`},(()=>this.tabSet())),this._parser.registerEscHandler({final:`M`},(()=>this.reverseIndex())),this._parser.registerEscHandler({final:`=`},(()=>this.keypadApplicationMode())),this._parser.registerEscHandler({final:`>`},(()=>this.keypadNumericMode())),this._parser.registerEscHandler({final:`c`},(()=>this.fullReset())),this._parser.registerEscHandler({final:`n`},(()=>this.setgLevel(2))),this._parser.registerEscHandler({final:`o`},(()=>this.setgLevel(3))),this._parser.registerEscHandler({final:`|`},(()=>this.setgLevel(3))),this._parser.registerEscHandler({final:`}`},(()=>this.setgLevel(2))),this._parser.registerEscHandler({final:`~`},(()=>this.setgLevel(1))),this._parser.registerEscHandler({intermediates:`%`,final:`@`},(()=>this.selectDefaultCharset())),this._parser.registerEscHandler({intermediates:`%`,final:`G`},(()=>this.selectDefaultCharset()));for(let e in o.CHARSETS)this._parser.registerEscHandler({intermediates:`(`,final:e},(()=>this.selectCharset(`(`+e))),this._parser.registerEscHandler({intermediates:`)`,final:e},(()=>this.selectCharset(`)`+e))),this._parser.registerEscHandler({intermediates:`*`,final:e},(()=>this.selectCharset(`*`+e))),this._parser.registerEscHandler({intermediates:`+`,final:e},(()=>this.selectCharset(`+`+e))),this._parser.registerEscHandler({intermediates:`-`,final:e},(()=>this.selectCharset(`-`+e))),this._parser.registerEscHandler({intermediates:`.`,final:e},(()=>this.selectCharset(`.`+e))),this._parser.registerEscHandler({intermediates:`/`,final:e},(()=>this.selectCharset(`/`+e)));this._parser.registerEscHandler({intermediates:`#`,final:`8`},(()=>this.screenAlignmentPattern())),this._parser.setErrorHandler((e=>(this._logService.error(`Parsing error: `,e),e))),this._parser.registerDcsHandler({intermediates:`$`,final:`q`},new v.DcsHandler(((e,t)=>this.requestStatusString(e,t))))}_preserveStack(e,t,n,r){this._parseStack.paused=!0,this._parseStack.cursorStartX=e,this._parseStack.cursorStartY=t,this._parseStack.decodedLength=n,this._parseStack.position=r}_logSlowResolvingAsync(e){this._logService.logLevel<=h.LogLevelEnum.WARN&&Promise.race([e,new Promise(((e,t)=>setTimeout((()=>t(`#SLOW_TIMEOUT`)),5e3)))]).catch((e=>{if(e!==`#SLOW_TIMEOUT`)throw e;console.warn(`async parser handler taking longer than 5000 ms`)}))}_getCurrentLinkId(){return this._curAttrData.extended.urlId}parse(e,t){let n,r=this._activeBuffer.x,i=this._activeBuffer.y,a=0,o=this._parseStack.paused;if(o){if(n=this._parser.parse(this._parseBuffer,this._parseStack.decodedLength,t))return this._logSlowResolvingAsync(n),n;r=this._parseStack.cursorStartX,i=this._parseStack.cursorStartY,this._parseStack.paused=!1,e.length>x&&(a=this._parseStack.position+x)}if(this._logService.logLevel<=h.LogLevelEnum.DEBUG&&this._logService.debug(`parsing data`+(typeof e==`string`?` "${e}"`:` "${Array.prototype.map.call(e,(e=>String.fromCharCode(e))).join(``)}"`),typeof e==`string`?e.split(``).map((e=>e.charCodeAt(0))):e),this._parseBuffer.lengthx)for(let t=a;t0&&m.getWidth(this._activeBuffer.x-1)===2&&m.setCellFromCodepoint(this._activeBuffer.x-1,0,1,p);let h=this._parser.precedingJoinState;for(let _=t;_s){if(c){let e=m,t=this._activeBuffer.x-v;for(this._activeBuffer.x=v,this._activeBuffer.y++,this._activeBuffer.y===this._activeBuffer.scrollBottom+1?(this._activeBuffer.y--,this._bufferService.scroll(this._eraseAttrData(),!0)):(this._activeBuffer.y>=this._bufferService.rows&&(this._activeBuffer.y=this._bufferService.rows-1),this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y).isWrapped=!0),m=this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y),v>0&&m instanceof u.BufferLine&&m.copyCellsFrom(e,t,0,v,!1);t=0;)m.setCellFromCodepoint(this._activeBuffer.x++,0,0,p)}else if(d&&(m.insertCells(this._activeBuffer.x,i-v,this._activeBuffer.getNullCell(p)),m.getWidth(s-1)===2&&m.setCellFromCodepoint(s-1,f.NULL_CELL_CODE,f.NULL_CELL_WIDTH,p)),m.setCellFromCodepoint(this._activeBuffer.x++,r,i,p),i>0)for(;--i;)m.setCellFromCodepoint(this._activeBuffer.x++,0,0,p)}this._parser.precedingJoinState=h,this._activeBuffer.x0&&m.getWidth(this._activeBuffer.x)===0&&!m.hasContent(this._activeBuffer.x)&&m.setCellFromCodepoint(this._activeBuffer.x,0,1,p),this._dirtyRowTracker.markDirty(this._activeBuffer.y)}registerCsiHandler(e,t){return e.final!==`t`||e.prefix||e.intermediates?this._parser.registerCsiHandler(e,t):this._parser.registerCsiHandler(e,(e=>!S(e.params[0],this._optionsService.rawOptions.windowOptions)||t(e)))}registerDcsHandler(e,t){return this._parser.registerDcsHandler(e,new v.DcsHandler(t))}registerEscHandler(e,t){return this._parser.registerEscHandler(e,t)}registerOscHandler(e,t){return this._parser.registerOscHandler(e,new _.OscHandler(t))}bell(){return this._onRequestBell.fire(),!0}lineFeed(){return this._dirtyRowTracker.markDirty(this._activeBuffer.y),this._optionsService.rawOptions.convertEol&&(this._activeBuffer.x=0),this._activeBuffer.y++,this._activeBuffer.y===this._activeBuffer.scrollBottom+1?(this._activeBuffer.y--,this._bufferService.scroll(this._eraseAttrData())):this._activeBuffer.y>=this._bufferService.rows?this._activeBuffer.y=this._bufferService.rows-1:this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y).isWrapped=!1,this._activeBuffer.x>=this._bufferService.cols&&this._activeBuffer.x--,this._dirtyRowTracker.markDirty(this._activeBuffer.y),this._onLineFeed.fire(),!0}carriageReturn(){return this._activeBuffer.x=0,!0}backspace(){if(!this._coreService.decPrivateModes.reverseWraparound)return this._restrictCursor(),this._activeBuffer.x>0&&this._activeBuffer.x--,!0;if(this._restrictCursor(this._bufferService.cols),this._activeBuffer.x>0)this._activeBuffer.x--;else if(this._activeBuffer.x===0&&this._activeBuffer.y>this._activeBuffer.scrollTop&&this._activeBuffer.y<=this._activeBuffer.scrollBottom&&this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y)?.isWrapped){this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y).isWrapped=!1,this._activeBuffer.y--,this._activeBuffer.x=this._bufferService.cols-1;let e=this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y);e.hasWidth(this._activeBuffer.x)&&!e.hasContent(this._activeBuffer.x)&&this._activeBuffer.x--}return this._restrictCursor(),!0}tab(){if(this._activeBuffer.x>=this._bufferService.cols)return!0;let e=this._activeBuffer.x;return this._activeBuffer.x=this._activeBuffer.nextStop(),this._optionsService.rawOptions.screenReaderMode&&this._onA11yTab.fire(this._activeBuffer.x-e),!0}shiftOut(){return this._charsetService.setgLevel(1),!0}shiftIn(){return this._charsetService.setgLevel(0),!0}_restrictCursor(e=this._bufferService.cols-1){this._activeBuffer.x=Math.min(e,Math.max(0,this._activeBuffer.x)),this._activeBuffer.y=this._coreService.decPrivateModes.origin?Math.min(this._activeBuffer.scrollBottom,Math.max(this._activeBuffer.scrollTop,this._activeBuffer.y)):Math.min(this._bufferService.rows-1,Math.max(0,this._activeBuffer.y)),this._dirtyRowTracker.markDirty(this._activeBuffer.y)}_setCursor(e,t){this._dirtyRowTracker.markDirty(this._activeBuffer.y),this._coreService.decPrivateModes.origin?(this._activeBuffer.x=e,this._activeBuffer.y=this._activeBuffer.scrollTop+t):(this._activeBuffer.x=e,this._activeBuffer.y=t),this._restrictCursor(),this._dirtyRowTracker.markDirty(this._activeBuffer.y)}_moveCursor(e,t){this._restrictCursor(),this._setCursor(this._activeBuffer.x+e,this._activeBuffer.y+t)}cursorUp(e){let t=this._activeBuffer.y-this._activeBuffer.scrollTop;return t>=0?this._moveCursor(0,-Math.min(t,e.params[0]||1)):this._moveCursor(0,-(e.params[0]||1)),!0}cursorDown(e){let t=this._activeBuffer.scrollBottom-this._activeBuffer.y;return t>=0?this._moveCursor(0,Math.min(t,e.params[0]||1)):this._moveCursor(0,e.params[0]||1),!0}cursorForward(e){return this._moveCursor(e.params[0]||1,0),!0}cursorBackward(e){return this._moveCursor(-(e.params[0]||1),0),!0}cursorNextLine(e){return this.cursorDown(e),this._activeBuffer.x=0,!0}cursorPrecedingLine(e){return this.cursorUp(e),this._activeBuffer.x=0,!0}cursorCharAbsolute(e){return this._setCursor((e.params[0]||1)-1,this._activeBuffer.y),!0}cursorPosition(e){return this._setCursor(e.length>=2?(e.params[1]||1)-1:0,(e.params[0]||1)-1),!0}charPosAbsolute(e){return this._setCursor((e.params[0]||1)-1,this._activeBuffer.y),!0}hPositionRelative(e){return this._moveCursor(e.params[0]||1,0),!0}linePosAbsolute(e){return this._setCursor(this._activeBuffer.x,(e.params[0]||1)-1),!0}vPositionRelative(e){return this._moveCursor(0,e.params[0]||1),!0}hVPosition(e){return this.cursorPosition(e),!0}tabClear(e){let t=e.params[0];return t===0?delete this._activeBuffer.tabs[this._activeBuffer.x]:t===3&&(this._activeBuffer.tabs={}),!0}cursorForwardTab(e){if(this._activeBuffer.x>=this._bufferService.cols)return!0;let t=e.params[0]||1;for(;t--;)this._activeBuffer.x=this._activeBuffer.nextStop();return!0}cursorBackwardTab(e){if(this._activeBuffer.x>=this._bufferService.cols)return!0;let t=e.params[0]||1;for(;t--;)this._activeBuffer.x=this._activeBuffer.prevStop();return!0}selectProtected(e){let t=e.params[0];return t===1&&(this._curAttrData.bg|=536870912),t!==2&&t!==0||(this._curAttrData.bg&=-536870913),!0}_eraseInBufferLine(e,t,n,r=!1,i=!1){let a=this._activeBuffer.lines.get(this._activeBuffer.ybase+e);a.replaceCells(t,n,this._activeBuffer.getNullCell(this._eraseAttrData()),i),r&&(a.isWrapped=!1)}_resetBufferLine(e,t=!1){let n=this._activeBuffer.lines.get(this._activeBuffer.ybase+e);n&&(n.fill(this._activeBuffer.getNullCell(this._eraseAttrData()),t),this._bufferService.buffer.clearMarkers(this._activeBuffer.ybase+e),n.isWrapped=!1)}eraseInDisplay(e,t=!1){let n;switch(this._restrictCursor(this._bufferService.cols),e.params[0]){case 0:for(n=this._activeBuffer.y,this._dirtyRowTracker.markDirty(n),this._eraseInBufferLine(n++,this._activeBuffer.x,this._bufferService.cols,this._activeBuffer.x===0,t);n=this._bufferService.cols&&(this._activeBuffer.lines.get(n+1).isWrapped=!1);n--;)this._resetBufferLine(n,t);this._dirtyRowTracker.markDirty(0);break;case 2:for(n=this._bufferService.rows,this._dirtyRowTracker.markDirty(n-1);n--;)this._resetBufferLine(n,t);this._dirtyRowTracker.markDirty(0);break;case 3:let e=this._activeBuffer.lines.length-this._bufferService.rows;e>0&&(this._activeBuffer.lines.trimStart(e),this._activeBuffer.ybase=Math.max(this._activeBuffer.ybase-e,0),this._activeBuffer.ydisp=Math.max(this._activeBuffer.ydisp-e,0),this._onScroll.fire(0))}return!0}eraseInLine(e,t=!1){switch(this._restrictCursor(this._bufferService.cols),e.params[0]){case 0:this._eraseInBufferLine(this._activeBuffer.y,this._activeBuffer.x,this._bufferService.cols,this._activeBuffer.x===0,t);break;case 1:this._eraseInBufferLine(this._activeBuffer.y,0,this._activeBuffer.x+1,!1,t);break;case 2:this._eraseInBufferLine(this._activeBuffer.y,0,this._bufferService.cols,!0,t)}return this._dirtyRowTracker.markDirty(this._activeBuffer.y),!0}insertLines(e){this._restrictCursor();let t=e.params[0]||1;if(this._activeBuffer.y>this._activeBuffer.scrollBottom||this._activeBuffer.ythis._activeBuffer.scrollBottom||this._activeBuffer.ythis._activeBuffer.scrollBottom||this._activeBuffer.ythis._activeBuffer.scrollBottom||this._activeBuffer.ythis._activeBuffer.scrollBottom||this._activeBuffer.ythis._activeBuffer.scrollBottom||this._activeBuffer.y65535?2:1}let c=s;for(let e=1;e0||(this._is(`xterm`)||this._is(`rxvt-unicode`)||this._is(`screen`)?this._coreService.triggerDataEvent(a.C0.ESC+`[?1;2c`):this._is(`linux`)&&this._coreService.triggerDataEvent(a.C0.ESC+`[?6c`)),!0}sendDeviceAttributesSecondary(e){return e.params[0]>0||(this._is(`xterm`)?this._coreService.triggerDataEvent(a.C0.ESC+`[>0;276;0c`):this._is(`rxvt-unicode`)?this._coreService.triggerDataEvent(a.C0.ESC+`[>85;95;0c`):this._is(`linux`)?this._coreService.triggerDataEvent(e.params[0]+`c`):this._is(`screen`)&&this._coreService.triggerDataEvent(a.C0.ESC+`[>83;40003;0c`)),!0}_is(e){return(this._optionsService.rawOptions.termName+``).indexOf(e)===0}setMode(e){for(let t=0;te?1:2,p=e.params[0];return m=p,h=t?p===2?4:p===4?f(o.modes.insertMode):p===12?3:p===20?f(d.convertEol):0:p===1?f(n.applicationCursorKeys):p===3?d.windowOptions.setWinLines?c===80?2:+(c===132):0:p===6?f(n.origin):p===7?f(n.wraparound):p===8?3:p===9?f(r===`X10`):p===12?f(d.cursorBlink):p===25?f(!o.isCursorHidden):p===45?f(n.reverseWraparound):p===66?f(n.applicationKeypad):p===67?4:p===1e3?f(r===`VT200`):p===1002?f(r===`DRAG`):p===1003?f(r===`ANY`):p===1004?f(n.sendFocus):p===1005?4:p===1006?f(i===`SGR`):p===1015?4:p===1016?f(i===`SGR_PIXELS`):p===1048?1:p===47||p===1047||p===1049?f(l===u):p===2004?f(n.bracketedPasteMode):0,o.triggerDataEvent(`${a.C0.ESC}[${t?``:`?`}${m};${h}$y`),!0;var m,h}_updateAttrColor(e,t,n,r,i){return t===2?(e|=50331648,e&=-16777216,e|=m.AttributeData.fromColorRGB([n,r,i])):t===5&&(e&=-50331904,e|=33554432|255&n),e}_extractColor(e,t,n){let r=[0,0,-1,0,0,0],i=0,a=0;do{if(r[a+i]=e.params[t+a],e.hasSubParams(t+a)){let n=e.getSubParams(t+a),o=0;do r[1]===5&&(i=1),r[a+o+1+i]=n[o];while(++o=2||r[1]===2&&a+i>=5)break;r[1]&&(i=1)}while(++a+t5)&&(e=1),t.extended.underlineStyle=e,t.fg|=268435456,e===0&&(t.fg&=-268435457),t.updateExtended()}_processSGR0(e){e.fg=u.DEFAULT_ATTR_DATA.fg,e.bg=u.DEFAULT_ATTR_DATA.bg,e.extended=e.extended.clone(),e.extended.underlineStyle=0,e.extended.underlineColor&=-67108864,e.updateExtended()}charAttributes(e){if(e.length===1&&e.params[0]===0)return this._processSGR0(this._curAttrData),!0;let t=e.length,n,r=this._curAttrData;for(let i=0;i=30&&n<=37?(r.fg&=-50331904,r.fg|=16777216|n-30):n>=40&&n<=47?(r.bg&=-50331904,r.bg|=16777216|n-40):n>=90&&n<=97?(r.fg&=-50331904,r.fg|=16777224|n-90):n>=100&&n<=107?(r.bg&=-50331904,r.bg|=16777224|n-100):n===0?this._processSGR0(r):n===1?r.fg|=134217728:n===3?r.bg|=67108864:n===4?(r.fg|=268435456,this._processUnderline(e.hasSubParams(i)?e.getSubParams(i)[0]:1,r)):n===5?r.fg|=536870912:n===7?r.fg|=67108864:n===8?r.fg|=1073741824:n===9?r.fg|=2147483648:n===2?r.bg|=134217728:n===21?this._processUnderline(2,r):n===22?(r.fg&=-134217729,r.bg&=-134217729):n===23?r.bg&=-67108865:n===24?(r.fg&=-268435457,this._processUnderline(0,r)):n===25?r.fg&=-536870913:n===27?r.fg&=-67108865:n===28?r.fg&=-1073741825:n===29?r.fg&=2147483647:n===39?(r.fg&=-67108864,r.fg|=16777215&u.DEFAULT_ATTR_DATA.fg):n===49?(r.bg&=-67108864,r.bg|=16777215&u.DEFAULT_ATTR_DATA.bg):n===38||n===48||n===58?i+=this._extractColor(e,i,r):n===53?r.bg|=1073741824:n===55?r.bg&=-1073741825:n===59?(r.extended=r.extended.clone(),r.extended.underlineColor=-1,r.updateExtended()):n===100?(r.fg&=-67108864,r.fg|=16777215&u.DEFAULT_ATTR_DATA.fg,r.bg&=-67108864,r.bg|=16777215&u.DEFAULT_ATTR_DATA.bg):this._logService.debug(`Unknown SGR attribute: %d.`,n);return!0}deviceStatus(e){switch(e.params[0]){case 5:this._coreService.triggerDataEvent(`${a.C0.ESC}[0n`);break;case 6:let e=this._activeBuffer.y+1,t=this._activeBuffer.x+1;this._coreService.triggerDataEvent(`${a.C0.ESC}[${e};${t}R`)}return!0}deviceStatusPrivate(e){if(e.params[0]===6){let e=this._activeBuffer.y+1,t=this._activeBuffer.x+1;this._coreService.triggerDataEvent(`${a.C0.ESC}[?${e};${t}R`)}return!0}softReset(e){return this._coreService.isCursorHidden=!1,this._onRequestSyncScrollBar.fire(),this._activeBuffer.scrollTop=0,this._activeBuffer.scrollBottom=this._bufferService.rows-1,this._curAttrData=u.DEFAULT_ATTR_DATA.clone(),this._coreService.reset(),this._charsetService.reset(),this._activeBuffer.savedX=0,this._activeBuffer.savedY=this._activeBuffer.ybase,this._activeBuffer.savedCurAttrData.fg=this._curAttrData.fg,this._activeBuffer.savedCurAttrData.bg=this._curAttrData.bg,this._activeBuffer.savedCharset=this._charsetService.charset,this._coreService.decPrivateModes.origin=!1,!0}setCursorStyle(e){let t=e.params[0]||1;switch(t){case 1:case 2:this._optionsService.options.cursorStyle=`block`;break;case 3:case 4:this._optionsService.options.cursorStyle=`underline`;break;case 5:case 6:this._optionsService.options.cursorStyle=`bar`}let n=t%2==1;return this._optionsService.options.cursorBlink=n,!0}setScrollRegion(e){let t=e.params[0]||1,n;return(e.length<2||(n=e.params[1])>this._bufferService.rows||n===0)&&(n=this._bufferService.rows),n>t&&(this._activeBuffer.scrollTop=t-1,this._activeBuffer.scrollBottom=n-1,this._setCursor(0,0)),!0}windowOptions(e){if(!S(e.params[0],this._optionsService.rawOptions.windowOptions))return!0;let t=e.length>1?e.params[1]:0;switch(e.params[0]){case 14:t!==2&&this._onRequestWindowsOptionsReport.fire(C.GET_WIN_SIZE_PIXELS);break;case 16:this._onRequestWindowsOptionsReport.fire(C.GET_CELL_SIZE_PIXELS);break;case 18:this._bufferService&&this._coreService.triggerDataEvent(`${a.C0.ESC}[8;${this._bufferService.rows};${this._bufferService.cols}t`);break;case 22:t!==0&&t!==2||(this._windowTitleStack.push(this._windowTitle),this._windowTitleStack.length>10&&this._windowTitleStack.shift()),t!==0&&t!==1||(this._iconNameStack.push(this._iconName),this._iconNameStack.length>10&&this._iconNameStack.shift());break;case 23:t!==0&&t!==2||this._windowTitleStack.length&&this.setTitle(this._windowTitleStack.pop()),t!==0&&t!==1||this._iconNameStack.length&&this.setIconName(this._iconNameStack.pop())}return!0}saveCursor(e){return this._activeBuffer.savedX=this._activeBuffer.x,this._activeBuffer.savedY=this._activeBuffer.ybase+this._activeBuffer.y,this._activeBuffer.savedCurAttrData.fg=this._curAttrData.fg,this._activeBuffer.savedCurAttrData.bg=this._curAttrData.bg,this._activeBuffer.savedCharset=this._charsetService.charset,!0}restoreCursor(e){return this._activeBuffer.x=this._activeBuffer.savedX||0,this._activeBuffer.y=Math.max(this._activeBuffer.savedY-this._activeBuffer.ybase,0),this._curAttrData.fg=this._activeBuffer.savedCurAttrData.fg,this._curAttrData.bg=this._activeBuffer.savedCurAttrData.bg,this._charsetService.charset=this._savedCharset,this._activeBuffer.savedCharset&&(this._charsetService.charset=this._activeBuffer.savedCharset),this._restrictCursor(),!0}setTitle(e){return this._windowTitle=e,this._onTitleChange.fire(e),!0}setIconName(e){return this._iconName=e,!0}setOrReportIndexedColor(e){let t=[],n=e.split(`;`);for(;n.length>1;){let e=n.shift(),r=n.shift();if(/^\d+$/.exec(e)){let n=parseInt(e);if(D(n))if(r===`?`)t.push({type:0,index:n});else{let e=(0,y.parseColor)(r);e&&t.push({type:1,index:n,color:e})}}}return t.length&&this._onColor.fire(t),!0}setHyperlink(e){let t=e.split(`;`);return!(t.length<2)&&(t[1]?this._createHyperlink(t[0],t[1]):!t[0]&&this._finishHyperlink())}_createHyperlink(e,t){this._getCurrentLinkId()&&this._finishHyperlink();let n=e.split(`:`),r,i=n.findIndex((e=>e.startsWith(`id=`)));return i!==-1&&(r=n[i].slice(3)||void 0),this._curAttrData.extended=this._curAttrData.extended.clone(),this._curAttrData.extended.urlId=this._oscLinkService.registerLink({id:r,uri:t}),this._curAttrData.updateExtended(),!0}_finishHyperlink(){return this._curAttrData.extended=this._curAttrData.extended.clone(),this._curAttrData.extended.urlId=0,this._curAttrData.updateExtended(),!0}_setOrReportSpecialColor(e,t){let n=e.split(`;`);for(let e=0;e=this._specialColors.length);++e,++t)if(n[e]===`?`)this._onColor.fire([{type:0,index:this._specialColors[t]}]);else{let r=(0,y.parseColor)(n[e]);r&&this._onColor.fire([{type:1,index:this._specialColors[t],color:r}])}return!0}setOrReportFgColor(e){return this._setOrReportSpecialColor(e,0)}setOrReportBgColor(e){return this._setOrReportSpecialColor(e,1)}setOrReportCursorColor(e){return this._setOrReportSpecialColor(e,2)}restoreIndexedColor(e){if(!e)return this._onColor.fire([{type:2}]),!0;let t=[],n=e.split(`;`);for(let e=0;e=this._bufferService.rows&&(this._activeBuffer.y=this._bufferService.rows-1),this._restrictCursor(),!0}tabSet(){return this._activeBuffer.tabs[this._activeBuffer.x]=!0,!0}reverseIndex(){if(this._restrictCursor(),this._activeBuffer.y===this._activeBuffer.scrollTop){let e=this._activeBuffer.scrollBottom-this._activeBuffer.scrollTop;this._activeBuffer.lines.shiftElements(this._activeBuffer.ybase+this._activeBuffer.y,e,1),this._activeBuffer.lines.set(this._activeBuffer.ybase+this._activeBuffer.y,this._activeBuffer.getBlankLine(this._eraseAttrData())),this._dirtyRowTracker.markRangeDirty(this._activeBuffer.scrollTop,this._activeBuffer.scrollBottom)}else this._activeBuffer.y--,this._restrictCursor();return!0}fullReset(){return this._parser.reset(),this._onRequestReset.fire(),!0}reset(){this._curAttrData=u.DEFAULT_ATTR_DATA.clone(),this._eraseAttrDataInternal=u.DEFAULT_ATTR_DATA.clone()}_eraseAttrData(){return this._eraseAttrDataInternal.bg&=-67108864,this._eraseAttrDataInternal.bg|=67108863&this._curAttrData.bg,this._eraseAttrDataInternal}setgLevel(e){return this._charsetService.setgLevel(e),!0}screenAlignmentPattern(){let e=new p.CellData;e.content=4194373,e.fg=this._curAttrData.fg,e.bg=this._curAttrData.bg,this._setCursor(0,0);for(let t=0;t(this._coreService.triggerDataEvent(`${a.C0.ESC}${e}${a.C0.ESC}\\`),!0))(e===`"q`?`P1$r${+!!this._curAttrData.isProtected()}"q`:e===`"p`?`P1$r61;1"p`:e===`r`?`P1$r${n.scrollTop+1};${n.scrollBottom+1}r`:e===`m`?`P1$r0m`:e===` q`?`P1$r${{block:2,underline:4,bar:6}[r.cursorStyle]-+!!r.cursorBlink} q`:`P0$r`)}markRangeDirty(e,t){this._dirtyRowTracker.markRangeDirty(e,t)}}t.InputHandler=T;let E=class{constructor(e){this._bufferService=e,this.clearRange()}clearRange(){this.start=this._bufferService.buffer.y,this.end=this._bufferService.buffer.y}markDirty(e){ethis.end&&(this.end=e)}markRangeDirty(e,t){e>t&&(w=e,e=t,t=w),ethis.end&&(this.end=t)}markAllDirty(){this.markRangeDirty(0,this._bufferService.rows-1)}};function D(e){return 0<=e&&e<256}E=r([i(0,h.IBufferService)],E)},844:(e,t)=>{function n(e){for(let t of e)t.dispose();e.length=0}Object.defineProperty(t,"__esModule",{value:!0}),t.getDisposeArrayDisposable=t.disposeArray=t.toDisposable=t.MutableDisposable=t.Disposable=void 0,t.Disposable=class{constructor(){this._disposables=[],this._isDisposed=!1}dispose(){this._isDisposed=!0;for(let e of this._disposables)e.dispose();this._disposables.length=0}register(e){return this._disposables.push(e),e}unregister(e){let t=this._disposables.indexOf(e);t!==-1&&this._disposables.splice(t,1)}},t.MutableDisposable=class{constructor(){this._isDisposed=!1}get value(){return this._isDisposed?void 0:this._value}set value(e){this._isDisposed||e===this._value||(this._value?.dispose(),this._value=e)}clear(){this.value=void 0}dispose(){this._isDisposed=!0,this._value?.dispose(),this._value=void 0}},t.toDisposable=function(e){return{dispose:e}},t.disposeArray=n,t.getDisposeArrayDisposable=function(e){return{dispose:()=>n(e)}}},1505:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.FourKeyMap=t.TwoKeyMap=void 0;class n{constructor(){this._data={}}set(e,t,n){this._data[e]||(this._data[e]={}),this._data[e][t]=n}get(e,t){return this._data[e]?this._data[e][t]:void 0}clear(){this._data={}}}t.TwoKeyMap=n,t.FourKeyMap=class{constructor(){this._data=new n}set(e,t,r,i,a){this._data.get(e,t)||this._data.set(e,t,new n),this._data.get(e,t).set(r,i,a)}get(e,t,n,r){return this._data.get(e,t)?.get(n,r)}clear(){this._data.clear()}}},6114:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.isChromeOS=t.isLinux=t.isWindows=t.isIphone=t.isIpad=t.isMac=t.getSafariVersion=t.isSafari=t.isLegacyEdge=t.isFirefox=t.isNode=void 0,t.isNode=typeof process<`u`&&`title`in process;let n=t.isNode?`node`:navigator.userAgent,r=t.isNode?`node`:navigator.platform;t.isFirefox=n.includes(`Firefox`),t.isLegacyEdge=n.includes(`Edge`),t.isSafari=/^((?!chrome|android).)*safari/i.test(n),t.getSafariVersion=function(){if(!t.isSafari)return 0;let e=n.match(/Version\/(\d+)/);return e===null||e.length<2?0:parseInt(e[1])},t.isMac=[`Macintosh`,`MacIntel`,`MacPPC`,`Mac68K`].includes(r),t.isIpad=r===`iPad`,t.isIphone=r===`iPhone`,t.isWindows=[`Windows`,`Win16`,`Win32`,`WinCE`].includes(r),t.isLinux=r.indexOf(`Linux`)>=0,t.isChromeOS=/\bCrOS\b/.test(n)},6106:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.SortedList=void 0;let n=0;t.SortedList=class{constructor(e){this._getKey=e,this._array=[]}clear(){this._array.length=0}insert(e){this._array.length===0?this._array.push(e):(n=this._search(this._getKey(e)),this._array.splice(n,0,e))}delete(e){if(this._array.length===0)return!1;let t=this._getKey(e);if(t===void 0||(n=this._search(t),n===-1)||this._getKey(this._array[n])!==t)return!1;do if(this._array[n]===e)return this._array.splice(n,1),!0;while(++n=this._array.length)&&this._getKey(this._array[n])===e))do yield this._array[n];while(++n=this._array.length)&&this._getKey(this._array[n])===e))do t(this._array[n]);while(++n=t;){let r=t+n>>1,i=this._getKey(this._array[r]);if(i>e)n=r-1;else{if(!(i0&&this._getKey(this._array[r-1])===e;)r--;return r}t=r+1}}return t}}},7226:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.DebouncedIdleTask=t.IdleTaskQueue=t.PriorityTaskQueue=void 0;let r=n(6114);class i{constructor(){this._tasks=[],this._i=0}enqueue(e){this._tasks.push(e),this._start()}flush(){for(;this._ii)return r-t<-20&&console.warn(`task queue exceeded allotted deadline by ${Math.abs(Math.round(r-t))}ms`),void this._start();r=i}this.clear()}}class a extends i{_requestCallback(e){return setTimeout((()=>e(this._createDeadline(16))))}_cancelCallback(e){clearTimeout(e)}_createDeadline(e){let t=Date.now()+e;return{timeRemaining:()=>Math.max(0,t-Date.now())}}}t.PriorityTaskQueue=a,t.IdleTaskQueue=!r.isNode&&`requestIdleCallback`in window?class extends i{_requestCallback(e){return requestIdleCallback(e)}_cancelCallback(e){cancelIdleCallback(e)}}:a,t.DebouncedIdleTask=class{constructor(){this._queue=new t.IdleTaskQueue}set(e){this._queue.clear(),this._queue.enqueue(e)}flush(){this._queue.flush()}}},9282:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.updateWindowsModeWrappedState=void 0;let r=n(643);t.updateWindowsModeWrappedState=function(e){let t=e.buffer.lines.get(e.buffer.ybase+e.buffer.y-1)?.get(e.cols-1),n=e.buffer.lines.get(e.buffer.ybase+e.buffer.y);n&&t&&(n.isWrapped=t[r.CHAR_DATA_CODE_INDEX]!==r.NULL_CELL_CODE&&t[r.CHAR_DATA_CODE_INDEX]!==r.WHITESPACE_CELL_CODE)}},3734:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ExtendedAttrs=t.AttributeData=void 0;class n{constructor(){this.fg=0,this.bg=0,this.extended=new r}static toColorRGB(e){return[e>>>16&255,e>>>8&255,255&e]}static fromColorRGB(e){return(255&e[0])<<16|(255&e[1])<<8|255&e[2]}clone(){let e=new n;return e.fg=this.fg,e.bg=this.bg,e.extended=this.extended.clone(),e}isInverse(){return 67108864&this.fg}isBold(){return 134217728&this.fg}isUnderline(){return this.hasExtendedAttrs()&&this.extended.underlineStyle!==0?1:268435456&this.fg}isBlink(){return 536870912&this.fg}isInvisible(){return 1073741824&this.fg}isItalic(){return 67108864&this.bg}isDim(){return 134217728&this.bg}isStrikethrough(){return 2147483648&this.fg}isProtected(){return 536870912&this.bg}isOverline(){return 1073741824&this.bg}getFgColorMode(){return 50331648&this.fg}getBgColorMode(){return 50331648&this.bg}isFgRGB(){return(50331648&this.fg)==50331648}isBgRGB(){return(50331648&this.bg)==50331648}isFgPalette(){return(50331648&this.fg)==16777216||(50331648&this.fg)==33554432}isBgPalette(){return(50331648&this.bg)==16777216||(50331648&this.bg)==33554432}isFgDefault(){return(50331648&this.fg)==0}isBgDefault(){return(50331648&this.bg)==0}isAttributeDefault(){return this.fg===0&&this.bg===0}getFgColor(){switch(50331648&this.fg){case 16777216:case 33554432:return 255&this.fg;case 50331648:return 16777215&this.fg;default:return-1}}getBgColor(){switch(50331648&this.bg){case 16777216:case 33554432:return 255&this.bg;case 50331648:return 16777215&this.bg;default:return-1}}hasExtendedAttrs(){return 268435456&this.bg}updateExtended(){this.extended.isEmpty()?this.bg&=-268435457:this.bg|=268435456}getUnderlineColor(){if(268435456&this.bg&&~this.extended.underlineColor)switch(50331648&this.extended.underlineColor){case 16777216:case 33554432:return 255&this.extended.underlineColor;case 50331648:return 16777215&this.extended.underlineColor;default:return this.getFgColor()}return this.getFgColor()}getUnderlineColorMode(){return 268435456&this.bg&&~this.extended.underlineColor?50331648&this.extended.underlineColor:this.getFgColorMode()}isUnderlineColorRGB(){return 268435456&this.bg&&~this.extended.underlineColor?(50331648&this.extended.underlineColor)==50331648:this.isFgRGB()}isUnderlineColorPalette(){return 268435456&this.bg&&~this.extended.underlineColor?(50331648&this.extended.underlineColor)==16777216||(50331648&this.extended.underlineColor)==33554432:this.isFgPalette()}isUnderlineColorDefault(){return 268435456&this.bg&&~this.extended.underlineColor?(50331648&this.extended.underlineColor)==0:this.isFgDefault()}getUnderlineStyle(){return 268435456&this.fg?268435456&this.bg?this.extended.underlineStyle:1:0}getUnderlineVariantOffset(){return this.extended.underlineVariantOffset}}t.AttributeData=n;class r{get ext(){return this._urlId?-469762049&this._ext|this.underlineStyle<<26:this._ext}set ext(e){this._ext=e}get underlineStyle(){return this._urlId?5:(469762048&this._ext)>>26}set underlineStyle(e){this._ext&=-469762049,this._ext|=e<<26&469762048}get underlineColor(){return 67108863&this._ext}set underlineColor(e){this._ext&=-67108864,this._ext|=67108863&e}get urlId(){return this._urlId}set urlId(e){this._urlId=e}get underlineVariantOffset(){let e=(3758096384&this._ext)>>29;return e<0?4294967288^e:e}set underlineVariantOffset(e){this._ext&=536870911,this._ext|=e<<29&3758096384}constructor(e=0,t=0){this._ext=0,this._urlId=0,this._ext=e,this._urlId=t}clone(){return new r(this._ext,this._urlId)}isEmpty(){return this.underlineStyle===0&&this._urlId===0}}t.ExtendedAttrs=r},9092:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Buffer=t.MAX_BUFFER_SIZE=void 0;let r=n(6349),i=n(7226),a=n(3734),o=n(8437),s=n(4634),c=n(511),l=n(643),u=n(4863),d=n(7116);t.MAX_BUFFER_SIZE=4294967295,t.Buffer=class{constructor(e,t,n){this._hasScrollback=e,this._optionsService=t,this._bufferService=n,this.ydisp=0,this.ybase=0,this.y=0,this.x=0,this.tabs={},this.savedY=0,this.savedX=0,this.savedCurAttrData=o.DEFAULT_ATTR_DATA.clone(),this.savedCharset=d.DEFAULT_CHARSET,this.markers=[],this._nullCell=c.CellData.fromCharData([0,l.NULL_CELL_CHAR,l.NULL_CELL_WIDTH,l.NULL_CELL_CODE]),this._whitespaceCell=c.CellData.fromCharData([0,l.WHITESPACE_CELL_CHAR,l.WHITESPACE_CELL_WIDTH,l.WHITESPACE_CELL_CODE]),this._isClearing=!1,this._memoryCleanupQueue=new i.IdleTaskQueue,this._memoryCleanupPosition=0,this._cols=this._bufferService.cols,this._rows=this._bufferService.rows,this.lines=new r.CircularList(this._getCorrectBufferLength(this._rows)),this.scrollTop=0,this.scrollBottom=this._rows-1,this.setupTabStops()}getNullCell(e){return e?(this._nullCell.fg=e.fg,this._nullCell.bg=e.bg,this._nullCell.extended=e.extended):(this._nullCell.fg=0,this._nullCell.bg=0,this._nullCell.extended=new a.ExtendedAttrs),this._nullCell}getWhitespaceCell(e){return e?(this._whitespaceCell.fg=e.fg,this._whitespaceCell.bg=e.bg,this._whitespaceCell.extended=e.extended):(this._whitespaceCell.fg=0,this._whitespaceCell.bg=0,this._whitespaceCell.extended=new a.ExtendedAttrs),this._whitespaceCell}getBlankLine(e,t){return new o.BufferLine(this._bufferService.cols,this.getNullCell(e),t)}get hasScrollback(){return this._hasScrollback&&this.lines.maxLength>this._rows}get isCursorInViewport(){let e=this.ybase+this.y-this.ydisp;return e>=0&&et.MAX_BUFFER_SIZE?t.MAX_BUFFER_SIZE:n}fillViewportRows(e){if(this.lines.length===0){e===void 0&&(e=o.DEFAULT_ATTR_DATA);let t=this._rows;for(;t--;)this.lines.push(this.getBlankLine(e))}}clear(){this.ydisp=0,this.ybase=0,this.y=0,this.x=0,this.lines=new r.CircularList(this._getCorrectBufferLength(this._rows)),this.scrollTop=0,this.scrollBottom=this._rows-1,this.setupTabStops()}resize(e,t){let n=this.getNullCell(o.DEFAULT_ATTR_DATA),r=0,i=this._getCorrectBufferLength(t);if(i>this.lines.maxLength&&(this.lines.maxLength=i),this.lines.length>0){if(this._cols0&&this.lines.length<=this.ybase+this.y+a+1?(this.ybase--,a++,this.ydisp>0&&this.ydisp--):this.lines.push(new o.BufferLine(e,n)));else for(let e=this._rows;e>t;e--)this.lines.length>t+this.ybase&&(this.lines.length>this.ybase+this.y+1?this.lines.pop():(this.ybase++,this.ydisp++));if(i0&&(this.lines.trimStart(e),this.ybase=Math.max(this.ybase-e,0),this.ydisp=Math.max(this.ydisp-e,0),this.savedY=Math.max(this.savedY-e,0)),this.lines.maxLength=i}this.x=Math.min(this.x,e-1),this.y=Math.min(this.y,t-1),a&&(this.y+=a),this.savedX=Math.min(this.savedX,e-1),this.scrollTop=0}if(this.scrollBottom=t-1,this._isReflowEnabled&&(this._reflow(e,t),this._cols>e))for(let t=0;t.1*this.lines.length&&(this._memoryCleanupPosition=0,this._memoryCleanupQueue.enqueue((()=>this._batchedMemoryCleanup())))}_batchedMemoryCleanup(){let e=!0;this._memoryCleanupPosition>=this.lines.length&&(this._memoryCleanupPosition=0,e=!1);let t=0;for(;this._memoryCleanupPosition100)return!0;return e}get _isReflowEnabled(){let e=this._optionsService.rawOptions.windowsPty;return e&&e.buildNumber?this._hasScrollback&&e.backend===`conpty`&&e.buildNumber>=21376:this._hasScrollback&&!this._optionsService.rawOptions.windowsMode}_reflow(e,t){this._cols!==e&&(e>this._cols?this._reflowLarger(e,t):this._reflowSmaller(e,t))}_reflowLarger(e,t){let n=(0,s.reflowLargerGetLinesToRemove)(this.lines,this._cols,e,this.ybase+this.y,this.getNullCell(o.DEFAULT_ATTR_DATA));if(n.length>0){let r=(0,s.reflowLargerCreateNewLayout)(this.lines,n);(0,s.reflowLargerApplyNewLayout)(this.lines,r.layout),this._reflowLargerAdjustViewport(e,t,r.countRemoved)}}_reflowLargerAdjustViewport(e,t,n){let r=this.getNullCell(o.DEFAULT_ATTR_DATA),i=n;for(;i-- >0;)this.ybase===0?(this.y>0&&this.y--,this.lines.length=0;a--){let c=this.lines.get(a);if(!c||!c.isWrapped&&c.getTrimmedLength()<=e)continue;let l=[c];for(;c.isWrapped&&a>0;)c=this.lines.get(--a),l.unshift(c);let u=this.ybase+this.y;if(u>=a&&u0&&(r.push({start:a+l.length+i,newLines:h}),i+=h.length),l.push(...h);let g=f.length-1,_=f[g];_===0&&(g--,_=f[g]);let v=l.length-p-1,y=d;for(;v>=0;){let e=Math.min(y,_);if(l[g]===void 0)break;if(l[g].copyCellsFrom(l[v],y-e,_-e,e,!0),_-=e,_===0&&(g--,_=f[g]),y-=e,y===0){v--;let e=Math.max(v,0);y=(0,s.getWrappedLineTrimmedLength)(l,e,this._cols)}}for(let t=0;t0;)this.ybase===0?this.y0){let e=[],t=[];for(let e=0;e=0;l--)if(s&&s.start>a+c){for(let e=s.newLines.length-1;e>=0;e--)this.lines.set(l--,s.newLines[e]);l++,e.push({index:a+1,amount:s.newLines.length}),c+=s.newLines.length,s=r[++o]}else this.lines.set(l,t[a--]);let l=0;for(let t=e.length-1;t>=0;t--)e[t].index+=l,this.lines.onInsertEmitter.fire(e[t]),l+=e[t].amount;let u=Math.max(0,n+i-this.lines.maxLength);u>0&&this.lines.onTrimEmitter.fire(u)}}translateBufferLineToString(e,t,n=0,r){let i=this.lines.get(e);return i?i.translateToString(t,n,r):``}getWrappedRangeForLine(e){let t=e,n=e;for(;t>0&&this.lines.get(t).isWrapped;)t--;for(;n+10;);return e>=this._cols?this._cols-1:e<0?0:e}nextStop(e){for(e??=this.x;!this.tabs[++e]&&e=this._cols?this._cols-1:e<0?0:e}clearMarkers(e){this._isClearing=!0;for(let t=0;t{t.line-=e,t.line<0&&t.dispose()}))),t.register(this.lines.onInsert((e=>{t.line>=e.index&&(t.line+=e.amount)}))),t.register(this.lines.onDelete((e=>{t.line>=e.index&&t.linee.index&&(t.line-=e.amount)}))),t.register(t.onDispose((()=>this._removeMarker(t)))),t}_removeMarker(e){this._isClearing||this.markers.splice(this.markers.indexOf(e),1)}}},8437:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.BufferLine=t.DEFAULT_ATTR_DATA=void 0;let r=n(3734),i=n(511),a=n(643),o=n(482);t.DEFAULT_ATTR_DATA=Object.freeze(new r.AttributeData);let s=0;class c{constructor(e,t,n=!1){this.isWrapped=n,this._combined={},this._extendedAttrs={},this._data=new Uint32Array(3*e);let r=t||i.CellData.fromCharData([0,a.NULL_CELL_CHAR,a.NULL_CELL_WIDTH,a.NULL_CELL_CODE]);for(let t=0;t>22,2097152&t?this._combined[e].charCodeAt(this._combined[e].length-1):n]}set(e,t){this._data[3*e+1]=t[a.CHAR_DATA_ATTR_INDEX],t[a.CHAR_DATA_CHAR_INDEX].length>1?(this._combined[e]=t[1],this._data[3*e+0]=2097152|e|t[a.CHAR_DATA_WIDTH_INDEX]<<22):this._data[3*e+0]=t[a.CHAR_DATA_CHAR_INDEX].charCodeAt(0)|t[a.CHAR_DATA_WIDTH_INDEX]<<22}getWidth(e){return this._data[3*e+0]>>22}hasWidth(e){return 12582912&this._data[3*e+0]}getFg(e){return this._data[3*e+1]}getBg(e){return this._data[3*e+2]}hasContent(e){return 4194303&this._data[3*e+0]}getCodePoint(e){let t=this._data[3*e+0];return 2097152&t?this._combined[e].charCodeAt(this._combined[e].length-1):2097151&t}isCombined(e){return 2097152&this._data[3*e+0]}getString(e){let t=this._data[3*e+0];return 2097152&t?this._combined[e]:2097151&t?(0,o.stringFromCodePoint)(2097151&t):``}isProtected(e){return 536870912&this._data[3*e+2]}loadCell(e,t){return s=3*e,t.content=this._data[s+0],t.fg=this._data[s+1],t.bg=this._data[s+2],2097152&t.content&&(t.combinedData=this._combined[e]),268435456&t.bg&&(t.extended=this._extendedAttrs[e]),t}setCell(e,t){2097152&t.content&&(this._combined[e]=t.combinedData),268435456&t.bg&&(this._extendedAttrs[e]=t.extended),this._data[3*e+0]=t.content,this._data[3*e+1]=t.fg,this._data[3*e+2]=t.bg}setCellFromCodepoint(e,t,n,r){268435456&r.bg&&(this._extendedAttrs[e]=r.extended),this._data[3*e+0]=t|n<<22,this._data[3*e+1]=r.fg,this._data[3*e+2]=r.bg}addCodepointToCell(e,t,n){let r=this._data[3*e+0];2097152&r?this._combined[e]+=(0,o.stringFromCodePoint)(t):2097151&r?(this._combined[e]=(0,o.stringFromCodePoint)(2097151&r)+(0,o.stringFromCodePoint)(t),r&=-2097152,r|=2097152):r=t|1<<22,n&&(r&=-12582913,r|=n<<22),this._data[3*e+0]=r}insertCells(e,t,n){if((e%=this.length)&&this.getWidth(e-1)===2&&this.setCellFromCodepoint(e-1,0,1,n),t=0;--n)this.setCell(e+t+n,this.loadCell(e+n,r));for(let r=0;rthis.length){if(this._data.buffer.byteLength>=4*n)this._data=new Uint32Array(this._data.buffer,0,n);else{let e=new Uint32Array(n);e.set(this._data),this._data=e}for(let n=this.length;n=e&&delete this._combined[r]}let r=Object.keys(this._extendedAttrs);for(let t=0;t=e&&delete this._extendedAttrs[n]}}return this.length=e,4*n*2=0;--e)if(4194303&this._data[3*e+0])return e+(this._data[3*e+0]>>22);return 0}getNoBgTrimmedLength(){for(let e=this.length-1;e>=0;--e)if(4194303&this._data[3*e+0]||50331648&this._data[3*e+2])return e+(this._data[3*e+0]>>22);return 0}copyCellsFrom(e,t,n,r,i){let a=e._data;if(i)for(let i=r-1;i>=0;i--){for(let e=0;e<3;e++)this._data[3*(n+i)+e]=a[3*(t+i)+e];268435456&a[3*(t+i)+2]&&(this._extendedAttrs[n+i]=e._extendedAttrs[t+i])}else for(let i=0;i=t&&(this._combined[i-t+n]=e._combined[i])}}translateToString(e,t,n,r){t??=0,n??=this.length,e&&(n=Math.min(n,this.getTrimmedLength())),r&&(r.length=0);let i=``;for(;t>22||1}return r&&r.push(t),i}}t.BufferLine=c},4841:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.getRangeLength=void 0,t.getRangeLength=function(e,t){if(e.start.y>e.end.y)throw Error(`Buffer range end (${e.end.x}, ${e.end.y}) cannot be before start (${e.start.x}, ${e.start.y})`);return t*(e.end.y-e.start.y)+(e.end.x-e.start.x+1)}},4634:(e,t)=>{function n(e,t,n){if(t===e.length-1)return e[t].getTrimmedLength();let r=!e[t].hasContent(n-1)&&e[t].getWidth(n-1)===1,i=e[t+1].getWidth(0)===2;return r&&i?n-1:n}Object.defineProperty(t,"__esModule",{value:!0}),t.getWrappedLineTrimmedLength=t.reflowSmallerGetNewLineLengths=t.reflowLargerApplyNewLayout=t.reflowLargerCreateNewLayout=t.reflowLargerGetLinesToRemove=void 0,t.reflowLargerGetLinesToRemove=function(e,t,r,i,a){let o=[];for(let s=0;s=s&&i0&&(e>d||u[e].getTrimmedLength()===0);e--)h++;h>0&&(o.push(s+u.length-h),o.push(h)),s+=u.length-1}return o},t.reflowLargerCreateNewLayout=function(e,t){let n=[],r=0,i=t[r],a=0;for(let o=0;on(e,i,t))).reduce(((e,t)=>e+t)),o=0,s=0,c=0;for(;cl&&(o-=l,s++);let u=e[s].getWidth(o-1)===2;u&&o--;let d=u?r-1:r;i.push(d),c+=d}return i},t.getWrappedLineTrimmedLength=n},5295:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.BufferSet=void 0;let r=n(8460),i=n(844),a=n(9092);class o extends i.Disposable{constructor(e,t){super(),this._optionsService=e,this._bufferService=t,this._onBufferActivate=this.register(new r.EventEmitter),this.onBufferActivate=this._onBufferActivate.event,this.reset(),this.register(this._optionsService.onSpecificOptionChange(`scrollback`,(()=>this.resize(this._bufferService.cols,this._bufferService.rows)))),this.register(this._optionsService.onSpecificOptionChange(`tabStopWidth`,(()=>this.setupTabStops())))}reset(){this._normal=new a.Buffer(!0,this._optionsService,this._bufferService),this._normal.fillViewportRows(),this._alt=new a.Buffer(!1,this._optionsService,this._bufferService),this._activeBuffer=this._normal,this._onBufferActivate.fire({activeBuffer:this._normal,inactiveBuffer:this._alt}),this.setupTabStops()}get alt(){return this._alt}get active(){return this._activeBuffer}get normal(){return this._normal}activateNormalBuffer(){this._activeBuffer!==this._normal&&(this._normal.x=this._alt.x,this._normal.y=this._alt.y,this._alt.clearAllMarkers(),this._alt.clear(),this._activeBuffer=this._normal,this._onBufferActivate.fire({activeBuffer:this._normal,inactiveBuffer:this._alt}))}activateAltBuffer(e){this._activeBuffer!==this._alt&&(this._alt.fillViewportRows(e),this._alt.x=this._normal.x,this._alt.y=this._normal.y,this._activeBuffer=this._alt,this._onBufferActivate.fire({activeBuffer:this._alt,inactiveBuffer:this._normal}))}resize(e,t){this._normal.resize(e,t),this._alt.resize(e,t),this.setupTabStops(e)}setupTabStops(e){this._normal.setupTabStops(e),this._alt.setupTabStops(e)}}t.BufferSet=o},511:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.CellData=void 0;let r=n(482),i=n(643),a=n(3734);class o extends a.AttributeData{constructor(){super(...arguments),this.content=0,this.fg=0,this.bg=0,this.extended=new a.ExtendedAttrs,this.combinedData=``}static fromCharData(e){let t=new o;return t.setFromCharData(e),t}isCombined(){return 2097152&this.content}getWidth(){return this.content>>22}getChars(){return 2097152&this.content?this.combinedData:2097151&this.content?(0,r.stringFromCodePoint)(2097151&this.content):``}getCode(){return this.isCombined()?this.combinedData.charCodeAt(this.combinedData.length-1):2097151&this.content}setFromCharData(e){this.fg=e[i.CHAR_DATA_ATTR_INDEX],this.bg=0;let t=!1;if(e[i.CHAR_DATA_CHAR_INDEX].length>2)t=!0;else if(e[i.CHAR_DATA_CHAR_INDEX].length===2){let n=e[i.CHAR_DATA_CHAR_INDEX].charCodeAt(0);if(55296<=n&&n<=56319){let r=e[i.CHAR_DATA_CHAR_INDEX].charCodeAt(1);56320<=r&&r<=57343?this.content=1024*(n-55296)+r-56320+65536|e[i.CHAR_DATA_WIDTH_INDEX]<<22:t=!0}else t=!0}else this.content=e[i.CHAR_DATA_CHAR_INDEX].charCodeAt(0)|e[i.CHAR_DATA_WIDTH_INDEX]<<22;t&&(this.combinedData=e[i.CHAR_DATA_CHAR_INDEX],this.content=2097152|e[i.CHAR_DATA_WIDTH_INDEX]<<22)}getAsCharData(){return[this.fg,this.getChars(),this.getWidth(),this.getCode()]}}t.CellData=o},643:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.WHITESPACE_CELL_CODE=t.WHITESPACE_CELL_WIDTH=t.WHITESPACE_CELL_CHAR=t.NULL_CELL_CODE=t.NULL_CELL_WIDTH=t.NULL_CELL_CHAR=t.CHAR_DATA_CODE_INDEX=t.CHAR_DATA_WIDTH_INDEX=t.CHAR_DATA_CHAR_INDEX=t.CHAR_DATA_ATTR_INDEX=t.DEFAULT_EXT=t.DEFAULT_ATTR=t.DEFAULT_COLOR=void 0,t.DEFAULT_COLOR=0,t.DEFAULT_ATTR=256|t.DEFAULT_COLOR<<9,t.DEFAULT_EXT=0,t.CHAR_DATA_ATTR_INDEX=0,t.CHAR_DATA_CHAR_INDEX=1,t.CHAR_DATA_WIDTH_INDEX=2,t.CHAR_DATA_CODE_INDEX=3,t.NULL_CELL_CHAR=``,t.NULL_CELL_WIDTH=1,t.NULL_CELL_CODE=0,t.WHITESPACE_CELL_CHAR=` `,t.WHITESPACE_CELL_WIDTH=1,t.WHITESPACE_CELL_CODE=32},4863:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Marker=void 0;let r=n(8460),i=n(844);class a{get id(){return this._id}constructor(e){this.line=e,this.isDisposed=!1,this._disposables=[],this._id=a._nextId++,this._onDispose=this.register(new r.EventEmitter),this.onDispose=this._onDispose.event}dispose(){this.isDisposed||(this.isDisposed=!0,this.line=-1,this._onDispose.fire(),(0,i.disposeArray)(this._disposables),this._disposables.length=0)}register(e){return this._disposables.push(e),e}}t.Marker=a,a._nextId=1},7116:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.DEFAULT_CHARSET=t.CHARSETS=void 0,t.CHARSETS={},t.DEFAULT_CHARSET=t.CHARSETS.B,t.CHARSETS[0]={"`":`◆`,a:`▒`,b:`␉`,c:`␌`,d:`␍`,e:`␊`,f:`°`,g:`±`,h:`␤`,i:`␋`,j:`┘`,k:`┐`,l:`┌`,m:`└`,n:`┼`,o:`⎺`,p:`⎻`,q:`─`,r:`⎼`,s:`⎽`,t:`├`,u:`┤`,v:`┴`,w:`┬`,x:`│`,y:`≤`,z:`≥`,"{":`π`,"|":`≠`,"}":`£`,"~":`·`},t.CHARSETS.A={"#":`£`},t.CHARSETS.B=void 0,t.CHARSETS[4]={"#":`£`,"@":`¾`,"[":`ij`,"\\":`½`,"]":`|`,"{":`¨`,"|":`f`,"}":`¼`,"~":`´`},t.CHARSETS.C=t.CHARSETS[5]={"[":`Ä`,"\\":`Ö`,"]":`Å`,"^":`Ü`,"`":`é`,"{":`ä`,"|":`ö`,"}":`å`,"~":`ü`},t.CHARSETS.R={"#":`£`,"@":`à`,"[":`°`,"\\":`ç`,"]":`§`,"{":`é`,"|":`ù`,"}":`è`,"~":`¨`},t.CHARSETS.Q={"@":`à`,"[":`â`,"\\":`ç`,"]":`ê`,"^":`î`,"`":`ô`,"{":`é`,"|":`ù`,"}":`è`,"~":`û`},t.CHARSETS.K={"@":`§`,"[":`Ä`,"\\":`Ö`,"]":`Ü`,"{":`ä`,"|":`ö`,"}":`ü`,"~":`ß`},t.CHARSETS.Y={"#":`£`,"@":`§`,"[":`°`,"\\":`ç`,"]":`é`,"`":`ù`,"{":`à`,"|":`ò`,"}":`è`,"~":`ì`},t.CHARSETS.E=t.CHARSETS[6]={"@":`Ä`,"[":`Æ`,"\\":`Ø`,"]":`Å`,"^":`Ü`,"`":`ä`,"{":`æ`,"|":`ø`,"}":`å`,"~":`ü`},t.CHARSETS.Z={"#":`£`,"@":`§`,"[":`¡`,"\\":`Ñ`,"]":`¿`,"{":`°`,"|":`ñ`,"}":`ç`},t.CHARSETS.H=t.CHARSETS[7]={"@":`É`,"[":`Ä`,"\\":`Ö`,"]":`Å`,"^":`Ü`,"`":`é`,"{":`ä`,"|":`ö`,"}":`å`,"~":`ü`},t.CHARSETS[`=`]={"#":`ù`,"@":`à`,"[":`é`,"\\":`ç`,"]":`ê`,"^":`î`,_:`è`,"`":`ô`,"{":`ä`,"|":`ö`,"}":`ü`,"~":`û`}},2584:(e,t)=>{var n,r,i;Object.defineProperty(t,"__esModule",{value:!0}),t.C1_ESCAPED=t.C1=t.C0=void 0,function(e){e.NUL=`\0`,e.SOH=``,e.STX=``,e.ETX=``,e.EOT=``,e.ENQ=``,e.ACK=``,e.BEL=`\x07`,e.BS=`\b`,e.HT=` `,e.LF=` `,e.VT=`\v`,e.FF=`\f`,e.CR=`\r`,e.SO=``,e.SI=``,e.DLE=``,e.DC1=``,e.DC2=``,e.DC3=``,e.DC4=``,e.NAK=``,e.SYN=``,e.ETB=``,e.CAN=``,e.EM=``,e.SUB=``,e.ESC=`\x1B`,e.FS=``,e.GS=``,e.RS=``,e.US=``,e.SP=` `,e.DEL=``}(n||(t.C0=n={})),function(e){e.PAD=`€`,e.HOP=``,e.BPH=`‚`,e.NBH=`ƒ`,e.IND=`„`,e.NEL=`…`,e.SSA=`†`,e.ESA=`‡`,e.HTS=`ˆ`,e.HTJ=`‰`,e.VTS=`Š`,e.PLD=`‹`,e.PLU=`Œ`,e.RI=``,e.SS2=`Ž`,e.SS3=``,e.DCS=``,e.PU1=`‘`,e.PU2=`’`,e.STS=`“`,e.CCH=`”`,e.MW=`•`,e.SPA=`–`,e.EPA=`—`,e.SOS=`˜`,e.SGCI=`™`,e.SCI=`š`,e.CSI=`›`,e.ST=`œ`,e.OSC=``,e.PM=`ž`,e.APC=`Ÿ`}(r||(t.C1=r={})),function(e){e.ST=`${n.ESC}\\`}(i||(t.C1_ESCAPED=i={}))},7399:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.evaluateKeyboardEvent=void 0;let r=n(2584),i={48:[`0`,`)`],49:[`1`,`!`],50:[`2`,`@`],51:[`3`,`#`],52:[`4`,`$`],53:[`5`,`%`],54:[`6`,`^`],55:[`7`,`&`],56:[`8`,`*`],57:[`9`,`(`],186:[`;`,`:`],187:[`=`,`+`],188:[`,`,`<`],189:[`-`,`_`],190:[`.`,`>`],191:[`/`,`?`],192:["`",`~`],219:[`[`,`{`],220:[`\\`,`|`],221:[`]`,`}`],222:[`'`,`"`]};t.evaluateKeyboardEvent=function(e,t,n,a){let o={type:0,cancel:!1,key:void 0},s=!!e.shiftKey|(e.altKey?2:0)|(e.ctrlKey?4:0)|(e.metaKey?8:0);switch(e.keyCode){case 0:e.key===`UIKeyInputUpArrow`?o.key=t?r.C0.ESC+`OA`:r.C0.ESC+`[A`:e.key===`UIKeyInputLeftArrow`?o.key=t?r.C0.ESC+`OD`:r.C0.ESC+`[D`:e.key===`UIKeyInputRightArrow`?o.key=t?r.C0.ESC+`OC`:r.C0.ESC+`[C`:e.key===`UIKeyInputDownArrow`&&(o.key=t?r.C0.ESC+`OB`:r.C0.ESC+`[B`);break;case 8:o.key=e.ctrlKey?`\b`:r.C0.DEL,e.altKey&&(o.key=r.C0.ESC+o.key);break;case 9:if(e.shiftKey){o.key=r.C0.ESC+`[Z`;break}o.key=r.C0.HT,o.cancel=!0;break;case 13:o.key=e.altKey?r.C0.ESC+r.C0.CR:r.C0.CR,o.cancel=!0;break;case 27:o.key=r.C0.ESC,e.altKey&&(o.key=r.C0.ESC+r.C0.ESC),o.cancel=!0;break;case 37:if(e.metaKey)break;s?(o.key=r.C0.ESC+`[1;`+(s+1)+`D`,o.key===r.C0.ESC+`[1;3D`&&(o.key=r.C0.ESC+(n?`b`:`[1;5D`))):o.key=t?r.C0.ESC+`OD`:r.C0.ESC+`[D`;break;case 39:if(e.metaKey)break;s?(o.key=r.C0.ESC+`[1;`+(s+1)+`C`,o.key===r.C0.ESC+`[1;3C`&&(o.key=r.C0.ESC+(n?`f`:`[1;5C`))):o.key=t?r.C0.ESC+`OC`:r.C0.ESC+`[C`;break;case 38:if(e.metaKey)break;s?(o.key=r.C0.ESC+`[1;`+(s+1)+`A`,n||o.key!==r.C0.ESC+`[1;3A`||(o.key=r.C0.ESC+`[1;5A`)):o.key=t?r.C0.ESC+`OA`:r.C0.ESC+`[A`;break;case 40:if(e.metaKey)break;s?(o.key=r.C0.ESC+`[1;`+(s+1)+`B`,n||o.key!==r.C0.ESC+`[1;3B`||(o.key=r.C0.ESC+`[1;5B`)):o.key=t?r.C0.ESC+`OB`:r.C0.ESC+`[B`;break;case 45:e.shiftKey||e.ctrlKey||(o.key=r.C0.ESC+`[2~`);break;case 46:o.key=s?r.C0.ESC+`[3;`+(s+1)+`~`:r.C0.ESC+`[3~`;break;case 36:o.key=s?r.C0.ESC+`[1;`+(s+1)+`H`:t?r.C0.ESC+`OH`:r.C0.ESC+`[H`;break;case 35:o.key=s?r.C0.ESC+`[1;`+(s+1)+`F`:t?r.C0.ESC+`OF`:r.C0.ESC+`[F`;break;case 33:e.shiftKey?o.type=2:e.ctrlKey?o.key=r.C0.ESC+`[5;`+(s+1)+`~`:o.key=r.C0.ESC+`[5~`;break;case 34:e.shiftKey?o.type=3:e.ctrlKey?o.key=r.C0.ESC+`[6;`+(s+1)+`~`:o.key=r.C0.ESC+`[6~`;break;case 112:o.key=s?r.C0.ESC+`[1;`+(s+1)+`P`:r.C0.ESC+`OP`;break;case 113:o.key=s?r.C0.ESC+`[1;`+(s+1)+`Q`:r.C0.ESC+`OQ`;break;case 114:o.key=s?r.C0.ESC+`[1;`+(s+1)+`R`:r.C0.ESC+`OR`;break;case 115:o.key=s?r.C0.ESC+`[1;`+(s+1)+`S`:r.C0.ESC+`OS`;break;case 116:o.key=s?r.C0.ESC+`[15;`+(s+1)+`~`:r.C0.ESC+`[15~`;break;case 117:o.key=s?r.C0.ESC+`[17;`+(s+1)+`~`:r.C0.ESC+`[17~`;break;case 118:o.key=s?r.C0.ESC+`[18;`+(s+1)+`~`:r.C0.ESC+`[18~`;break;case 119:o.key=s?r.C0.ESC+`[19;`+(s+1)+`~`:r.C0.ESC+`[19~`;break;case 120:o.key=s?r.C0.ESC+`[20;`+(s+1)+`~`:r.C0.ESC+`[20~`;break;case 121:o.key=s?r.C0.ESC+`[21;`+(s+1)+`~`:r.C0.ESC+`[21~`;break;case 122:o.key=s?r.C0.ESC+`[23;`+(s+1)+`~`:r.C0.ESC+`[23~`;break;case 123:o.key=s?r.C0.ESC+`[24;`+(s+1)+`~`:r.C0.ESC+`[24~`;break;default:if(!e.ctrlKey||e.shiftKey||e.altKey||e.metaKey)if(n&&!a||!e.altKey||e.metaKey)!n||e.altKey||e.ctrlKey||e.shiftKey||!e.metaKey?e.key&&!e.ctrlKey&&!e.altKey&&!e.metaKey&&e.keyCode>=48&&e.key.length===1?o.key=e.key:e.key&&e.ctrlKey&&(e.key===`_`&&(o.key=r.C0.US),e.key===`@`&&(o.key=r.C0.NUL)):e.keyCode===65&&(o.type=1);else{let t=i[e.keyCode]?.[+!!e.shiftKey];if(t)o.key=r.C0.ESC+t;else if(e.keyCode>=65&&e.keyCode<=90){let t=e.ctrlKey?e.keyCode-64:e.keyCode+32,n=String.fromCharCode(t);e.shiftKey&&(n=n.toUpperCase()),o.key=r.C0.ESC+n}else if(e.keyCode===32)o.key=r.C0.ESC+(e.ctrlKey?r.C0.NUL:` `);else if(e.key===`Dead`&&e.code.startsWith(`Key`)){let t=e.code.slice(3,4);e.shiftKey||(t=t.toLowerCase()),o.key=r.C0.ESC+t,o.cancel=!0}}else e.keyCode>=65&&e.keyCode<=90?o.key=String.fromCharCode(e.keyCode-64):e.keyCode===32?o.key=r.C0.NUL:e.keyCode>=51&&e.keyCode<=55?o.key=String.fromCharCode(e.keyCode-51+27):e.keyCode===56?o.key=r.C0.DEL:e.keyCode===219?o.key=r.C0.ESC:e.keyCode===220?o.key=r.C0.FS:e.keyCode===221&&(o.key=r.C0.GS)}return o}},482:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Utf8ToUtf32=t.StringToUtf32=t.utf32ToString=t.stringFromCodePoint=void 0,t.stringFromCodePoint=function(e){return e>65535?(e-=65536,String.fromCharCode(55296+(e>>10))+String.fromCharCode(e%1024+56320)):String.fromCharCode(e)},t.utf32ToString=function(e,t=0,n=e.length){let r=``;for(let i=t;i65535?(t-=65536,r+=String.fromCharCode(55296+(t>>10))+String.fromCharCode(t%1024+56320)):r+=String.fromCharCode(t)}return r},t.StringToUtf32=class{constructor(){this._interim=0}clear(){this._interim=0}decode(e,t){let n=e.length;if(!n)return 0;let r=0,i=0;if(this._interim){let n=e.charCodeAt(i++);56320<=n&&n<=57343?t[r++]=1024*(this._interim-55296)+n-56320+65536:(t[r++]=this._interim,t[r++]=n),this._interim=0}for(let a=i;a=n)return this._interim=i,r;let o=e.charCodeAt(a);56320<=o&&o<=57343?t[r++]=1024*(i-55296)+o-56320+65536:(t[r++]=i,t[r++]=o)}else i!==65279&&(t[r++]=i)}return r}},t.Utf8ToUtf32=class{constructor(){this.interim=new Uint8Array(3)}clear(){this.interim.fill(0)}decode(e,t){let n=e.length;if(!n)return 0;let r,i,a,o,s=0,c=0,l=0;if(this.interim[0]){let r=!1,i=this.interim[0];i&=(224&i)==192?31:(240&i)==224?15:7;let a,o=0;for(;(a=63&this.interim[++o])&&o<4;)i<<=6,i|=a;let c=(224&this.interim[0])==192?2:(240&this.interim[0])==224?3:4,u=c-o;for(;l=n)return 0;if(a=e[l++],(192&a)!=128){l--,r=!0;break}this.interim[o++]=a,i<<=6,i|=63&a}r||(c===2?i<128?l--:t[s++]=i:c===3?i<2048||i>=55296&&i<=57343||i===65279||(t[s++]=i):i<65536||i>1114111||(t[s++]=i)),this.interim.fill(0)}let u=n-4,d=l;for(;d=n)return this.interim[0]=r,s;if(i=e[d++],(192&i)!=128){d--;continue}if(c=(31&r)<<6|63&i,c<128){d--;continue}t[s++]=c}else if((240&r)==224){if(d>=n)return this.interim[0]=r,s;if(i=e[d++],(192&i)!=128){d--;continue}if(d>=n)return this.interim[0]=r,this.interim[1]=i,s;if(a=e[d++],(192&a)!=128){d--;continue}if(c=(15&r)<<12|(63&i)<<6|63&a,c<2048||c>=55296&&c<=57343||c===65279)continue;t[s++]=c}else if((248&r)==240){if(d>=n)return this.interim[0]=r,s;if(i=e[d++],(192&i)!=128){d--;continue}if(d>=n)return this.interim[0]=r,this.interim[1]=i,s;if(a=e[d++],(192&a)!=128){d--;continue}if(d>=n)return this.interim[0]=r,this.interim[1]=i,this.interim[2]=a,s;if(o=e[d++],(192&o)!=128){d--;continue}if(c=(7&r)<<18|(63&i)<<12|(63&a)<<6|63&o,c<65536||c>1114111)continue;t[s++]=c}}return s}}},225:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.UnicodeV6=void 0;let r=n(1480),i=[[768,879],[1155,1158],[1160,1161],[1425,1469],[1471,1471],[1473,1474],[1476,1477],[1479,1479],[1536,1539],[1552,1557],[1611,1630],[1648,1648],[1750,1764],[1767,1768],[1770,1773],[1807,1807],[1809,1809],[1840,1866],[1958,1968],[2027,2035],[2305,2306],[2364,2364],[2369,2376],[2381,2381],[2385,2388],[2402,2403],[2433,2433],[2492,2492],[2497,2500],[2509,2509],[2530,2531],[2561,2562],[2620,2620],[2625,2626],[2631,2632],[2635,2637],[2672,2673],[2689,2690],[2748,2748],[2753,2757],[2759,2760],[2765,2765],[2786,2787],[2817,2817],[2876,2876],[2879,2879],[2881,2883],[2893,2893],[2902,2902],[2946,2946],[3008,3008],[3021,3021],[3134,3136],[3142,3144],[3146,3149],[3157,3158],[3260,3260],[3263,3263],[3270,3270],[3276,3277],[3298,3299],[3393,3395],[3405,3405],[3530,3530],[3538,3540],[3542,3542],[3633,3633],[3636,3642],[3655,3662],[3761,3761],[3764,3769],[3771,3772],[3784,3789],[3864,3865],[3893,3893],[3895,3895],[3897,3897],[3953,3966],[3968,3972],[3974,3975],[3984,3991],[3993,4028],[4038,4038],[4141,4144],[4146,4146],[4150,4151],[4153,4153],[4184,4185],[4448,4607],[4959,4959],[5906,5908],[5938,5940],[5970,5971],[6002,6003],[6068,6069],[6071,6077],[6086,6086],[6089,6099],[6109,6109],[6155,6157],[6313,6313],[6432,6434],[6439,6440],[6450,6450],[6457,6459],[6679,6680],[6912,6915],[6964,6964],[6966,6970],[6972,6972],[6978,6978],[7019,7027],[7616,7626],[7678,7679],[8203,8207],[8234,8238],[8288,8291],[8298,8303],[8400,8431],[12330,12335],[12441,12442],[43014,43014],[43019,43019],[43045,43046],[64286,64286],[65024,65039],[65056,65059],[65279,65279],[65529,65531]],a=[[68097,68099],[68101,68102],[68108,68111],[68152,68154],[68159,68159],[119143,119145],[119155,119170],[119173,119179],[119210,119213],[119362,119364],[917505,917505],[917536,917631],[917760,917999]],o;t.UnicodeV6=class{constructor(){if(this.version=`6`,!o){o=new Uint8Array(65536),o.fill(1),o[0]=0,o.fill(0,1,32),o.fill(0,127,160),o.fill(2,4352,4448),o[9001]=2,o[9002]=2,o.fill(2,11904,42192),o[12351]=1,o.fill(2,44032,55204),o.fill(2,63744,64256),o.fill(2,65040,65050),o.fill(2,65072,65136),o.fill(2,65280,65377),o.fill(2,65504,65511);for(let e=0;et[i][1])return!1;for(;i>=r;)if(n=r+i>>1,e>t[n][1])r=n+1;else{if(!(e=131072&&e<=196605||e>=196608&&e<=262141?2:1}charProperties(e,t){let n=this.wcwidth(e),i=n===0&&t!==0;if(i){let e=r.UnicodeService.extractWidth(t);e===0?i=!1:e>n&&(n=e)}return r.UnicodeService.createPropertyValue(0,n,i)}}},5981:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.WriteBuffer=void 0;let r=n(8460),i=n(844);class a extends i.Disposable{constructor(e){super(),this._action=e,this._writeBuffer=[],this._callbacks=[],this._pendingData=0,this._bufferOffset=0,this._isSyncWriting=!1,this._syncCalls=0,this._didUserInput=!1,this._onWriteParsed=this.register(new r.EventEmitter),this.onWriteParsed=this._onWriteParsed.event}handleUserInput(){this._didUserInput=!0}writeSync(e,t){if(t!==void 0&&this._syncCalls>t)return void(this._syncCalls=0);if(this._pendingData+=e.length,this._writeBuffer.push(e),this._callbacks.push(void 0),this._syncCalls++,this._isSyncWriting)return;let n;for(this._isSyncWriting=!0;n=this._writeBuffer.shift();){this._action(n);let e=this._callbacks.shift();e&&e()}this._pendingData=0,this._bufferOffset=2147483647,this._isSyncWriting=!1,this._syncCalls=0}write(e,t){if(this._pendingData>5e7)throw Error(`write data discarded, use flow control to avoid losing data`);if(!this._writeBuffer.length){if(this._bufferOffset=0,this._didUserInput)return this._didUserInput=!1,this._pendingData+=e.length,this._writeBuffer.push(e),this._callbacks.push(t),void this._innerWrite();setTimeout((()=>this._innerWrite()))}this._pendingData+=e.length,this._writeBuffer.push(e),this._callbacks.push(t)}_innerWrite(e=0,t=!0){let n=e||Date.now();for(;this._writeBuffer.length>this._bufferOffset;){let e=this._writeBuffer[this._bufferOffset],r=this._action(e,t);if(r){r.catch((e=>(queueMicrotask((()=>{throw e})),Promise.resolve(!1)))).then(e=>Date.now()-n>=12?setTimeout((()=>this._innerWrite(0,e))):this._innerWrite(n,e));return}let i=this._callbacks[this._bufferOffset];if(i&&i(),this._bufferOffset++,this._pendingData-=e.length,Date.now()-n>=12)break}this._writeBuffer.length>this._bufferOffset?(this._bufferOffset>50&&(this._writeBuffer=this._writeBuffer.slice(this._bufferOffset),this._callbacks=this._callbacks.slice(this._bufferOffset),this._bufferOffset=0),setTimeout((()=>this._innerWrite()))):(this._writeBuffer.length=0,this._callbacks.length=0,this._pendingData=0,this._bufferOffset=0),this._onWriteParsed.fire()}}t.WriteBuffer=a},5941:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.toRgbString=t.parseColor=void 0;let n=/^([\da-f])\/([\da-f])\/([\da-f])$|^([\da-f]{2})\/([\da-f]{2})\/([\da-f]{2})$|^([\da-f]{3})\/([\da-f]{3})\/([\da-f]{3})$|^([\da-f]{4})\/([\da-f]{4})\/([\da-f]{4})$/,r=/^[\da-f]+$/;function i(e,t){let n=e.toString(16),r=n.length<2?`0`+n:n;switch(t){case 4:return n[0];case 8:return r;case 12:return(r+r).slice(0,3);default:return r+r}}t.parseColor=function(e){if(!e)return;let t=e.toLowerCase();if(t.indexOf(`rgb:`)===0){t=t.slice(4);let e=n.exec(t);if(e){let t=e[1]?15:e[4]?255:e[7]?4095:65535;return[Math.round(parseInt(e[1]||e[4]||e[7]||e[10],16)/t*255),Math.round(parseInt(e[2]||e[5]||e[8]||e[11],16)/t*255),Math.round(parseInt(e[3]||e[6]||e[9]||e[12],16)/t*255)]}}else if(t.indexOf(`#`)===0&&(t=t.slice(1),r.exec(t)&&[3,6,9,12].includes(t.length))){let e=t.length/3,n=[0,0,0];for(let r=0;r<3;++r){let i=parseInt(t.slice(e*r,e*r+e),16);n[r]=e===1?i<<4:e===2?i:e===3?i>>4:i>>8}return n}},t.toRgbString=function(e,t=16){let[n,r,a]=e;return`rgb:${i(n,t)}/${i(r,t)}/${i(a,t)}`}},5770:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.PAYLOAD_LIMIT=void 0,t.PAYLOAD_LIMIT=1e7},6351:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.DcsHandler=t.DcsParser=void 0;let r=n(482),i=n(8742),a=n(5770),o=[];t.DcsParser=class{constructor(){this._handlers=Object.create(null),this._active=o,this._ident=0,this._handlerFb=()=>{},this._stack={paused:!1,loopPosition:0,fallThrough:!1}}dispose(){this._handlers=Object.create(null),this._handlerFb=()=>{},this._active=o}registerHandler(e,t){this._handlers[e]===void 0&&(this._handlers[e]=[]);let n=this._handlers[e];return n.push(t),{dispose:()=>{let e=n.indexOf(t);e!==-1&&n.splice(e,1)}}}clearHandler(e){this._handlers[e]&&delete this._handlers[e]}setHandlerFallback(e){this._handlerFb=e}reset(){if(this._active.length)for(let e=this._stack.paused?this._stack.loopPosition-1:this._active.length-1;e>=0;--e)this._active[e].unhook(!1);this._stack.paused=!1,this._active=o,this._ident=0}hook(e,t){if(this.reset(),this._ident=e,this._active=this._handlers[e]||o,this._active.length)for(let e=this._active.length-1;e>=0;e--)this._active[e].hook(t);else this._handlerFb(this._ident,`HOOK`,t)}put(e,t,n){if(this._active.length)for(let r=this._active.length-1;r>=0;r--)this._active[r].put(e,t,n);else this._handlerFb(this._ident,`PUT`,(0,r.utf32ToString)(e,t,n))}unhook(e,t=!0){if(this._active.length){let n=!1,r=this._active.length-1,i=!1;if(this._stack.paused&&(r=this._stack.loopPosition-1,n=t,i=this._stack.fallThrough,this._stack.paused=!1),!i&&!1===n){for(;r>=0&&(n=this._active[r].unhook(e),!0!==n);r--)if(n instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=r,this._stack.fallThrough=!1,n;r--}for(;r>=0;r--)if(n=this._active[r].unhook(!1),n instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=r,this._stack.fallThrough=!0,n}else this._handlerFb(this._ident,`UNHOOK`,e);this._active=o,this._ident=0}};let s=new i.Params;s.addParam(0),t.DcsHandler=class{constructor(e){this._handler=e,this._data=``,this._params=s,this._hitLimit=!1}hook(e){this._params=e.length>1||e.params[0]?e.clone():s,this._data=``,this._hitLimit=!1}put(e,t,n){this._hitLimit||(this._data+=(0,r.utf32ToString)(e,t,n),this._data.length>a.PAYLOAD_LIMIT&&(this._data=``,this._hitLimit=!0))}unhook(e){let t=!1;if(this._hitLimit)t=!1;else if(e&&(t=this._handler(this._data,this._params),t instanceof Promise))return t.then((e=>(this._params=s,this._data=``,this._hitLimit=!1,e)));return this._params=s,this._data=``,this._hitLimit=!1,t}}},2015:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.EscapeSequenceParser=t.VT500_TRANSITION_TABLE=t.TransitionTable=void 0;let r=n(844),i=n(8742),a=n(6242),o=n(6351);class s{constructor(e){this.table=new Uint8Array(e)}setDefault(e,t){this.table.fill(e<<4|t)}add(e,t,n,r){this.table[t<<8|e]=n<<4|r}addMany(e,t,n,r){for(let i=0;it)),n=(e,n)=>t.slice(e,n),r=n(32,127),i=n(0,24);i.push(25),i.push.apply(i,n(28,32));let a=n(0,14),o;for(o in e.setDefault(1,0),e.addMany(r,0,2,0),a)e.addMany([24,26,153,154],o,3,0),e.addMany(n(128,144),o,3,0),e.addMany(n(144,152),o,3,0),e.add(156,o,0,0),e.add(27,o,11,1),e.add(157,o,4,8),e.addMany([152,158,159],o,0,7),e.add(155,o,11,3),e.add(144,o,11,9);return e.addMany(i,0,3,0),e.addMany(i,1,3,1),e.add(127,1,0,1),e.addMany(i,8,0,8),e.addMany(i,3,3,3),e.add(127,3,0,3),e.addMany(i,4,3,4),e.add(127,4,0,4),e.addMany(i,6,3,6),e.addMany(i,5,3,5),e.add(127,5,0,5),e.addMany(i,2,3,2),e.add(127,2,0,2),e.add(93,1,4,8),e.addMany(r,8,5,8),e.add(127,8,5,8),e.addMany([156,27,24,26,7],8,6,0),e.addMany(n(28,32),8,0,8),e.addMany([88,94,95],1,0,7),e.addMany(r,7,0,7),e.addMany(i,7,0,7),e.add(156,7,0,0),e.add(127,7,0,7),e.add(91,1,11,3),e.addMany(n(64,127),3,7,0),e.addMany(n(48,60),3,8,4),e.addMany([60,61,62,63],3,9,4),e.addMany(n(48,60),4,8,4),e.addMany(n(64,127),4,7,0),e.addMany([60,61,62,63],4,0,6),e.addMany(n(32,64),6,0,6),e.add(127,6,0,6),e.addMany(n(64,127),6,0,0),e.addMany(n(32,48),3,9,5),e.addMany(n(32,48),5,9,5),e.addMany(n(48,64),5,0,6),e.addMany(n(64,127),5,7,0),e.addMany(n(32,48),4,9,5),e.addMany(n(32,48),1,9,2),e.addMany(n(32,48),2,9,2),e.addMany(n(48,127),2,10,0),e.addMany(n(48,80),1,10,0),e.addMany(n(81,88),1,10,0),e.addMany([89,90,92],1,10,0),e.addMany(n(96,127),1,10,0),e.add(80,1,11,9),e.addMany(i,9,0,9),e.add(127,9,0,9),e.addMany(n(28,32),9,0,9),e.addMany(n(32,48),9,9,12),e.addMany(n(48,60),9,8,10),e.addMany([60,61,62,63],9,9,10),e.addMany(i,11,0,11),e.addMany(n(32,128),11,0,11),e.addMany(n(28,32),11,0,11),e.addMany(i,10,0,10),e.add(127,10,0,10),e.addMany(n(28,32),10,0,10),e.addMany(n(48,60),10,8,10),e.addMany([60,61,62,63],10,0,11),e.addMany(n(32,48),10,9,12),e.addMany(i,12,0,12),e.add(127,12,0,12),e.addMany(n(28,32),12,0,12),e.addMany(n(32,48),12,9,12),e.addMany(n(48,64),12,0,11),e.addMany(n(64,127),12,12,13),e.addMany(n(64,127),10,12,13),e.addMany(n(64,127),9,12,13),e.addMany(i,13,13,13),e.addMany(r,13,13,13),e.add(127,13,0,13),e.addMany([27,156,24,26],13,14,0),e.add(160,0,2,0),e.add(160,8,5,8),e.add(160,6,0,6),e.add(160,11,0,11),e.add(160,13,13,13),e}();class c extends r.Disposable{constructor(e=t.VT500_TRANSITION_TABLE){super(),this._transitions=e,this._parseStack={state:0,handlers:[],handlerPos:0,transition:0,chunkPos:0},this.initialState=0,this.currentState=this.initialState,this._params=new i.Params,this._params.addParam(0),this._collect=0,this.precedingJoinState=0,this._printHandlerFb=(e,t,n)=>{},this._executeHandlerFb=e=>{},this._csiHandlerFb=(e,t)=>{},this._escHandlerFb=e=>{},this._errorHandlerFb=e=>e,this._printHandler=this._printHandlerFb,this._executeHandlers=Object.create(null),this._csiHandlers=Object.create(null),this._escHandlers=Object.create(null),this.register((0,r.toDisposable)((()=>{this._csiHandlers=Object.create(null),this._executeHandlers=Object.create(null),this._escHandlers=Object.create(null)}))),this._oscParser=this.register(new a.OscParser),this._dcsParser=this.register(new o.DcsParser),this._errorHandler=this._errorHandlerFb,this.registerEscHandler({final:`\\`},(()=>!0))}_identifier(e,t=[64,126]){let n=0;if(e.prefix){if(e.prefix.length>1)throw Error(`only one byte as prefix supported`);if(n=e.prefix.charCodeAt(0),n&&60>n||n>63)throw Error(`prefix must be in range 0x3c .. 0x3f`)}if(e.intermediates){if(e.intermediates.length>2)throw Error(`only two bytes as intermediates are supported`);for(let t=0;tr||r>47)throw Error(`intermediate must be in range 0x20 .. 0x2f`);n<<=8,n|=r}}if(e.final.length!==1)throw Error(`final must be a single byte`);let r=e.final.charCodeAt(0);if(t[0]>r||r>t[1])throw Error(`final must be in range ${t[0]} .. ${t[1]}`);return n<<=8,n|=r,n}identToString(e){let t=[];for(;e;)t.push(String.fromCharCode(255&e)),e>>=8;return t.reverse().join(``)}setPrintHandler(e){this._printHandler=e}clearPrintHandler(){this._printHandler=this._printHandlerFb}registerEscHandler(e,t){let n=this._identifier(e,[48,126]);this._escHandlers[n]===void 0&&(this._escHandlers[n]=[]);let r=this._escHandlers[n];return r.push(t),{dispose:()=>{let e=r.indexOf(t);e!==-1&&r.splice(e,1)}}}clearEscHandler(e){this._escHandlers[this._identifier(e,[48,126])]&&delete this._escHandlers[this._identifier(e,[48,126])]}setEscHandlerFallback(e){this._escHandlerFb=e}setExecuteHandler(e,t){this._executeHandlers[e.charCodeAt(0)]=t}clearExecuteHandler(e){this._executeHandlers[e.charCodeAt(0)]&&delete this._executeHandlers[e.charCodeAt(0)]}setExecuteHandlerFallback(e){this._executeHandlerFb=e}registerCsiHandler(e,t){let n=this._identifier(e);this._csiHandlers[n]===void 0&&(this._csiHandlers[n]=[]);let r=this._csiHandlers[n];return r.push(t),{dispose:()=>{let e=r.indexOf(t);e!==-1&&r.splice(e,1)}}}clearCsiHandler(e){this._csiHandlers[this._identifier(e)]&&delete this._csiHandlers[this._identifier(e)]}setCsiHandlerFallback(e){this._csiHandlerFb=e}registerDcsHandler(e,t){return this._dcsParser.registerHandler(this._identifier(e),t)}clearDcsHandler(e){this._dcsParser.clearHandler(this._identifier(e))}setDcsHandlerFallback(e){this._dcsParser.setHandlerFallback(e)}registerOscHandler(e,t){return this._oscParser.registerHandler(e,t)}clearOscHandler(e){this._oscParser.clearHandler(e)}setOscHandlerFallback(e){this._oscParser.setHandlerFallback(e)}setErrorHandler(e){this._errorHandler=e}clearErrorHandler(){this._errorHandler=this._errorHandlerFb}reset(){this.currentState=this.initialState,this._oscParser.reset(),this._dcsParser.reset(),this._params.reset(),this._params.addParam(0),this._collect=0,this.precedingJoinState=0,this._parseStack.state!==0&&(this._parseStack.state=2,this._parseStack.handlers=[])}_preserveStack(e,t,n,r,i){this._parseStack.state=e,this._parseStack.handlers=t,this._parseStack.handlerPos=n,this._parseStack.transition=r,this._parseStack.chunkPos=i}parse(e,t,n){let r,i=0,a=0,o=0;if(this._parseStack.state)if(this._parseStack.state===2)this._parseStack.state=0,o=this._parseStack.chunkPos+1;else{if(n===void 0||this._parseStack.state===1)throw this._parseStack.state=1,Error(`improper continuation due to previous async handler, giving up parsing`);let t=this._parseStack.handlers,a=this._parseStack.handlerPos-1;switch(this._parseStack.state){case 3:if(!1===n&&a>-1){for(;a>=0&&(r=t[a](this._params),!0!==r);a--)if(r instanceof Promise)return this._parseStack.handlerPos=a,r}this._parseStack.handlers=[];break;case 4:if(!1===n&&a>-1){for(;a>=0&&(r=t[a](),!0!==r);a--)if(r instanceof Promise)return this._parseStack.handlerPos=a,r}this._parseStack.handlers=[];break;case 6:if(i=e[this._parseStack.chunkPos],r=this._dcsParser.unhook(i!==24&&i!==26,n),r)return r;i===27&&(this._parseStack.transition|=1),this._params.reset(),this._params.addParam(0),this._collect=0;break;case 5:if(i=e[this._parseStack.chunkPos],r=this._oscParser.end(i!==24&&i!==26,n),r)return r;i===27&&(this._parseStack.transition|=1),this._params.reset(),this._params.addParam(0),this._collect=0}this._parseStack.state=0,o=this._parseStack.chunkPos+1,this.precedingJoinState=0,this.currentState=15&this._parseStack.transition}for(let n=o;n>4){case 2:for(let r=n+1;;++r){if(r>=t||(i=e[r])<32||i>126&&i<160){this._printHandler(e,n,r),n=r-1;break}if(++r>=t||(i=e[r])<32||i>126&&i<160){this._printHandler(e,n,r),n=r-1;break}if(++r>=t||(i=e[r])<32||i>126&&i<160){this._printHandler(e,n,r),n=r-1;break}if(++r>=t||(i=e[r])<32||i>126&&i<160){this._printHandler(e,n,r),n=r-1;break}}break;case 3:this._executeHandlers[i]?this._executeHandlers[i]():this._executeHandlerFb(i),this.precedingJoinState=0;break;case 0:break;case 1:if(this._errorHandler({position:n,code:i,currentState:this.currentState,collect:this._collect,params:this._params,abort:!1}).abort)return;break;case 7:let o=this._csiHandlers[this._collect<<8|i],s=o?o.length-1:-1;for(;s>=0&&(r=o[s](this._params),!0!==r);s--)if(r instanceof Promise)return this._preserveStack(3,o,s,a,n),r;s<0&&this._csiHandlerFb(this._collect<<8|i,this._params),this.precedingJoinState=0;break;case 8:do switch(i){case 59:this._params.addParam(0);break;case 58:this._params.addSubParam(-1);break;default:this._params.addDigit(i-48)}while(++n47&&i<60);n--;break;case 9:this._collect<<=8,this._collect|=i;break;case 10:let c=this._escHandlers[this._collect<<8|i],l=c?c.length-1:-1;for(;l>=0&&(r=c[l](),!0!==r);l--)if(r instanceof Promise)return this._preserveStack(4,c,l,a,n),r;l<0&&this._escHandlerFb(this._collect<<8|i),this.precedingJoinState=0;break;case 11:this._params.reset(),this._params.addParam(0),this._collect=0;break;case 12:this._dcsParser.hook(this._collect<<8|i,this._params);break;case 13:for(let r=n+1;;++r)if(r>=t||(i=e[r])===24||i===26||i===27||i>127&&i<160){this._dcsParser.put(e,n,r),n=r-1;break}break;case 14:if(r=this._dcsParser.unhook(i!==24&&i!==26),r)return this._preserveStack(6,[],0,a,n),r;i===27&&(a|=1),this._params.reset(),this._params.addParam(0),this._collect=0,this.precedingJoinState=0;break;case 4:this._oscParser.start();break;case 5:for(let r=n+1;;r++)if(r>=t||(i=e[r])<32||i>127&&i<160){this._oscParser.put(e,n,r),n=r-1;break}break;case 6:if(r=this._oscParser.end(i!==24&&i!==26),r)return this._preserveStack(5,[],0,a,n),r;i===27&&(a|=1),this._params.reset(),this._params.addParam(0),this._collect=0,this.precedingJoinState=0}this.currentState=15&a}}}t.EscapeSequenceParser=c},6242:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.OscHandler=t.OscParser=void 0;let r=n(5770),i=n(482),a=[];t.OscParser=class{constructor(){this._state=0,this._active=a,this._id=-1,this._handlers=Object.create(null),this._handlerFb=()=>{},this._stack={paused:!1,loopPosition:0,fallThrough:!1}}registerHandler(e,t){this._handlers[e]===void 0&&(this._handlers[e]=[]);let n=this._handlers[e];return n.push(t),{dispose:()=>{let e=n.indexOf(t);e!==-1&&n.splice(e,1)}}}clearHandler(e){this._handlers[e]&&delete this._handlers[e]}setHandlerFallback(e){this._handlerFb=e}dispose(){this._handlers=Object.create(null),this._handlerFb=()=>{},this._active=a}reset(){if(this._state===2)for(let e=this._stack.paused?this._stack.loopPosition-1:this._active.length-1;e>=0;--e)this._active[e].end(!1);this._stack.paused=!1,this._active=a,this._id=-1,this._state=0}_start(){if(this._active=this._handlers[this._id]||a,this._active.length)for(let e=this._active.length-1;e>=0;e--)this._active[e].start();else this._handlerFb(this._id,`START`)}_put(e,t,n){if(this._active.length)for(let r=this._active.length-1;r>=0;r--)this._active[r].put(e,t,n);else this._handlerFb(this._id,`PUT`,(0,i.utf32ToString)(e,t,n))}start(){this.reset(),this._state=1}put(e,t,n){if(this._state!==3){if(this._state===1)for(;t0&&this._put(e,t,n)}}end(e,t=!0){if(this._state!==0){if(this._state!==3)if(this._state===1&&this._start(),this._active.length){let n=!1,r=this._active.length-1,i=!1;if(this._stack.paused&&(r=this._stack.loopPosition-1,n=t,i=this._stack.fallThrough,this._stack.paused=!1),!i&&!1===n){for(;r>=0&&(n=this._active[r].end(e),!0!==n);r--)if(n instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=r,this._stack.fallThrough=!1,n;r--}for(;r>=0;r--)if(n=this._active[r].end(!1),n instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=r,this._stack.fallThrough=!0,n}else this._handlerFb(this._id,`END`,e);this._active=a,this._id=-1,this._state=0}}},t.OscHandler=class{constructor(e){this._handler=e,this._data=``,this._hitLimit=!1}start(){this._data=``,this._hitLimit=!1}put(e,t,n){this._hitLimit||(this._data+=(0,i.utf32ToString)(e,t,n),this._data.length>r.PAYLOAD_LIMIT&&(this._data=``,this._hitLimit=!0))}end(e){let t=!1;if(this._hitLimit)t=!1;else if(e&&(t=this._handler(this._data),t instanceof Promise))return t.then((e=>(this._data=``,this._hitLimit=!1,e)));return this._data=``,this._hitLimit=!1,t}}},8742:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Params=void 0;let n=2147483647;class r{static fromArray(e){let t=new r;if(!e.length)return t;for(let n=+!!Array.isArray(e[0]);n256)throw Error(`maxSubParamsLength must not be greater than 256`);this.params=new Int32Array(e),this.length=0,this._subParams=new Int32Array(t),this._subParamsLength=0,this._subParamsIdx=new Uint16Array(e),this._rejectDigits=!1,this._rejectSubDigits=!1,this._digitIsSub=!1}clone(){let e=new r(this.maxLength,this.maxSubParamsLength);return e.params.set(this.params),e.length=this.length,e._subParams.set(this._subParams),e._subParamsLength=this._subParamsLength,e._subParamsIdx.set(this._subParamsIdx),e._rejectDigits=this._rejectDigits,e._rejectSubDigits=this._rejectSubDigits,e._digitIsSub=this._digitIsSub,e}toArray(){let e=[];for(let t=0;t>8,r=255&this._subParamsIdx[t];r-n>0&&e.push(Array.prototype.slice.call(this._subParams,n,r))}return e}reset(){this.length=0,this._subParamsLength=0,this._rejectDigits=!1,this._rejectSubDigits=!1,this._digitIsSub=!1}addParam(e){if(this._digitIsSub=!1,this.length>=this.maxLength)this._rejectDigits=!0;else{if(e<-1)throw Error(`values lesser than -1 are not allowed`);this._subParamsIdx[this.length]=this._subParamsLength<<8|this._subParamsLength,this.params[this.length++]=e>n?n:e}}addSubParam(e){if(this._digitIsSub=!0,this.length)if(this._rejectDigits||this._subParamsLength>=this.maxSubParamsLength)this._rejectSubDigits=!0;else{if(e<-1)throw Error(`values lesser than -1 are not allowed`);this._subParams[this._subParamsLength++]=e>n?n:e,this._subParamsIdx[this.length-1]++}}hasSubParams(e){return(255&this._subParamsIdx[e])-(this._subParamsIdx[e]>>8)>0}getSubParams(e){let t=this._subParamsIdx[e]>>8,n=255&this._subParamsIdx[e];return n-t>0?this._subParams.subarray(t,n):null}getSubParamsAll(){let e={};for(let t=0;t>8,r=255&this._subParamsIdx[t];r-n>0&&(e[t]=this._subParams.slice(n,r))}return e}addDigit(e){let t;if(this._rejectDigits||!(t=this._digitIsSub?this._subParamsLength:this.length)||this._digitIsSub&&this._rejectSubDigits)return;let r=this._digitIsSub?this._subParams:this.params,i=r[t-1];r[t-1]=~i?Math.min(10*i+e,n):e}}t.Params=r},5741:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.AddonManager=void 0,t.AddonManager=class{constructor(){this._addons=[]}dispose(){for(let e=this._addons.length-1;e>=0;e--)this._addons[e].instance.dispose()}loadAddon(e,t){let n={instance:t,dispose:t.dispose,isDisposed:!1};this._addons.push(n),t.dispose=()=>this._wrappedAddonDispose(n),t.activate(e)}_wrappedAddonDispose(e){if(e.isDisposed)return;let t=-1;for(let n=0;n{Object.defineProperty(t,"__esModule",{value:!0}),t.BufferApiView=void 0;let r=n(3785),i=n(511);t.BufferApiView=class{constructor(e,t){this._buffer=e,this.type=t}init(e){return this._buffer=e,this}get cursorY(){return this._buffer.y}get cursorX(){return this._buffer.x}get viewportY(){return this._buffer.ydisp}get baseY(){return this._buffer.ybase}get length(){return this._buffer.lines.length}getLine(e){let t=this._buffer.lines.get(e);if(t)return new r.BufferLineApiView(t)}getNullCell(){return new i.CellData}}},3785:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.BufferLineApiView=void 0;let r=n(511);t.BufferLineApiView=class{constructor(e){this._line=e}get isWrapped(){return this._line.isWrapped}get length(){return this._line.length}getCell(e,t){if(!(e<0||e>=this._line.length))return t?(this._line.loadCell(e,t),t):this._line.loadCell(e,new r.CellData)}translateToString(e,t,n){return this._line.translateToString(e,t,n)}}},8285:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.BufferNamespaceApi=void 0;let r=n(8771),i=n(8460),a=n(844);class o extends a.Disposable{constructor(e){super(),this._core=e,this._onBufferChange=this.register(new i.EventEmitter),this.onBufferChange=this._onBufferChange.event,this._normal=new r.BufferApiView(this._core.buffers.normal,`normal`),this._alternate=new r.BufferApiView(this._core.buffers.alt,`alternate`),this._core.buffers.onBufferActivate((()=>this._onBufferChange.fire(this.active)))}get active(){if(this._core.buffers.active===this._core.buffers.normal)return this.normal;if(this._core.buffers.active===this._core.buffers.alt)return this.alternate;throw Error(`Active buffer is neither normal nor alternate`)}get normal(){return this._normal.init(this._core.buffers.normal)}get alternate(){return this._alternate.init(this._core.buffers.alt)}}t.BufferNamespaceApi=o},7975:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ParserApi=void 0,t.ParserApi=class{constructor(e){this._core=e}registerCsiHandler(e,t){return this._core.registerCsiHandler(e,(e=>t(e.toArray())))}addCsiHandler(e,t){return this.registerCsiHandler(e,t)}registerDcsHandler(e,t){return this._core.registerDcsHandler(e,((e,n)=>t(e,n.toArray())))}addDcsHandler(e,t){return this.registerDcsHandler(e,t)}registerEscHandler(e,t){return this._core.registerEscHandler(e,t)}addEscHandler(e,t){return this.registerEscHandler(e,t)}registerOscHandler(e,t){return this._core.registerOscHandler(e,t)}addOscHandler(e,t){return this.registerOscHandler(e,t)}}},7090:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.UnicodeApi=void 0,t.UnicodeApi=class{constructor(e){this._core=e}register(e){this._core.unicodeService.register(e)}get versions(){return this._core.unicodeService.versions}get activeVersion(){return this._core.unicodeService.activeVersion}set activeVersion(e){this._core.unicodeService.activeVersion=e}}},744:function(e,t,n){var r=this&&this.__decorate||function(e,t,n,r){var i,a=arguments.length,o=a<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r;if(typeof Reflect==`object`&&typeof Reflect.decorate==`function`)o=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(i=e[s])&&(o=(a<3?i(o):a>3?i(t,n,o):i(t,n))||o);return a>3&&o&&Object.defineProperty(t,n,o),o},i=this&&this.__param||function(e,t){return function(n,r){t(n,r,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.BufferService=t.MINIMUM_ROWS=t.MINIMUM_COLS=void 0;let a=n(8460),o=n(844),s=n(5295),c=n(2585);t.MINIMUM_COLS=2,t.MINIMUM_ROWS=1;let l=t.BufferService=class extends o.Disposable{get buffer(){return this.buffers.active}constructor(e){super(),this.isUserScrolling=!1,this._onResize=this.register(new a.EventEmitter),this.onResize=this._onResize.event,this._onScroll=this.register(new a.EventEmitter),this.onScroll=this._onScroll.event,this.cols=Math.max(e.rawOptions.cols||0,t.MINIMUM_COLS),this.rows=Math.max(e.rawOptions.rows||0,t.MINIMUM_ROWS),this.buffers=this.register(new s.BufferSet(e,this))}resize(e,t){this.cols=e,this.rows=t,this.buffers.resize(e,t),this._onResize.fire({cols:e,rows:t})}reset(){this.buffers.reset(),this.isUserScrolling=!1}scroll(e,t=!1){let n=this.buffer,r;r=this._cachedBlankLine,r&&r.length===this.cols&&r.getFg(0)===e.fg&&r.getBg(0)===e.bg||(r=n.getBlankLine(e,t),this._cachedBlankLine=r),r.isWrapped=t;let i=n.ybase+n.scrollTop,a=n.ybase+n.scrollBottom;if(n.scrollTop===0){let e=n.lines.isFull;a===n.lines.length-1?e?n.lines.recycle().copyFrom(r):n.lines.push(r.clone()):n.lines.splice(a+1,0,r.clone()),e?this.isUserScrolling&&(n.ydisp=Math.max(n.ydisp-1,0)):(n.ybase++,this.isUserScrolling||n.ydisp++)}else{let e=a-i+1;n.lines.shiftElements(i+1,e-1,-1),n.lines.set(a,r.clone())}this.isUserScrolling||(n.ydisp=n.ybase),this._onScroll.fire(n.ydisp)}scrollLines(e,t,n){let r=this.buffer;if(e<0){if(r.ydisp===0)return;this.isUserScrolling=!0}else e+r.ydisp>=r.ybase&&(this.isUserScrolling=!1);let i=r.ydisp;r.ydisp=Math.max(Math.min(r.ydisp+e,r.ybase),0),i!==r.ydisp&&(t||this._onScroll.fire(r.ydisp))}};t.BufferService=l=r([i(0,c.IOptionsService)],l)},7994:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.CharsetService=void 0,t.CharsetService=class{constructor(){this.glevel=0,this._charsets=[]}reset(){this.charset=void 0,this._charsets=[],this.glevel=0}setgLevel(e){this.glevel=e,this.charset=this._charsets[e]}setgCharset(e,t){this._charsets[e]=t,this.glevel===e&&(this.charset=t)}}},1753:function(e,t,n){var r=this&&this.__decorate||function(e,t,n,r){var i,a=arguments.length,o=a<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r;if(typeof Reflect==`object`&&typeof Reflect.decorate==`function`)o=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(i=e[s])&&(o=(a<3?i(o):a>3?i(t,n,o):i(t,n))||o);return a>3&&o&&Object.defineProperty(t,n,o),o},i=this&&this.__param||function(e,t){return function(n,r){t(n,r,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.CoreMouseService=void 0;let a=n(2585),o=n(8460),s=n(844),c={NONE:{events:0,restrict:()=>!1},X10:{events:1,restrict:e=>e.button!==4&&e.action===1&&(e.ctrl=!1,e.alt=!1,e.shift=!1,!0)},VT200:{events:19,restrict:e=>e.action!==32},DRAG:{events:23,restrict:e=>e.action!==32||e.button!==3},ANY:{events:31,restrict:e=>!0}};function l(e,t){let n=(e.ctrl?16:0)|(e.shift?4:0)|(e.alt?8:0);return e.button===4?(n|=64,n|=e.action):(n|=3&e.button,4&e.button&&(n|=64),8&e.button&&(n|=128),e.action===32?n|=32:e.action!==0||t||(n|=3)),n}let u=String.fromCharCode,d={DEFAULT:e=>{let t=[l(e,!1)+32,e.col+32,e.row+32];return t[0]>255||t[1]>255||t[2]>255?``:`${u(t[0])}${u(t[1])}${u(t[2])}`},SGR:e=>{let t=e.action===0&&e.button!==4?`m`:`M`;return`[<${l(e,!0)};${e.col};${e.row}${t}`},SGR_PIXELS:e=>{let t=e.action===0&&e.button!==4?`m`:`M`;return`[<${l(e,!0)};${e.x};${e.y}${t}`}},f=t.CoreMouseService=class extends s.Disposable{constructor(e,t){super(),this._bufferService=e,this._coreService=t,this._protocols={},this._encodings={},this._activeProtocol=``,this._activeEncoding=``,this._lastEvent=null,this._onProtocolChange=this.register(new o.EventEmitter),this.onProtocolChange=this._onProtocolChange.event;for(let e of Object.keys(c))this.addProtocol(e,c[e]);for(let e of Object.keys(d))this.addEncoding(e,d[e]);this.reset()}addProtocol(e,t){this._protocols[e]=t}addEncoding(e,t){this._encodings[e]=t}get activeProtocol(){return this._activeProtocol}get areMouseEventsActive(){return this._protocols[this._activeProtocol].events!==0}set activeProtocol(e){if(!this._protocols[e])throw Error(`unknown protocol "${e}"`);this._activeProtocol=e,this._onProtocolChange.fire(this._protocols[e].events)}get activeEncoding(){return this._activeEncoding}set activeEncoding(e){if(!this._encodings[e])throw Error(`unknown encoding "${e}"`);this._activeEncoding=e}reset(){this.activeProtocol=`NONE`,this.activeEncoding=`DEFAULT`,this._lastEvent=null}triggerMouseEvent(e){if(e.col<0||e.col>=this._bufferService.cols||e.row<0||e.row>=this._bufferService.rows||e.button===4&&e.action===32||e.button===3&&e.action!==32||e.button!==4&&(e.action===2||e.action===3)||(e.col++,e.row++,e.action===32&&this._lastEvent&&this._equalEvents(this._lastEvent,e,this._activeEncoding===`SGR_PIXELS`))||!this._protocols[this._activeProtocol].restrict(e))return!1;let t=this._encodings[this._activeEncoding](e);return t&&(this._activeEncoding===`DEFAULT`?this._coreService.triggerBinaryEvent(t):this._coreService.triggerDataEvent(t,!0)),this._lastEvent=e,!0}explainEvents(e){return{down:!!(1&e),up:!!(2&e),drag:!!(4&e),move:!!(8&e),wheel:!!(16&e)}}_equalEvents(e,t,n){if(n){if(e.x!==t.x||e.y!==t.y)return!1}else if(e.col!==t.col||e.row!==t.row)return!1;return e.button===t.button&&e.action===t.action&&e.ctrl===t.ctrl&&e.alt===t.alt&&e.shift===t.shift}};t.CoreMouseService=f=r([i(0,a.IBufferService),i(1,a.ICoreService)],f)},6975:function(e,t,n){var r=this&&this.__decorate||function(e,t,n,r){var i,a=arguments.length,o=a<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r;if(typeof Reflect==`object`&&typeof Reflect.decorate==`function`)o=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(i=e[s])&&(o=(a<3?i(o):a>3?i(t,n,o):i(t,n))||o);return a>3&&o&&Object.defineProperty(t,n,o),o},i=this&&this.__param||function(e,t){return function(n,r){t(n,r,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.CoreService=void 0;let a=n(1439),o=n(8460),s=n(844),c=n(2585),l=Object.freeze({insertMode:!1}),u=Object.freeze({applicationCursorKeys:!1,applicationKeypad:!1,bracketedPasteMode:!1,origin:!1,reverseWraparound:!1,sendFocus:!1,wraparound:!0}),d=t.CoreService=class extends s.Disposable{constructor(e,t,n){super(),this._bufferService=e,this._logService=t,this._optionsService=n,this.isCursorInitialized=!1,this.isCursorHidden=!1,this._onData=this.register(new o.EventEmitter),this.onData=this._onData.event,this._onUserInput=this.register(new o.EventEmitter),this.onUserInput=this._onUserInput.event,this._onBinary=this.register(new o.EventEmitter),this.onBinary=this._onBinary.event,this._onRequestScrollToBottom=this.register(new o.EventEmitter),this.onRequestScrollToBottom=this._onRequestScrollToBottom.event,this.modes=(0,a.clone)(l),this.decPrivateModes=(0,a.clone)(u)}reset(){this.modes=(0,a.clone)(l),this.decPrivateModes=(0,a.clone)(u)}triggerDataEvent(e,t=!1){if(this._optionsService.rawOptions.disableStdin)return;let n=this._bufferService.buffer;t&&this._optionsService.rawOptions.scrollOnUserInput&&n.ybase!==n.ydisp&&this._onRequestScrollToBottom.fire(),t&&this._onUserInput.fire(),this._logService.debug(`sending data "${e}"`,(()=>e.split(``).map((e=>e.charCodeAt(0))))),this._onData.fire(e)}triggerBinaryEvent(e){this._optionsService.rawOptions.disableStdin||(this._logService.debug(`sending binary "${e}"`,(()=>e.split(``).map((e=>e.charCodeAt(0))))),this._onBinary.fire(e))}};t.CoreService=d=r([i(0,c.IBufferService),i(1,c.ILogService),i(2,c.IOptionsService)],d)},9074:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.DecorationService=void 0;let r=n(8055),i=n(8460),a=n(844),o=n(6106),s=0,c=0;class l extends a.Disposable{get decorations(){return this._decorations.values()}constructor(){super(),this._decorations=new o.SortedList((e=>e?.marker.line)),this._onDecorationRegistered=this.register(new i.EventEmitter),this.onDecorationRegistered=this._onDecorationRegistered.event,this._onDecorationRemoved=this.register(new i.EventEmitter),this.onDecorationRemoved=this._onDecorationRemoved.event,this.register((0,a.toDisposable)((()=>this.reset())))}registerDecoration(e){if(e.marker.isDisposed)return;let t=new u(e);if(t){let e=t.marker.onDispose((()=>t.dispose()));t.onDispose((()=>{t&&(this._decorations.delete(t)&&this._onDecorationRemoved.fire(t),e.dispose())})),this._decorations.insert(t),this._onDecorationRegistered.fire(t)}return t}reset(){for(let e of this._decorations.values())e.dispose();this._decorations.clear()}*getDecorationsAtCell(e,t,n){let r=0,i=0;for(let a of this._decorations.getKeyIterator(t))r=a.options.x??0,i=r+(a.options.width??1),e>=r&&e{s=t.options.x??0,c=s+(t.options.width??1),e>=s&&e{Object.defineProperty(t,"__esModule",{value:!0}),t.InstantiationService=t.ServiceCollection=void 0;let r=n(2585),i=n(8343);class a{constructor(...e){this._entries=new Map;for(let[t,n]of e)this.set(t,n)}set(e,t){let n=this._entries.get(e);return this._entries.set(e,t),n}forEach(e){for(let[t,n]of this._entries.entries())e(t,n)}has(e){return this._entries.has(e)}get(e){return this._entries.get(e)}}t.ServiceCollection=a,t.InstantiationService=class{constructor(){this._services=new a,this._services.set(r.IInstantiationService,this)}setService(e,t){this._services.set(e,t)}getService(e){return this._services.get(e)}createInstance(e,...t){let n=(0,i.getServiceDependencies)(e).sort(((e,t)=>e.index-t.index)),r=[];for(let t of n){let n=this._services.get(t.id);if(!n)throw Error(`[createInstance] ${e.name} depends on UNKNOWN service ${t.id}.`);r.push(n)}let a=n.length>0?n[0].index:t.length;if(t.length!==a)throw Error(`[createInstance] First service dependency of ${e.name} at position ${a+1} conflicts with ${t.length} static arguments`);return new e(...t,...r)}}},7866:function(e,t,n){var r=this&&this.__decorate||function(e,t,n,r){var i,a=arguments.length,o=a<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r;if(typeof Reflect==`object`&&typeof Reflect.decorate==`function`)o=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(i=e[s])&&(o=(a<3?i(o):a>3?i(t,n,o):i(t,n))||o);return a>3&&o&&Object.defineProperty(t,n,o),o},i=this&&this.__param||function(e,t){return function(n,r){t(n,r,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.traceCall=t.setTraceLogger=t.LogService=void 0;let a=n(844),o=n(2585),s={trace:o.LogLevelEnum.TRACE,debug:o.LogLevelEnum.DEBUG,info:o.LogLevelEnum.INFO,warn:o.LogLevelEnum.WARN,error:o.LogLevelEnum.ERROR,off:o.LogLevelEnum.OFF},c,l=t.LogService=class extends a.Disposable{get logLevel(){return this._logLevel}constructor(e){super(),this._optionsService=e,this._logLevel=o.LogLevelEnum.OFF,this._updateLogLevel(),this.register(this._optionsService.onSpecificOptionChange(`logLevel`,(()=>this._updateLogLevel()))),c=this}_updateLogLevel(){this._logLevel=s[this._optionsService.rawOptions.logLevel]}_evalLazyOptionalParams(e){for(let t=0;tJSON.stringify(e))).join(`, `)})`);let t=r.apply(this,e);return c.trace(`GlyphRenderer#${r.name} return`,t),t}}},7302:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.OptionsService=t.DEFAULT_OPTIONS=void 0;let r=n(8460),i=n(844);t.DEFAULT_OPTIONS={cols:80,rows:24,cursorBlink:!1,cursorStyle:`block`,cursorWidth:1,cursorInactiveStyle:`outline`,customGlyphs:!0,drawBoldTextInBrightColors:!0,documentOverride:null,fastScrollModifier:`alt`,fastScrollSensitivity:5,fontFamily:`courier-new, courier, monospace`,fontSize:15,fontWeight:`normal`,fontWeightBold:`bold`,ignoreBracketedPasteMode:!1,lineHeight:1,letterSpacing:0,linkHandler:null,logLevel:`info`,logger:null,scrollback:1e3,scrollOnUserInput:!0,scrollSensitivity:1,screenReaderMode:!1,smoothScrollDuration:0,macOptionIsMeta:!1,macOptionClickForcesSelection:!1,minimumContrastRatio:1,disableStdin:!1,allowProposedApi:!1,allowTransparency:!1,tabStopWidth:8,theme:{},rescaleOverlappingGlyphs:!1,rightClickSelectsWord:n(6114).isMac,windowOptions:{},windowsMode:!1,windowsPty:{},wordSeparator:` ()[]{}',"\``,altClickMovesCursor:!0,convertEol:!1,termName:`xterm`,cancelEvents:!1,overviewRulerWidth:0};let a=[`normal`,`bold`,`100`,`200`,`300`,`400`,`500`,`600`,`700`,`800`,`900`];class o extends i.Disposable{constructor(e){super(),this._onOptionChange=this.register(new r.EventEmitter),this.onOptionChange=this._onOptionChange.event;let n={...t.DEFAULT_OPTIONS};for(let t in e)if(t in n)try{let r=e[t];n[t]=this._sanitizeAndValidateOption(t,r)}catch(e){console.error(e)}this.rawOptions=n,this.options={...n},this._setupOptions(),this.register((0,i.toDisposable)((()=>{this.rawOptions.linkHandler=null,this.rawOptions.documentOverride=null})))}onSpecificOptionChange(e,t){return this.onOptionChange((n=>{n===e&&t(this.rawOptions[e])}))}onMultipleOptionChange(e,t){return this.onOptionChange((n=>{e.indexOf(n)!==-1&&t()}))}_setupOptions(){let e=e=>{if(!(e in t.DEFAULT_OPTIONS))throw Error(`No option with key "${e}"`);return this.rawOptions[e]},n=(e,n)=>{if(!(e in t.DEFAULT_OPTIONS))throw Error(`No option with key "${e}"`);n=this._sanitizeAndValidateOption(e,n),this.rawOptions[e]!==n&&(this.rawOptions[e]=n,this._onOptionChange.fire(e))};for(let t in this.rawOptions){let r={get:e.bind(this,t),set:n.bind(this,t)};Object.defineProperty(this.options,t,r)}}_sanitizeAndValidateOption(e,n){switch(e){case`cursorStyle`:if(n||=t.DEFAULT_OPTIONS[e],!function(e){return e===`block`||e===`underline`||e===`bar`}(n))throw Error(`"${n}" is not a valid value for ${e}`);break;case`wordSeparator`:n||=t.DEFAULT_OPTIONS[e];break;case`fontWeight`:case`fontWeightBold`:if(typeof n==`number`&&1<=n&&n<=1e3)break;n=a.includes(n)?n:t.DEFAULT_OPTIONS[e];break;case`cursorWidth`:n=Math.floor(n);case`lineHeight`:case`tabStopWidth`:if(n<1)throw Error(`${e} cannot be less than 1, value: ${n}`);break;case`minimumContrastRatio`:n=Math.max(1,Math.min(21,Math.round(10*n)/10));break;case`scrollback`:if((n=Math.min(n,4294967295))<0)throw Error(`${e} cannot be less than 0, value: ${n}`);break;case`fastScrollSensitivity`:case`scrollSensitivity`:if(n<=0)throw Error(`${e} cannot be less than or equal to 0, value: ${n}`);break;case`rows`:case`cols`:if(!n&&n!==0)throw Error(`${e} must be numeric, value: ${n}`);break;case`windowsPty`:n??={}}return n}}t.OptionsService=o},2660:function(e,t,n){var r=this&&this.__decorate||function(e,t,n,r){var i,a=arguments.length,o=a<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r;if(typeof Reflect==`object`&&typeof Reflect.decorate==`function`)o=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(i=e[s])&&(o=(a<3?i(o):a>3?i(t,n,o):i(t,n))||o);return a>3&&o&&Object.defineProperty(t,n,o),o},i=this&&this.__param||function(e,t){return function(n,r){t(n,r,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.OscLinkService=void 0;let a=n(2585),o=t.OscLinkService=class{constructor(e){this._bufferService=e,this._nextId=1,this._entriesWithId=new Map,this._dataByLinkId=new Map}registerLink(e){let t=this._bufferService.buffer;if(e.id===void 0){let n=t.addMarker(t.ybase+t.y),r={data:e,id:this._nextId++,lines:[n]};return n.onDispose((()=>this._removeMarkerFromLink(r,n))),this._dataByLinkId.set(r.id,r),r.id}let n=e,r=this._getEntryIdKey(n),i=this._entriesWithId.get(r);if(i)return this.addLineToLink(i.id,t.ybase+t.y),i.id;let a=t.addMarker(t.ybase+t.y),o={id:this._nextId++,key:this._getEntryIdKey(n),data:n,lines:[a]};return a.onDispose((()=>this._removeMarkerFromLink(o,a))),this._entriesWithId.set(o.key,o),this._dataByLinkId.set(o.id,o),o.id}addLineToLink(e,t){let n=this._dataByLinkId.get(e);if(n&&n.lines.every((e=>e.line!==t))){let e=this._bufferService.buffer.addMarker(t);n.lines.push(e),e.onDispose((()=>this._removeMarkerFromLink(n,e)))}}getLinkData(e){return this._dataByLinkId.get(e)?.data}_getEntryIdKey(e){return`${e.id};;${e.uri}`}_removeMarkerFromLink(e,t){let n=e.lines.indexOf(t);n!==-1&&(e.lines.splice(n,1),e.lines.length===0&&(e.data.id!==void 0&&this._entriesWithId.delete(e.key),this._dataByLinkId.delete(e.id)))}};t.OscLinkService=o=r([i(0,a.IBufferService)],o)},8343:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.createDecorator=t.getServiceDependencies=t.serviceRegistry=void 0;let n=`di$target`,r=`di$dependencies`;t.serviceRegistry=new Map,t.getServiceDependencies=function(e){return e[r]||[]},t.createDecorator=function(e){if(t.serviceRegistry.has(e))return t.serviceRegistry.get(e);let i=function(e,t,a){if(arguments.length!==3)throw Error(`@IServiceName-decorator can only be used to decorate a parameter`);(function(e,t,i){t[n]===t?t[r].push({id:e,index:i}):(t[r]=[{id:e,index:i}],t[n]=t)})(i,e,a)};return i.toString=()=>e,t.serviceRegistry.set(e,i),i}},2585:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.IDecorationService=t.IUnicodeService=t.IOscLinkService=t.IOptionsService=t.ILogService=t.LogLevelEnum=t.IInstantiationService=t.ICharsetService=t.ICoreService=t.ICoreMouseService=t.IBufferService=void 0;let r=n(8343);var i;t.IBufferService=(0,r.createDecorator)(`BufferService`),t.ICoreMouseService=(0,r.createDecorator)(`CoreMouseService`),t.ICoreService=(0,r.createDecorator)(`CoreService`),t.ICharsetService=(0,r.createDecorator)(`CharsetService`),t.IInstantiationService=(0,r.createDecorator)(`InstantiationService`),function(e){e[e.TRACE=0]=`TRACE`,e[e.DEBUG=1]=`DEBUG`,e[e.INFO=2]=`INFO`,e[e.WARN=3]=`WARN`,e[e.ERROR=4]=`ERROR`,e[e.OFF=5]=`OFF`}(i||(t.LogLevelEnum=i={})),t.ILogService=(0,r.createDecorator)(`LogService`),t.IOptionsService=(0,r.createDecorator)(`OptionsService`),t.IOscLinkService=(0,r.createDecorator)(`OscLinkService`),t.IUnicodeService=(0,r.createDecorator)(`UnicodeService`),t.IDecorationService=(0,r.createDecorator)(`DecorationService`)},1480:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.UnicodeService=void 0;let r=n(8460),i=n(225);class a{static extractShouldJoin(e){return(1&e)!=0}static extractWidth(e){return e>>1&3}static extractCharKind(e){return e>>3}static createPropertyValue(e,t,n=!1){return(16777215&e)<<3|(3&t)<<1|!!n}constructor(){this._providers=Object.create(null),this._active=``,this._onChange=new r.EventEmitter,this.onChange=this._onChange.event;let e=new i.UnicodeV6;this.register(e),this._active=e.version,this._activeProvider=e}dispose(){this._onChange.dispose()}get versions(){return Object.keys(this._providers)}get activeVersion(){return this._active}set activeVersion(e){if(!this._providers[e])throw Error(`unknown Unicode version "${e}"`);this._active=e,this._activeProvider=this._providers[e],this._onChange.fire(e)}register(e){this._providers[e.version]=e}wcwidth(e){return this._activeProvider.wcwidth(e)}getStringCellWidth(e){let t=0,n=0,r=e.length;for(let i=0;i=r)return t+this.wcwidth(o);let n=e.charCodeAt(i);56320<=n&&n<=57343?o=1024*(o-55296)+n-56320+65536:t+=this.wcwidth(n)}let s=this.charProperties(o,n),c=a.extractWidth(s);a.extractShouldJoin(s)&&(c-=a.extractWidth(n)),t+=c,n=s}return t}charProperties(e,t){return this._activeProvider.charProperties(e,t)}}t.UnicodeService=a}},t={};function n(r){var i=t[r];if(i!==void 0)return i.exports;var a=t[r]={exports:{}};return e[r].call(a.exports,a,a.exports,n),a.exports}var r={};return(()=>{var e=r;Object.defineProperty(e,"__esModule",{value:!0}),e.Terminal=void 0;let t=n(9042),i=n(3236),a=n(844),o=n(5741),s=n(8285),c=n(7975),l=n(7090),u=[`cols`,`rows`];class d extends a.Disposable{constructor(e){super(),this._core=this.register(new i.Terminal(e)),this._addonManager=this.register(new o.AddonManager),this._publicOptions={...this._core.options};let t=e=>this._core.options[e],n=(e,t)=>{this._checkReadonlyOptions(e),this._core.options[e]=t};for(let e in this._core.options){let r={get:t.bind(this,e),set:n.bind(this,e)};Object.defineProperty(this._publicOptions,e,r)}}_checkReadonlyOptions(e){if(u.includes(e))throw Error(`Option "${e}" can only be set in the constructor`)}_checkProposedApi(){if(!this._core.optionsService.rawOptions.allowProposedApi)throw Error(`You must set the allowProposedApi option to true to use proposed API`)}get onBell(){return this._core.onBell}get onBinary(){return this._core.onBinary}get onCursorMove(){return this._core.onCursorMove}get onData(){return this._core.onData}get onKey(){return this._core.onKey}get onLineFeed(){return this._core.onLineFeed}get onRender(){return this._core.onRender}get onResize(){return this._core.onResize}get onScroll(){return this._core.onScroll}get onSelectionChange(){return this._core.onSelectionChange}get onTitleChange(){return this._core.onTitleChange}get onWriteParsed(){return this._core.onWriteParsed}get element(){return this._core.element}get parser(){return this._parser||=new c.ParserApi(this._core),this._parser}get unicode(){return this._checkProposedApi(),new l.UnicodeApi(this._core)}get textarea(){return this._core.textarea}get rows(){return this._core.rows}get cols(){return this._core.cols}get buffer(){return this._buffer||=this.register(new s.BufferNamespaceApi(this._core)),this._buffer}get markers(){return this._checkProposedApi(),this._core.markers}get modes(){let e=this._core.coreService.decPrivateModes,t=`none`;switch(this._core.coreMouseService.activeProtocol){case`X10`:t=`x10`;break;case`VT200`:t=`vt200`;break;case`DRAG`:t=`drag`;break;case`ANY`:t=`any`}return{applicationCursorKeysMode:e.applicationCursorKeys,applicationKeypadMode:e.applicationKeypad,bracketedPasteMode:e.bracketedPasteMode,insertMode:this._core.coreService.modes.insertMode,mouseTrackingMode:t,originMode:e.origin,reverseWraparoundMode:e.reverseWraparound,sendFocusMode:e.sendFocus,wraparoundMode:e.wraparound}}get options(){return this._publicOptions}set options(e){for(let t in e)this._publicOptions[t]=e[t]}blur(){this._core.blur()}focus(){this._core.focus()}input(e,t=!0){this._core.input(e,t)}resize(e,t){this._verifyIntegers(e,t),this._core.resize(e,t)}open(e){this._core.open(e)}attachCustomKeyEventHandler(e){this._core.attachCustomKeyEventHandler(e)}attachCustomWheelEventHandler(e){this._core.attachCustomWheelEventHandler(e)}registerLinkProvider(e){return this._core.registerLinkProvider(e)}registerCharacterJoiner(e){return this._checkProposedApi(),this._core.registerCharacterJoiner(e)}deregisterCharacterJoiner(e){this._checkProposedApi(),this._core.deregisterCharacterJoiner(e)}registerMarker(e=0){return this._verifyIntegers(e),this._core.registerMarker(e)}registerDecoration(e){return this._checkProposedApi(),this._verifyPositiveIntegers(e.x??0,e.width??0,e.height??0),this._core.registerDecoration(e)}hasSelection(){return this._core.hasSelection()}select(e,t,n){this._verifyIntegers(e,t,n),this._core.select(e,t,n)}getSelection(){return this._core.getSelection()}getSelectionPosition(){return this._core.getSelectionPosition()}clearSelection(){this._core.clearSelection()}selectAll(){this._core.selectAll()}selectLines(e,t){this._verifyIntegers(e,t),this._core.selectLines(e,t)}dispose(){super.dispose()}scrollLines(e){this._verifyIntegers(e),this._core.scrollLines(e)}scrollPages(e){this._verifyIntegers(e),this._core.scrollPages(e)}scrollToTop(){this._core.scrollToTop()}scrollToBottom(){this._core.scrollToBottom()}scrollToLine(e){this._verifyIntegers(e),this._core.scrollToLine(e)}clear(){this._core.clear()}write(e,t){this._core.write(e,t)}writeln(e,t){this._core.write(e),this._core.write(`\r -`,t)}paste(e){this._core.paste(e)}refresh(e,t){this._verifyIntegers(e,t),this._core.refresh(e,t)}reset(){this._core.reset()}clearTextureAtlas(){this._core.clearTextureAtlas()}loadAddon(e){this._addonManager.loadAddon(this,e)}static get strings(){return t}_verifyIntegers(...e){for(let t of e)if(t===1/0||isNaN(t)||t%1!=0)throw Error(`This API only accepts integers`)}_verifyPositiveIntegers(...e){for(let t of e)if(t&&(t===1/0||isNaN(t)||t%1!=0||t<0))throw Error(`This API only accepts positive integers`)}}e.Terminal=d})(),r})()))})),l=a(n(),1),u=s(),d=c(),f=i(),p=r({flex:`1`,minHeight:`0`,minWidth:`0`,padding:`0.35rem 0.5rem`,background:`#070b0f`,borderWidth:`1px`,borderStyle:`solid`,borderColor:`grid`,borderRadius:`3px`,overflow:`hidden`,"& .xterm":{height:`100%`},"& .xterm-viewport":{overflowY:`auto !important`}}),m={background:`#070b0f`,foreground:`#d6e7da`,cursor:`#7fe0b0`,cursorAccent:`#070b0f`,selectionBackground:`#1f3b30`,black:`#0b0f14`,green:`#7fe0b0`,yellow:`#e8c170`,cyan:`#6fb8d6`,red:`#e06c75`,brightBlack:`#3a4750`,white:`#d6e7da`},h=40,g=3,_=1,v=2,y=`\x1B[5~`,b=`\x1B[6~`;function x(){return typeof document>`u`||document.documentElement.dataset.effects!==`off`}function S(e,t,n){if(e.deltaY===0)return[0,n];let r=e.deltaY>0?1:-1,i=Math.abs(e.deltaY);if(e.deltaMode===v)return[r*Math.max(1,Math.ceil(i)*Math.max(1,t-1)),0];if(e.deltaMode===_)return[r*Math.max(1,Math.ceil(i)),0];let a=n+e.deltaY,o=Math.trunc(a/h);return[o,a-o*h]}function C(e,t){let n=t+e,r=Math.trunc(n/g);return r===0?[``,n]:[(r<0?y:b).repeat(Math.abs(r)),n-r*g]}function w(e){return e.buffer.active.type===`normal`&&e.buffer.active.baseY>0}function T({sessionId:t,onConnection:n}){let r=(0,l.useRef)(null),i=(0,l.useContext)(o),a=(0,l.useRef)(n);return a.current=n,(0,l.useEffect)(()=>{let n=r.current;if(!n)return;let o=new d.Terminal({cursorBlink:x(),fontFamily:`"IBM Plex Mono", ui-monospace, "SFMono-Regular", monospace`,fontSize:13,theme:m,convertEol:!1,scrollback:5e3}),s=new u.FitAddon;o.loadAddon(s),o.open(n);let c=e(t,{write:e=>o.write(e),onExit:()=>o.write(`\r +`,t)}paste(e){this._core.paste(e)}refresh(e,t){this._verifyIntegers(e,t),this._core.refresh(e,t)}reset(){this._core.reset()}clearTextureAtlas(){this._core.clearTextureAtlas()}loadAddon(e){this._addonManager.loadAddon(this,e)}static get strings(){return t}_verifyIntegers(...e){for(let t of e)if(t===1/0||isNaN(t)||t%1!=0)throw Error(`This API only accepts integers`)}_verifyPositiveIntegers(...e){for(let t of e)if(t&&(t===1/0||isNaN(t)||t%1!=0||t<0))throw Error(`This API only accepts positive integers`)}}e.Terminal=d})(),r})()))})),l=a(n(),1),u=s(),d=c(),f=i(),p=r({flex:`1`,minHeight:`0`,minWidth:`0`,padding:`0.35rem 0.5rem`,background:`#070b0f`,borderWidth:`1px`,borderStyle:`solid`,borderColor:`grid`,borderRadius:`3px`,overflow:`hidden`,"& .xterm":{height:`100%`},"& .xterm-viewport":{overflowY:`auto !important`}}),m={background:`#070b0f`,foreground:`#d6e7da`,cursor:`#7fe0b0`,cursorAccent:`#070b0f`,selectionBackground:`#1f3b30`,black:`#0b0f14`,green:`#7fe0b0`,yellow:`#e8c170`,cyan:`#6fb8d6`,red:`#e06c75`,brightBlack:`#3a4750`,white:`#d6e7da`},h=40,g=3,_=1,v=2,y=`\x1B[5~`,b=`\x1B[6~`;function x(){return typeof document>`u`||document.documentElement.dataset.effects!==`off`}function S(e,t,n){if(e.deltaY===0)return[0,n];let r=e.deltaY>0?1:-1,i=Math.abs(e.deltaY);if(e.deltaMode===v)return[r*Math.max(1,Math.ceil(i)*Math.max(1,t-1)),0];if(e.deltaMode===_)return[r*Math.max(1,Math.ceil(i)),0];let a=n+e.deltaY,o=Math.trunc(a/h);return[o,a-o*h]}function C(e,t){let n=t+e,r=Math.trunc(n/g);return r===0?[``,n]:[(r<0?y:b).repeat(Math.abs(r)),n-r*g]}function w(e){return e.buffer.active.type===`normal`&&e.buffer.active.baseY>0}function T({sessionId:t,onConnection:n,readOnly:r=!1}){let i=(0,l.useRef)(null),a=(0,l.useContext)(o),s=(0,l.useRef)(n);return s.current=n,(0,l.useEffect)(()=>{let n=i.current;if(!n)return;let o=new d.Terminal({cursorBlink:x(),fontFamily:`"IBM Plex Mono", ui-monospace, "SFMono-Regular", monospace`,fontSize:13,theme:m,convertEol:!1,scrollback:5e3}),c=new u.FitAddon;o.loadAddon(c),o.open(n);let l=e(t,{write:e=>o.write(e),onExit:()=>o.write(`\r \x1B[2m— session ended —\x1B[0m\r -`)},i?{socketFactory:i}:{});a.current?.(c);let l=0,f=0,p=e=>{if(e.deltaY===0||o.modes.mouseTrackingMode!==`none`)return;let[t,n]=S(e,o.rows,l);if(l=n,t!==0)if(w(o))f=0,o.scrollLines(t);else{let[e,n]=C(t,f);f=n,e&&c.sendInput(e)}e.cancelable&&e.preventDefault(),e.stopPropagation()};n.addEventListener(`wheel`,p,{passive:!1,capture:!0});let h=o.onData(e=>c.sendInput(e)),g=()=>{!n.clientWidth||!n.clientHeight||(s.fit(),c.sendResize(o.cols,o.rows))};g();let _=!0,v=requestAnimationFrame(g);document.fonts.ready.then(()=>{_&&g()});let y=new ResizeObserver(g);return y.observe(n),()=>{_=!1,cancelAnimationFrame(v),a.current?.(null),n.removeEventListener(`wheel`,p,{capture:!0}),y.disconnect(),h.dispose(),c.dispose(),o.dispose()}},[t,i]),(0,f.jsx)(`div`,{ref:r,className:p,"data-testid":`terminal-host`})}export{T as Terminal}; \ No newline at end of file +`)},a?{socketFactory:a}:{});s.current?.(l);let f=0,p=0,h=e=>{if(e.deltaY===0||o.modes.mouseTrackingMode!==`none`)return;let[t,n]=S(e,o.rows,f);if(f=n,t!==0)if(w(o))p=0,o.scrollLines(t);else{let[e,n]=C(t,p);p=n,e&&!r&&l.sendInput(e)}e.cancelable&&e.preventDefault(),e.stopPropagation()};n.addEventListener(`wheel`,h,{passive:!1,capture:!0});let g=r?null:o.onData(e=>l.sendInput(e)),_=()=>{!n.clientWidth||!n.clientHeight||(c.fit(),l.sendResize(o.cols,o.rows))};_();let v=!0,y=requestAnimationFrame(_);document.fonts.ready.then(()=>{v&&_()});let b=new ResizeObserver(_);return b.observe(n),()=>{v=!1,cancelAnimationFrame(y),s.current?.(null),n.removeEventListener(`wheel`,h,{capture:!0}),b.disconnect(),g?.dispose(),l.dispose(),o.dispose()}},[r,t,a]),(0,f.jsx)(`div`,{ref:i,className:p,"data-testid":`terminal-host`})}export{T as Terminal}; \ No newline at end of file diff --git a/mcp/src/agents_remember/package_data/dashboard/assets/dist-DlUN4-j9.js b/mcp/src/agents_remember/package_data/dashboard/assets/dist-BT1tv7N8.js similarity index 96% rename from mcp/src/agents_remember/package_data/dashboard/assets/dist-DlUN4-j9.js rename to mcp/src/agents_remember/package_data/dashboard/assets/dist-BT1tv7N8.js index c995020e..aee77644 100644 --- a/mcp/src/agents_remember/package_data/dashboard/assets/dist-DlUN4-j9.js +++ b/mcp/src/agents_remember/package_data/dashboard/assets/dist-BT1tv7N8.js @@ -1 +1 @@ -import{b as e,d as t,f as n,i as r,m as i,s as a,t as o,x as s}from"./index-CLJ0aycO.js";import{r as c}from"./dist-B2R-c1vX.js";var l=e({String:s.string,Number:s.number,"True False":s.bool,PropertyName:s.propertyName,Null:s.null,", :":s.separator,"[ ]":s.squareBracket,"{ }":s.brace}),u=c.deserialize({version:14,states:"$bOVQPOOOOQO'#Cb'#CbOnQPO'#CeOvQPO'#ClOOQO'#Cr'#CrQOQPOOOOQO'#Cg'#CgO}QPO'#CfO!SQPO'#CtOOQO,59P,59PO![QPO,59PO!aQPO'#CuOOQO,59W,59WO!iQPO,59WOVQPO,59QOqQPO'#CmO!nQPO,59`OOQO1G.k1G.kOVQPO'#CnO!vQPO,59aOOQO1G.r1G.rOOQO1G.l1G.lOOQO,59X,59XOOQO-E6k-E6kOOQO,59Y,59YOOQO-E6l-E6l",stateData:`#O~OeOS~OQSORSOSSOTSOWQO_ROgPO~OVXOgUO~O^[O~PVO[^O~O]_OVhX~OVaO~O]bO^iX~O^dO~O]_OVha~O]bO^ia~O`,goto:"!kjPPPPPPkPPkqwPPPPk{!RPPP!XP!e!hXSOR^bQWQRf_TVQ_Q`WRg`QcZRicQTOQZRQe^RhbRYQR]R",nodeNames:`⚠ JsonText True False Null Number String } { Object Property PropertyName : , ] [ Array`,maxTerm:25,nodeProps:[[`isolate`,-2,6,11,``],[`openedBy`,7,`{`,14,`[`],[`closedBy`,8,`}`,15,`]`]],propSources:[l],skippedNodes:[0],repeatNodeCount:2,tokenData:"(|~RaXY!WYZ!W]^!Wpq!Wrs!]|}$u}!O$z!Q!R%T!R![&c![!]&t!}#O&y#P#Q'O#Y#Z'T#b#c'r#h#i(Z#o#p(r#q#r(w~!]Oe~~!`Wpq!]qr!]rs!xs#O!]#O#P!}#P;'S!];'S;=`$o<%lO!]~!}Og~~#QXrs!]!P!Q!]#O#P!]#U#V!]#Y#Z!]#b#c!]#f#g!]#h#i!]#i#j#m~#pR!Q![#y!c!i#y#T#Z#y~#|R!Q![$V!c!i$V#T#Z$V~$YR!Q![$c!c!i$c#T#Z$c~$fR!Q![!]!c!i!]#T#Z!]~$rP;=`<%l!]~$zO]~~$}Q!Q!R%T!R![&c~%YRT~!O!P%c!g!h%w#X#Y%w~%fP!Q![%i~%nRT~!Q![%i!g!h%w#X#Y%w~%zR{|&T}!O&T!Q![&Z~&WP!Q![&Z~&`PT~!Q![&Z~&hST~!O!P%c!Q![&c!g!h%w#X#Y%w~&yO[~~'OO_~~'TO^~~'WP#T#U'Z~'^P#`#a'a~'dP#g#h'g~'jP#X#Y'm~'rOR~~'uP#i#j'x~'{P#`#a(O~(RP#`#a(U~(ZOS~~(^P#f#g(a~(dP#i#j(g~(jP#X#Y(m~(rOQ~~(wOW~~(|OV~",tokenizers:[0],topRules:{JsonText:[0,1]},tokenPrec:0}),d=o.define({name:`json`,parser:u.configure({props:[i.add({Object:a({except:/^\s*\}/}),Array:a({except:/^\s*\]/})}),n.add({"Object Array":t})]}),languageData:{closeBrackets:{brackets:[`[`,`{`,`"`]},indentOnInput:/^\s*[\}\]]$/}});function f(){return new r(d)}export{f as json}; \ No newline at end of file +import{b as e,d as t,f as n,i as r,m as i,s as a,t as o,x as s}from"./index-DPVcGf1z.js";import{r as c}from"./dist-T_roqrwM.js";var l=e({String:s.string,Number:s.number,"True False":s.bool,PropertyName:s.propertyName,Null:s.null,", :":s.separator,"[ ]":s.squareBracket,"{ }":s.brace}),u=c.deserialize({version:14,states:"$bOVQPOOOOQO'#Cb'#CbOnQPO'#CeOvQPO'#ClOOQO'#Cr'#CrQOQPOOOOQO'#Cg'#CgO}QPO'#CfO!SQPO'#CtOOQO,59P,59PO![QPO,59PO!aQPO'#CuOOQO,59W,59WO!iQPO,59WOVQPO,59QOqQPO'#CmO!nQPO,59`OOQO1G.k1G.kOVQPO'#CnO!vQPO,59aOOQO1G.r1G.rOOQO1G.l1G.lOOQO,59X,59XOOQO-E6k-E6kOOQO,59Y,59YOOQO-E6l-E6l",stateData:`#O~OeOS~OQSORSOSSOTSOWQO_ROgPO~OVXOgUO~O^[O~PVO[^O~O]_OVhX~OVaO~O]bO^iX~O^dO~O]_OVha~O]bO^ia~O`,goto:"!kjPPPPPPkPPkqwPPPPk{!RPPP!XP!e!hXSOR^bQWQRf_TVQ_Q`WRg`QcZRicQTOQZRQe^RhbRYQR]R",nodeNames:`⚠ JsonText True False Null Number String } { Object Property PropertyName : , ] [ Array`,maxTerm:25,nodeProps:[[`isolate`,-2,6,11,``],[`openedBy`,7,`{`,14,`[`],[`closedBy`,8,`}`,15,`]`]],propSources:[l],skippedNodes:[0],repeatNodeCount:2,tokenData:"(|~RaXY!WYZ!W]^!Wpq!Wrs!]|}$u}!O$z!Q!R%T!R![&c![!]&t!}#O&y#P#Q'O#Y#Z'T#b#c'r#h#i(Z#o#p(r#q#r(w~!]Oe~~!`Wpq!]qr!]rs!xs#O!]#O#P!}#P;'S!];'S;=`$o<%lO!]~!}Og~~#QXrs!]!P!Q!]#O#P!]#U#V!]#Y#Z!]#b#c!]#f#g!]#h#i!]#i#j#m~#pR!Q![#y!c!i#y#T#Z#y~#|R!Q![$V!c!i$V#T#Z$V~$YR!Q![$c!c!i$c#T#Z$c~$fR!Q![!]!c!i!]#T#Z!]~$rP;=`<%l!]~$zO]~~$}Q!Q!R%T!R![&c~%YRT~!O!P%c!g!h%w#X#Y%w~%fP!Q![%i~%nRT~!Q![%i!g!h%w#X#Y%w~%zR{|&T}!O&T!Q![&Z~&WP!Q![&Z~&`PT~!Q![&Z~&hST~!O!P%c!Q![&c!g!h%w#X#Y%w~&yO[~~'OO_~~'TO^~~'WP#T#U'Z~'^P#`#a'a~'dP#g#h'g~'jP#X#Y'm~'rOR~~'uP#i#j'x~'{P#`#a(O~(RP#`#a(U~(ZOS~~(^P#f#g(a~(dP#i#j(g~(jP#X#Y(m~(rOQ~~(wOW~~(|OV~",tokenizers:[0],topRules:{JsonText:[0,1]},tokenPrec:0}),d=o.define({name:`json`,parser:u.configure({props:[i.add({Object:a({except:/^\s*\}/}),Array:a({except:/^\s*\]/})}),n.add({"Object Array":t})]}),languageData:{closeBrackets:{brackets:[`[`,`{`,`"`]},indentOnInput:/^\s*[\}\]]$/}});function f(){return new r(d)}export{f as json}; \ No newline at end of file diff --git a/mcp/src/agents_remember/package_data/dashboard/assets/dist-jYLMKGSZ.js b/mcp/src/agents_remember/package_data/dashboard/assets/dist-BTdFvK3x.js similarity index 99% rename from mcp/src/agents_remember/package_data/dashboard/assets/dist-jYLMKGSZ.js rename to mcp/src/agents_remember/package_data/dashboard/assets/dist-BTdFvK3x.js index 6549dcda..2148b330 100644 --- a/mcp/src/agents_remember/package_data/dashboard/assets/dist-jYLMKGSZ.js +++ b/mcp/src/agents_remember/package_data/dashboard/assets/dist-BTdFvK3x.js @@ -1 +1 @@ -import{A as e,I as t,M as n,b as r,f as i,i as a,m as o,o as s,t as c,v as l,x as u}from"./index-CLJ0aycO.js";import{n as d,r as ee,t as te}from"./dist-B2R-c1vX.js";import{n as f,t as ne}from"./dist-BJusfjLN.js";import{a as re,i as ie,n as ae,o as oe,r as p}from"./dist-17r-i6rR.js";var se=55,ce=1,le=56,ue=2,de=57,fe=3,m=4,pe=5,h=6,g=7,me=8,he=9,ge=10,_e=11,ve=12,ye=13,_=58,be=14,xe=15,v=59,y=21,Se=23,b=24,Ce=25,x=27,S=28,we=29,Te=32,Ee=35,De=37,Oe=38,ke=0,Ae=1,je={area:!0,base:!0,br:!0,col:!0,command:!0,embed:!0,frame:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0,menuitem:!0},Me={dd:!0,li:!0,optgroup:!0,option:!0,p:!0,rp:!0,rt:!0,tbody:!0,td:!0,tfoot:!0,th:!0,tr:!0},C={dd:{dd:!0,dt:!0},dt:{dd:!0,dt:!0},li:{li:!0},option:{option:!0,optgroup:!0},optgroup:{optgroup:!0},p:{address:!0,article:!0,aside:!0,blockquote:!0,dir:!0,div:!0,dl:!0,fieldset:!0,footer:!0,form:!0,h1:!0,h2:!0,h3:!0,h4:!0,h5:!0,h6:!0,header:!0,hgroup:!0,hr:!0,menu:!0,nav:!0,ol:!0,p:!0,pre:!0,section:!0,table:!0,ul:!0},rp:{rp:!0,rt:!0},rt:{rp:!0,rt:!0},tbody:{tbody:!0,tfoot:!0},td:{td:!0,th:!0},tfoot:{tbody:!0},th:{td:!0,th:!0},thead:{tbody:!0,tfoot:!0},tr:{tr:!0}};function Ne(e){return e==45||e==46||e==58||e>=65&&e<=90||e==95||e>=97&&e<=122||e>=161}var w=null,T=null,E=0;function D(e,t){let n=e.pos+t;if(E==n&&T==e)return w;let r=e.peek(t),i=``;for(;Ne(r);)i+=String.fromCharCode(r),r=e.peek(++t);return T=e,E=n,w=i?i.toLowerCase():r==Pe||r==Fe?void 0:null}var O=60,k=62,A=47,Pe=63,Fe=33,Ie=45;function j(e,t){this.name=e,this.parent=t}var Le=[h,ge,g,me,he],Re=new te({start:null,shift(e,t,n,r){return Le.indexOf(t)>-1?new j(D(r,1)||``,e):e},reduce(e,t){return t==y&&e?e.parent:e},reuse(e,t,n,r){let i=t.type.id;return i==h||i==De?new j(D(r,1)||``,e):e},strict:!1}),ze=new d((e,t)=>{if(e.next!=O){e.next<0&&t.context&&e.acceptToken(_);return}e.advance();let n=e.next==A;n&&e.advance();let r=D(e,0);if(r===void 0)return;if(!r)return e.acceptToken(n?xe:be);let i=t.context?t.context.name:null;if(n){if(r==i)return e.acceptToken(_e);if(i&&Me[i])return e.acceptToken(_,-2);if(t.dialectEnabled(ke))return e.acceptToken(ve);for(let e=t.context;e;e=e.parent)if(e.name==r)return;e.acceptToken(ye)}else{if(r==`script`)return e.acceptToken(g);if(r==`style`)return e.acceptToken(me);if(r==`textarea`)return e.acceptToken(he);if(je.hasOwnProperty(r))return e.acceptToken(ge);i&&C[i]&&C[i][r]?e.acceptToken(_,-1):e.acceptToken(h)}},{contextual:!0}),Be=new d(e=>{for(let t=0,n=0;;n++){if(e.next<0){n&&e.acceptToken(v);break}if(e.next==Ie)t++;else if(e.next==k&&t>=2){n>=3&&e.acceptToken(v,-2);break}else t=0;e.advance()}});function Ve(e){for(;e;e=e.parent)if(e.name==`svg`||e.name==`math`)return!0;return!1}var He=new d((e,t)=>{if(e.next==A&&e.peek(1)==k){let n=t.dialectEnabled(Ae)||Ve(t.context);e.acceptToken(n?pe:m,2)}else e.next==k&&e.acceptToken(m,1)});function M(e,t,n){let r=2+e.length;return new d(i=>{for(let a=0,o=0,s=0;;s++){if(i.next<0){s&&i.acceptToken(t);break}if(a==0&&i.next==O||a==1&&i.next==A||a>=2&&ao?i.acceptToken(t,-o):i.acceptToken(n,-(o-2));break}else if((i.next==10||i.next==13)&&s){i.acceptToken(t,1);break}else a=o=0;i.advance()}})}var Ue=M(`script`,se,ce),We=M(`style`,le,ue),Ge=M(`textarea`,de,fe),Ke=r({"Text RawText IncompleteTag IncompleteCloseTag":u.content,"StartTag StartCloseTag SelfClosingEndTag EndTag":u.angleBracket,TagName:u.tagName,"MismatchedCloseTag/TagName":[u.tagName,u.invalid],AttributeName:u.attributeName,"AttributeValue UnquotedAttributeValue":u.attributeValue,Is:u.definitionOperator,"EntityReference CharacterReference":u.character,Comment:u.blockComment,ProcessingInst:u.processingInstruction,DoctypeDecl:u.documentMeta}),qe=ee.deserialize({version:14,states:",xOVO!rOOO!ZQ#tO'#CrO!`Q#tO'#C{O!eQ#tO'#DOO!jQ#tO'#DRO!oQ#tO'#DTO!tOaO'#CqO#PObO'#CqO#[OdO'#CqO$kO!rO'#CqOOO`'#Cq'#CqO$rO$fO'#DUO$zQ#tO'#DWO%PQ#tO'#DXOOO`'#Dl'#DlOOO`'#DZ'#DZQVO!rOOO%UQ&rO,59^O%aQ&rO,59gO%lQ&rO,59jO%wQ&rO,59mO&SQ&rO,59oOOOa'#D_'#D_O&_OaO'#CyO&jOaO,59]OOOb'#D`'#D`O&rObO'#C|O&}ObO,59]OOOd'#Da'#DaO'VOdO'#DPO'bOdO,59]OOO`'#Db'#DbO'jO!rO,59]O'qQ#tO'#DSOOO`,59],59]OOOp'#Dc'#DcO'vO$fO,59pOOO`,59p,59pO(OQ#|O,59rO(TQ#|O,59sOOO`-E7X-E7XO(YQ&rO'#CtOOQW'#D['#D[O(hQ&rO1G.xOOOa1G.x1G.xOOO`1G/Z1G/ZO(sQ&rO1G/ROOOb1G/R1G/RO)OQ&rO1G/UOOOd1G/U1G/UO)ZQ&rO1G/XOOO`1G/X1G/XO)fQ&rO1G/ZOOOa-E7]-E7]O)qQ#tO'#CzOOO`1G.w1G.wOOOb-E7^-E7^O)vQ#tO'#C}OOOd-E7_-E7_O){Q#tO'#DQOOO`-E7`-E7`O*QQ#|O,59nOOOp-E7a-E7aOOO`1G/[1G/[OOO`1G/^1G/^OOO`1G/_1G/_O*VQ,UO,59`OOQW-E7Y-E7YOOOa7+$d7+$dOOO`7+$u7+$uOOOb7+$m7+$mOOOd7+$p7+$pOOO`7+$s7+$sO*bQ#|O,59fO*gQ#|O,59iO*lQ#|O,59lOOO`1G/Y1G/YO*qO7[O'#CwO+SOMhO'#CwOOQW1G.z1G.zOOO`1G/Q1G/QOOO`1G/T1G/TOOO`1G/W1G/WOOOO'#D]'#D]O+eO7[O,59cOOQW,59c,59cOOOO'#D^'#D^O+vOMhO,59cOOOO-E7Z-E7ZOOQW1G.}1G.}OOOO-E7[-E7[",stateData:`,c~O!_OS~OUSOVPOWQOXROYTO[]O][O^^O_^Oa^Ob^Oc^Od^Oy^O|_O!eZO~OgaO~OgbO~OgcO~OgdO~OgeO~O!XfOPmP![mP~O!YiOQpP![pP~O!ZlORsP![sP~OUSOVPOWQOXROYTOZqO[]O][O^^O_^Oa^Ob^Oc^Od^Oy^O!eZO~O![rO~P#gO!]sO!fuO~OgvO~OgwO~OS|OT}OiyO~OS!POT}OiyO~OS!ROT}OiyO~OS!TOT}OiyO~OS}OT}OiyO~O!XfOPmX![mX~OP!WO![!XO~O!YiOQpX![pX~OQ!ZO![!XO~O!ZlORsX![sX~OR!]O![!XO~O![!XO~P#gOg!_O~O!]sO!f!aO~OS!bO~OS!cO~Oj!dOShXThXihX~OS!fOT!gOiyO~OS!hOT!gOiyO~OS!iOT!gOiyO~OS!jOT!gOiyO~OS!gOT!gOiyO~Og!kO~Og!lO~Og!mO~OS!nO~Ol!qO!a!oO!c!pO~OS!rO~OS!sO~OS!tO~Ob!uOc!uOd!uO!a!wO!b!uO~Ob!xOc!xOd!xO!c!wO!d!xO~Ob!uOc!uOd!uO!a!{O!b!uO~Ob!xOc!xOd!xO!c!{O!d!xO~OT~cbd!ey|!e~`,goto:"%q!aPPPPPPPPPPPPPPPPPPPPP!b!hP!nPP!zP!}#Q#T#Z#^#a#g#j#m#s#y!bP!b!bP$P$V$m$s$y%P%V%]%cPPPPPPPP%iX^OX`pXUOX`pezabcde{!O!Q!S!UR!q!dRhUR!XhXVOX`pRkVR!XkXWOX`pRnWR!XnXXOX`pQrXR!XpXYOX`pQ`ORx`Q{aQ!ObQ!QcQ!SdQ!UeZ!e{!O!Q!S!UQ!v!oR!z!vQ!y!pR!|!yQgUR!VgQjVR!YjQmWR![mQpXR!^pQtZR!`tS_O`ToXp",nodeNames:`⚠ StartCloseTag StartCloseTag StartCloseTag EndTag SelfClosingEndTag StartTag StartTag StartTag StartTag StartTag StartCloseTag StartCloseTag StartCloseTag IncompleteTag IncompleteCloseTag Document Text EntityReference CharacterReference InvalidEntity Element OpenTag TagName Attribute AttributeName Is AttributeValue UnquotedAttributeValue ScriptText CloseTag OpenTag StyleText CloseTag OpenTag TextareaText CloseTag OpenTag CloseTag SelfClosingTag Comment ProcessingInst MismatchedCloseTag CloseTag DoctypeDecl`,maxTerm:68,context:Re,nodeProps:[[`closedBy`,-10,1,2,3,7,8,9,10,11,12,13,`EndTag`,6,`EndTag SelfClosingEndTag`,-4,22,31,34,37,`CloseTag`],[`openedBy`,4,`StartTag StartCloseTag`,5,`StartTag`,-4,30,33,36,38,`OpenTag`],[`group`,-10,14,15,18,19,20,21,40,41,42,43,`Entity`,17,`Entity TextContent`,-3,29,32,35,`TextContent Entity`],[`isolate`,-11,22,30,31,33,34,36,37,38,39,42,43,`ltr`,-3,27,28,40,``]],propSources:[Ke],skippedNodes:[0],repeatNodeCount:9,tokenData:"!]tw8twx7Sx!P8t!P!Q5u!Q!]8t!]!^/^!^!a7S!a#S8t#S#T;{#T#s8t#s$f5u$f;'S8t;'S;=`>V<%l?Ah8t?Ah?BY5u?BY?Mn8t?MnO5u!Z5zblWOX5uXZ7SZ[5u[^7S^p5uqr5urs7Sst+Ptw5uwx7Sx!]5u!]!^7w!^!a7S!a#S5u#S#T7S#T;'S5u;'S;=`8n<%lO5u!R7VVOp7Sqs7St!]7S!]!^7l!^;'S7S;'S;=`7q<%lO7S!R7qOb!R!R7tP;=`<%l7S!Z8OYlWb!ROX+PZ[+P^p+Pqr+Psw+Px!^+P!a#S+P#T;'S+P;'S;=`+t<%lO+P!Z8qP;=`<%l5u!_8{iiSlWOX5uXZ7SZ[5u[^7S^p5uqr8trs7Sst/^tw8twx7Sx!P8t!P!Q5u!Q!]8t!]!^:j!^!a7S!a#S8t#S#T;{#T#s8t#s$f5u$f;'S8t;'S;=`>V<%l?Ah8t?Ah?BY5u?BY?Mn8t?MnO5u!_:sbiSlWb!ROX+PZ[+P^p+Pqr/^sw/^x!P/^!P!Q+P!Q!^/^!a#S/^#S#T0m#T#s/^#s$f+P$f;'S/^;'S;=`1e<%l?Ah/^?Ah?BY+P?BY?Mn/^?MnO+P!VP<%l?Ah;{?Ah?BY7S?BY?Mn;{?MnO7S!V=dXiSb!Rqr0msw0mx!P0m!Q!^0m!a#s0m$f;'S0m;'S;=`1_<%l?Ah0m?BY?Mn0m!V>SP;=`<%l;{!_>YP;=`<%l8t!_>dhiSlWOX@OXZAYZ[@O[^AY^p@OqrBwrsAYswBwwxAYx!PBw!P!Q@O!Q!]Bw!]!^/^!^!aAY!a#SBw#S#TE{#T#sBw#s$f@O$f;'SBw;'S;=`HS<%l?AhBw?Ah?BY@O?BY?MnBw?MnO@O!Z@TalWOX@OXZAYZ[@O[^AY^p@Oqr@OrsAYsw@OwxAYx!]@O!]!^Az!^!aAY!a#S@O#S#TAY#T;'S@O;'S;=`Bq<%lO@O!RA]UOpAYq!]AY!]!^Ao!^;'SAY;'S;=`At<%lOAY!RAtOc!R!RAwP;=`<%lAY!ZBRYlWc!ROX+PZ[+P^p+Pqr+Psw+Px!^+P!a#S+P#T;'S+P;'S;=`+t<%lO+P!ZBtP;=`<%l@O!_COhiSlWOX@OXZAYZ[@O[^AY^p@OqrBwrsAYswBwwxAYx!PBw!P!Q@O!Q!]Bw!]!^Dj!^!aAY!a#SBw#S#TE{#T#sBw#s$f@O$f;'SBw;'S;=`HS<%l?AhBw?Ah?BY@O?BY?MnBw?MnO@O!_DsbiSlWc!ROX+PZ[+P^p+Pqr/^sw/^x!P/^!P!Q+P!Q!^/^!a#S/^#S#T0m#T#s/^#s$f+P$f;'S/^;'S;=`1e<%l?Ah/^?Ah?BY+P?BY?Mn/^?MnO+P!VFQbiSOpAYqrE{rsAYswE{wxAYx!PE{!P!QAY!Q!]E{!]!^GY!^!aAY!a#sE{#s$fAY$f;'SE{;'S;=`G|<%l?AhE{?Ah?BYAY?BY?MnE{?MnOAY!VGaXiSc!Rqr0msw0mx!P0m!Q!^0m!a#s0m$f;'S0m;'S;=`1_<%l?Ah0m?BY?Mn0m!VHPP;=`<%lE{!_HVP;=`<%lBw!ZHcW!cxaP!b`Or(trs'ksv(tw!^(t!^!_)e!_;'S(t;'S;=`*P<%lO(t!aIYliSaPlW!b`!dpOX$qXZ&XZ[$q[^&X^p$qpq&Xqr-_rs&}sv-_vw/^wx(tx}-_}!OKQ!O!P-_!P!Q$q!Q!^-_!^!_*V!_!a&X!a#S-_#S#T1k#T#s-_#s$f$q$f;'S-_;'S;=`3X<%l?Ah-_?Ah?BY$q?BY?Mn-_?MnO$q!aK_kiSaPlW!b`!dpOX$qXZ&XZ[$q[^&X^p$qpq&Xqr-_rs&}sv-_vw/^wx(tx!P-_!P!Q$q!Q!^-_!^!_*V!_!`&X!`!aMS!a#S-_#S#T1k#T#s-_#s$f$q$f;'S-_;'S;=`3X<%l?Ah-_?Ah?BY$q?BY?Mn-_?MnO$q!TM_XaP!b`!dp!fQOr&Xrs&}sv&Xwx(tx!^&X!^!_*V!_;'S&X;'S;=`*y<%lO&X!aNZ!ZiSgQaPlW!b`!dpOX$qXZ&XZ[$q[^&X^p$qpq&Xqr-_rs&}sv-_vw/^wx(tx}-_}!OMz!O!PMz!P!Q$q!Q![Mz![!]Mz!]!^-_!^!_*V!_!a&X!a!c-_!c!}Mz!}#R-_#R#SMz#S#T1k#T#oMz#o#s-_#s$f$q$f$}-_$}%OMz%O%W-_%W%oMz%o%p-_%p&aMz&a&b-_&b1pMz1p4UMz4U4dMz4d4e-_4e$ISMz$IS$I`-_$I`$IbMz$Ib$Je-_$Je$JgMz$Jg$Kh-_$Kh%#tMz%#t&/x-_&/x&EtMz&Et&FV-_&FV;'SMz;'S;:j!#|;:j;=`3X<%l?&r-_?&r?AhMz?Ah?BY$q?BY?MnMz?MnO$q!a!$PP;=`<%lMz!R!$ZY!b`!dpOq*Vqr!$yrs(Vsv*Vwx)ex!a*V!a!b!4t!b;'S*V;'S;=`*s<%lO*V!R!%Q]!b`!dpOr*Vrs(Vsv*Vwx)ex}*V}!O!%y!O!f*V!f!g!']!g#W*V#W#X!0`#X;'S*V;'S;=`*s<%lO*V!R!&QX!b`!dpOr*Vrs(Vsv*Vwx)ex}*V}!O!&m!O;'S*V;'S;=`*s<%lO*V!R!&vV!b`!dp!ePOr*Vrs(Vsv*Vwx)ex;'S*V;'S;=`*s<%lO*V!R!'dX!b`!dpOr*Vrs(Vsv*Vwx)ex!q*V!q!r!(P!r;'S*V;'S;=`*s<%lO*V!R!(WX!b`!dpOr*Vrs(Vsv*Vwx)ex!e*V!e!f!(s!f;'S*V;'S;=`*s<%lO*V!R!(zX!b`!dpOr*Vrs(Vsv*Vwx)ex!v*V!v!w!)g!w;'S*V;'S;=`*s<%lO*V!R!)nX!b`!dpOr*Vrs(Vsv*Vwx)ex!{*V!{!|!*Z!|;'S*V;'S;=`*s<%lO*V!R!*bX!b`!dpOr*Vrs(Vsv*Vwx)ex!r*V!r!s!*}!s;'S*V;'S;=`*s<%lO*V!R!+UX!b`!dpOr*Vrs(Vsv*Vwx)ex!g*V!g!h!+q!h;'S*V;'S;=`*s<%lO*V!R!+xY!b`!dpOr!+qrs!,hsv!+qvw!-Swx!.[x!`!+q!`!a!/j!a;'S!+q;'S;=`!0Y<%lO!+qq!,mV!dpOv!,hvx!-Sx!`!,h!`!a!-q!a;'S!,h;'S;=`!.U<%lO!,hP!-VTO!`!-S!`!a!-f!a;'S!-S;'S;=`!-k<%lO!-SP!-kO|PP!-nP;=`<%l!-Sq!-xS!dp|POv(Vx;'S(V;'S;=`(h<%lO(Vq!.XP;=`<%l!,ha!.aX!b`Or!.[rs!-Ssv!.[vw!-Sw!`!.[!`!a!.|!a;'S!.[;'S;=`!/d<%lO!.[a!/TT!b`|POr)esv)ew;'S)e;'S;=`)y<%lO)ea!/gP;=`<%l!.[!R!/sV!b`!dp|POr*Vrs(Vsv*Vwx)ex;'S*V;'S;=`*s<%lO*V!R!0]P;=`<%l!+q!R!0gX!b`!dpOr*Vrs(Vsv*Vwx)ex#c*V#c#d!1S#d;'S*V;'S;=`*s<%lO*V!R!1ZX!b`!dpOr*Vrs(Vsv*Vwx)ex#V*V#V#W!1v#W;'S*V;'S;=`*s<%lO*V!R!1}X!b`!dpOr*Vrs(Vsv*Vwx)ex#h*V#h#i!2j#i;'S*V;'S;=`*s<%lO*V!R!2qX!b`!dpOr*Vrs(Vsv*Vwx)ex#m*V#m#n!3^#n;'S*V;'S;=`*s<%lO*V!R!3eX!b`!dpOr*Vrs(Vsv*Vwx)ex#d*V#d#e!4Q#e;'S*V;'S;=`*s<%lO*V!R!4XX!b`!dpOr*Vrs(Vsv*Vwx)ex#X*V#X#Y!+q#Y;'S*V;'S;=`*s<%lO*V!R!4{Y!b`!dpOr!4trs!5ksv!4tvw!6Vwx!8]x!a!4t!a!b!:]!b;'S!4t;'S;=`!;r<%lO!4tq!5pV!dpOv!5kvx!6Vx!a!5k!a!b!7W!b;'S!5k;'S;=`!8V<%lO!5kP!6YTO!a!6V!a!b!6i!b;'S!6V;'S;=`!7Q<%lO!6VP!6lTO!`!6V!`!a!6{!a;'S!6V;'S;=`!7Q<%lO!6VP!7QOyPP!7TP;=`<%l!6Vq!7]V!dpOv!5kvx!6Vx!`!5k!`!a!7r!a;'S!5k;'S;=`!8V<%lO!5kq!7yS!dpyPOv(Vx;'S(V;'S;=`(h<%lO(Vq!8YP;=`<%l!5ka!8bX!b`Or!8]rs!6Vsv!8]vw!6Vw!a!8]!a!b!8}!b;'S!8];'S;=`!:V<%lO!8]a!9SX!b`Or!8]rs!6Vsv!8]vw!6Vw!`!8]!`!a!9o!a;'S!8];'S;=`!:V<%lO!8]a!9vT!b`yPOr)esv)ew;'S)e;'S;=`)y<%lO)ea!:YP;=`<%l!8]!R!:dY!b`!dpOr!4trs!5ksv!4tvw!6Vwx!8]x!`!4t!`!a!;S!a;'S!4t;'S;=`!;r<%lO!4t!R!;]V!b`!dpyPOr*Vrs(Vsv*Vwx)ex;'S*V;'S;=`*s<%lO*V!R!;uP;=`<%l!4t!V!{let n=e.type.id;if(n==we)return F(e,t,r);if(n==Te)return F(e,t,i);if(n==Ee)return F(e,t,a);if(n==y&&o.length){let n=e.node,r=n.firstChild,i=r&&P(r,t),a;if(i){for(let e of o)if(e.tag==i&&(!e.attrs||e.attrs(a||=N(r,t)))){let t=n.lastChild,i=t.type.id==Oe?t.from:n.to;if(i>r.to)return{parser:e.parser,overlay:[{from:r.to,to:i}]}}}}if(s&&n==b){let n=e.node,r;if(r=n.firstChild){let e=s[t.read(r.from,r.to)];if(e)for(let r of e){if(r.tagName&&r.tagName!=P(n.parent,t))continue;let e=n.lastChild;if(e.type.id==x){let t=e.from+1,n=e.lastChild,i=e.to-(n&&n.isError?0:1);if(i>t)return{parser:r.parser,overlay:[{from:t,to:i}],bracketed:!0}}else if(e.type.id==S)return{parser:r.parser,overlay:[{from:e.from,to:e.to}]}}}}return null})}var L=[`_blank`,`_self`,`_top`,`_parent`],R=[`ascii`,`utf-8`,`utf-16`,`latin1`,`latin1`],z=[`get`,`post`,`put`,`delete`],B=[`application/x-www-form-urlencoded`,`multipart/form-data`,`text/plain`],V=[`true`,`false`],H={},Je={a:{attrs:{href:null,ping:null,type:null,media:null,target:L,hreflang:null}},abbr:H,address:H,area:{attrs:{alt:null,coords:null,href:null,target:null,ping:null,media:null,hreflang:null,type:null,shape:[`default`,`rect`,`circle`,`poly`]}},article:H,aside:H,audio:{attrs:{src:null,mediagroup:null,crossorigin:[`anonymous`,`use-credentials`],preload:[`none`,`metadata`,`auto`],autoplay:[`autoplay`],loop:[`loop`],controls:[`controls`]}},b:H,base:{attrs:{href:null,target:L}},bdi:H,bdo:H,blockquote:{attrs:{cite:null}},body:H,br:H,button:{attrs:{form:null,formaction:null,name:null,value:null,autofocus:[`autofocus`],disabled:[`autofocus`],formenctype:B,formmethod:z,formnovalidate:[`novalidate`],formtarget:L,type:[`submit`,`reset`,`button`]}},canvas:{attrs:{width:null,height:null}},caption:H,center:H,cite:H,code:H,col:{attrs:{span:null}},colgroup:{attrs:{span:null}},command:{attrs:{type:[`command`,`checkbox`,`radio`],label:null,icon:null,radiogroup:null,command:null,title:null,disabled:[`disabled`],checked:[`checked`]}},data:{attrs:{value:null}},datagrid:{attrs:{disabled:[`disabled`],multiple:[`multiple`]}},datalist:{attrs:{data:null}},dd:H,del:{attrs:{cite:null,datetime:null}},details:{attrs:{open:[`open`]}},dfn:H,div:H,dl:H,dt:H,em:H,embed:{attrs:{src:null,type:null,width:null,height:null}},eventsource:{attrs:{src:null}},fieldset:{attrs:{disabled:[`disabled`],form:null,name:null}},figcaption:H,figure:H,footer:H,form:{attrs:{action:null,name:null,"accept-charset":R,autocomplete:[`on`,`off`],enctype:B,method:z,novalidate:[`novalidate`],target:L}},h1:H,h2:H,h3:H,h4:H,h5:H,h6:H,head:{children:[`title`,`base`,`link`,`style`,`meta`,`script`,`noscript`,`command`]},header:H,hgroup:H,hr:H,html:{attrs:{manifest:null}},i:H,iframe:{attrs:{src:null,srcdoc:null,name:null,width:null,height:null,sandbox:[`allow-top-navigation`,`allow-same-origin`,`allow-forms`,`allow-scripts`],seamless:[`seamless`]}},img:{attrs:{alt:null,src:null,ismap:null,usemap:null,width:null,height:null,crossorigin:[`anonymous`,`use-credentials`]}},input:{attrs:{alt:null,dirname:null,form:null,formaction:null,height:null,list:null,max:null,maxlength:null,min:null,name:null,pattern:null,placeholder:null,size:null,src:null,step:null,value:null,width:null,accept:[`audio/*`,`video/*`,`image/*`],autocomplete:[`on`,`off`],autofocus:[`autofocus`],checked:[`checked`],disabled:[`disabled`],formenctype:B,formmethod:z,formnovalidate:[`novalidate`],formtarget:L,multiple:[`multiple`],readonly:[`readonly`],required:[`required`],type:[`hidden`,`text`,`search`,`tel`,`url`,`email`,`password`,`datetime`,`date`,`month`,`week`,`time`,`datetime-local`,`number`,`range`,`color`,`checkbox`,`radio`,`file`,`submit`,`image`,`reset`,`button`]}},ins:{attrs:{cite:null,datetime:null}},kbd:H,keygen:{attrs:{challenge:null,form:null,name:null,autofocus:[`autofocus`],disabled:[`disabled`],keytype:[`RSA`]}},label:{attrs:{for:null,form:null}},legend:H,li:{attrs:{value:null}},link:{attrs:{href:null,type:null,hreflang:null,media:null,sizes:[`all`,`16x16`,`16x16 32x32`,`16x16 32x32 64x64`]}},map:{attrs:{name:null}},mark:H,menu:{attrs:{label:null,type:[`list`,`context`,`toolbar`]}},meta:{attrs:{content:null,charset:R,name:[`viewport`,`application-name`,`author`,`description`,`generator`,`keywords`],"http-equiv":[`content-language`,`content-type`,`default-style`,`refresh`]}},meter:{attrs:{value:null,min:null,low:null,high:null,max:null,optimum:null}},nav:H,noscript:H,object:{attrs:{data:null,type:null,name:null,usemap:null,form:null,width:null,height:null,typemustmatch:[`typemustmatch`]}},ol:{attrs:{reversed:[`reversed`],start:null,type:[`1`,`a`,`A`,`i`,`I`]},children:[`li`,`script`,`template`,`ul`,`ol`]},optgroup:{attrs:{disabled:[`disabled`],label:null}},option:{attrs:{disabled:[`disabled`],label:null,selected:[`selected`],value:null}},output:{attrs:{for:null,form:null,name:null}},p:H,param:{attrs:{name:null,value:null}},pre:H,progress:{attrs:{value:null,max:null}},q:{attrs:{cite:null}},rp:H,rt:H,ruby:H,samp:H,script:{attrs:{type:[`text/javascript`],src:null,async:[`async`],defer:[`defer`],charset:R}},section:H,select:{attrs:{form:null,name:null,size:null,autofocus:[`autofocus`],disabled:[`disabled`],multiple:[`multiple`]}},slot:{attrs:{name:null}},small:H,source:{attrs:{src:null,type:null,media:null}},span:H,strong:H,style:{attrs:{type:[`text/css`],media:null,scoped:null}},sub:H,summary:H,sup:H,table:H,tbody:H,td:{attrs:{colspan:null,rowspan:null,headers:null}},template:H,textarea:{attrs:{dirname:null,form:null,maxlength:null,name:null,placeholder:null,rows:null,cols:null,autofocus:[`autofocus`],disabled:[`disabled`],readonly:[`readonly`],required:[`required`],wrap:[`soft`,`hard`]}},tfoot:H,th:{attrs:{colspan:null,rowspan:null,headers:null,scope:[`row`,`col`,`rowgroup`,`colgroup`]}},thead:H,time:{attrs:{datetime:null}},title:H,tr:H,track:{attrs:{src:null,label:null,default:null,kind:[`subtitles`,`captions`,`descriptions`,`chapters`,`metadata`],srclang:null}},ul:{children:[`li`,`script`,`template`,`ul`,`ol`]},var:H,video:{attrs:{src:null,poster:null,width:null,height:null,crossorigin:[`anonymous`,`use-credentials`],preload:[`auto`,`metadata`,`none`],autoplay:[`autoplay`],mediagroup:[`movie`],muted:[`muted`],controls:[`controls`]}},wbr:H},U={accesskey:null,class:null,contenteditable:V,contextmenu:null,dir:[`ltr`,`rtl`,`auto`],draggable:[`true`,`false`,`auto`],dropzone:[`copy`,`move`,`link`,`string:`,`file:`],hidden:[`hidden`],id:null,inert:[`inert`],itemid:null,itemprop:null,itemref:null,itemscope:[`itemscope`],itemtype:null,lang:[`ar`,`bn`,`de`,`en-GB`,`en-US`,`es`,`fr`,`hi`,`id`,`ja`,`pa`,`pt`,`ru`,`tr`,`zh`],spellcheck:V,autocorrect:V,autocapitalize:V,style:null,tabindex:null,title:null,translate:[`yes`,`no`],rel:[`stylesheet`,`alternate`,`author`,`bookmark`,`help`,`license`,`next`,`nofollow`,`noreferrer`,`prefetch`,`prev`,`search`,`tag`],role:`alert application article banner button cell checkbox complementary contentinfo dialog document feed figure form grid gridcell heading img list listbox listitem main navigation region row rowgroup search switch tab table tabpanel textbox timer`.split(` `),"aria-activedescendant":null,"aria-atomic":V,"aria-autocomplete":[`inline`,`list`,`both`,`none`],"aria-busy":V,"aria-checked":[`true`,`false`,`mixed`,`undefined`],"aria-controls":null,"aria-describedby":null,"aria-disabled":V,"aria-dropeffect":null,"aria-expanded":[`true`,`false`,`undefined`],"aria-flowto":null,"aria-grabbed":[`true`,`false`,`undefined`],"aria-haspopup":V,"aria-hidden":V,"aria-invalid":[`true`,`false`,`grammar`,`spelling`],"aria-label":null,"aria-labelledby":null,"aria-level":null,"aria-live":[`off`,`polite`,`assertive`],"aria-multiline":V,"aria-multiselectable":V,"aria-owns":null,"aria-posinset":null,"aria-pressed":[`true`,`false`,`mixed`,`undefined`],"aria-readonly":V,"aria-relevant":null,"aria-required":V,"aria-selected":[`true`,`false`,`undefined`],"aria-setsize":null,"aria-sort":[`ascending`,`descending`,`none`,`other`],"aria-valuemax":null,"aria-valuemin":null,"aria-valuenow":null,"aria-valuetext":null},W=`beforeunload copy cut dragstart dragover dragleave dragenter dragend drag paste focus blur change click load mousedown mouseenter mouseleave mouseup keydown keyup resize scroll unload`.split(` `).map(e=>`on`+e);for(let e of W)U[e]=null;var G=class{constructor(e,t){this.tags={...Je,...e},this.globalAttrs={...U,...t},this.allTags=Object.keys(this.tags),this.globalAttrNames=Object.keys(this.globalAttrs)}};G.default=new G;function K(e,t,n=e.length){if(!t)return``;let r=t.firstChild,i=r&&r.getChild(`TagName`);return i?e.sliceString(i.from,Math.min(i.to,n)):``}function q(e,t=!1){for(;e;e=e.parent)if(e.name==`Element`)if(t)t=!1;else return e;return null}function J(e,t,n){return n.tags[K(e,q(t))]?.children||n.allTags}function Y(e,t){let n=[];for(let r=q(t);r&&!r.type.isTop;r=q(r.parent)){let i=K(e,r);if(i&&r.lastChild.name==`CloseTag`)break;i&&n.indexOf(i)<0&&(t.name==`EndTag`||t.from>=r.firstChild.to)&&n.push(i)}return n}var X=/^[:\-\.\w\u00b7-\uffff]*$/;function Ye(e,t,n,r,i){let a=/\s*>/.test(e.sliceDoc(i,i+5))?``:`>`,o=q(n,n.name==`StartTag`||n.name==`TagName`);return{from:r,to:i,options:J(e.doc,o,t).map(e=>({label:e,type:`type`})).concat(Y(e.doc,n).map((e,t)=>({label:`/`+e,apply:`/`+e+a,type:`type`,boost:99-t}))),validFor:/^\/?[:\-\.\w\u00b7-\uffff]*$/}}function Xe(e,t,n,r){let i=/\s*>/.test(e.sliceDoc(r,r+5))?``:`>`;return{from:n,to:r,options:Y(e.doc,t).map((e,t)=>({label:e,apply:e+i,type:`type`,boost:99-t})),validFor:X}}function Ze(e,t,n,r){let i=[],a=0;for(let r of J(e.doc,n,t))i.push({label:`<`+r,type:`type`});for(let t of Y(e.doc,n))i.push({label:``,type:`type`,boost:99-a++});return{from:r,to:r,options:i,validFor:/^<\/?[:\-\.\w\u00b7-\uffff]*$/}}function Qe(e,t,n,r,i){let a=q(n),o=a?t.tags[K(e.doc,a)]:null,s=o&&o.attrs?Object.keys(o.attrs):[];return{from:r,to:i,options:(o&&o.globalAttrs===!1?s:s.length?s.concat(t.globalAttrNames):t.globalAttrNames).map(e=>({label:e,type:`property`})),validFor:X}}function $e(e,t,n,r,i){let a=n.parent?.getChild(`AttributeName`),o=[],s;if(a){let c=e.sliceDoc(a.from,a.to),l=t.globalAttrs[c];if(!l){let r=q(n),i=r?t.tags[K(e.doc,r)]:null;l=i?.attrs&&i.attrs[c]}if(l){let t=e.sliceDoc(r,i).toLowerCase(),n=`"`,a=`"`;/^['"]/.test(t)?(s=t[0]==`"`?/^[^"]*$/:/^[^']*$/,n=``,a=e.sliceDoc(i,i+1)==t[0]?``:t[0],t=t.slice(1),r++):s=/^[^\s<>='"]*$/;for(let e of l)o.push({label:e,apply:n+e+a,type:`constant`})}}return{from:r,to:i,options:o,validFor:s}}function Z(e,t){let{state:n,pos:r}=t,i=l(n).resolveInner(r,-1),a=i.resolve(r);for(let e=r,t;a==i&&(t=i.childBefore(e));){let n=t.lastChild;if(!n||!n.type.isError||n.fromZ(r,e)}var nt=p.parser.configure({top:`SingleExpression`}),rt=[{tag:`script`,attrs:e=>e.type==`text/typescript`||e.lang==`ts`,parser:oe.parser},{tag:`script`,attrs:e=>e.type==`text/babel`||e.type==`text/jsx`,parser:ie.parser},{tag:`script`,attrs:e=>e.type==`text/typescript-jsx`,parser:re.parser},{tag:`script`,attrs(e){return/^(importmap|speculationrules|application\/(.+\+)?json)$/i.test(e.type)},parser:nt},{tag:`script`,attrs(e){return!e.type||/^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i.test(e.type)},parser:p.parser},{tag:`style`,attrs(e){return(!e.lang||e.lang==`css`)&&(!e.type||/^(text\/)?(x-)?(stylesheet|css)$/i.test(e.type))},parser:f.parser}],it=[{name:`style`,parser:f.parser.configure({top:`Styles`})}].concat(W.map(e=>({name:e,parser:p.parser}))),at=c.define({name:`html`,parser:qe.configure({props:[o.add({Element(e){let t=/^(\s*)(<\/)?/.exec(e.textAfter);return e.node.to<=e.pos+t[0].length?e.continue():e.lineIndent(e.node.from)+(t[2]?0:e.unit)},"OpenTag CloseTag SelfClosingTag"(e){return e.column(e.node.from)+e.unit},Document(e){if(e.pos+/\s*/.exec(e.textAfter)[0].lengthe.getChild(`TagName`)})]}),languageData:{commentTokens:{block:{open:``}},indentOnInput:/^\s*<\/\w+\W$/,wordChars:`-_`}}),Q=at.configure({wrap:I(rt,it)});function ot(e={}){let t=``,n;return e.matchClosingTags===!1&&(t=`noMatch`),e.selfClosingTags===!0&&(t=(t?t+` `:``)+`selfClosing`),(e.nestedLanguages&&e.nestedLanguages.length||e.nestedAttributes&&e.nestedAttributes.length)&&(n=I((e.nestedLanguages||[]).concat(rt),(e.nestedAttributes||[]).concat(it))),new a(n?at.configure({wrap:n,dialect:t}):t?Q.configure({dialect:t}):Q,[Q.data.of({autocomplete:tt(e)}),e.autoCloseTags===!1?[]:st,ae().support,ne().support])}var $=new Set(`area base br col command embed frame hr img input keygen link meta param source track wbr menuitem`.split(` `)),st=n.inputHandler.of((e,n,r,i,a)=>{if(e.composing||e.state.readOnly||n!=r||i!=`>`&&i!=`/`||!Q.isActiveAt(e.state,n,-1))return!1;let o=a(),{state:s}=o,c=s.changeByRange(e=>{let n=s.doc.sliceString(e.from-1,e.to)==i,{head:r}=e,a=l(s).resolveInner(r,-1),o;if(n&&i==`>`&&a.name==`EndTag`){let t=a.parent;if(t.parent?.lastChild?.name!=`CloseTag`&&(o=K(s.doc,t.parent,r))&&!$.has(o))return{range:e,changes:{from:r,to:r+ +(s.doc.sliceString(r,r+1)===`>`),insert:``}}}else if(n&&i==`/`&&a.name==`IncompleteCloseTag`){let e=a.parent;if(a.from==r-2&&e.lastChild?.name!=`CloseTag`&&(o=K(s.doc,e,r))&&!$.has(o)){let e=r+ +(s.doc.sliceString(r,r+1)===`>`),n=`${o}>`;return{range:t.cursor(r+n.length,-1),changes:{from:r,to:e,insert:n}}}}return{range:e}});return c.changes.empty?!1:(e.dispatch([o,s.update(c,{userEvent:`input.complete`,scrollIntoView:!0})]),!0)});export{ot as html,et as htmlCompletionSource}; \ No newline at end of file +import{A as e,I as t,M as n,b as r,f as i,i as a,m as o,o as s,t as c,v as l,x as u}from"./index-DPVcGf1z.js";import{n as d,r as ee,t as te}from"./dist-T_roqrwM.js";import{n as f,t as ne}from"./dist-ChRcjm7W.js";import{a as re,i as ie,n as ae,o as oe,r as p}from"./dist-C-e5rk9l.js";var se=55,ce=1,le=56,ue=2,de=57,fe=3,m=4,pe=5,h=6,g=7,me=8,he=9,ge=10,_e=11,ve=12,ye=13,_=58,be=14,xe=15,v=59,y=21,Se=23,b=24,Ce=25,x=27,S=28,we=29,Te=32,Ee=35,De=37,Oe=38,ke=0,Ae=1,je={area:!0,base:!0,br:!0,col:!0,command:!0,embed:!0,frame:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0,menuitem:!0},Me={dd:!0,li:!0,optgroup:!0,option:!0,p:!0,rp:!0,rt:!0,tbody:!0,td:!0,tfoot:!0,th:!0,tr:!0},C={dd:{dd:!0,dt:!0},dt:{dd:!0,dt:!0},li:{li:!0},option:{option:!0,optgroup:!0},optgroup:{optgroup:!0},p:{address:!0,article:!0,aside:!0,blockquote:!0,dir:!0,div:!0,dl:!0,fieldset:!0,footer:!0,form:!0,h1:!0,h2:!0,h3:!0,h4:!0,h5:!0,h6:!0,header:!0,hgroup:!0,hr:!0,menu:!0,nav:!0,ol:!0,p:!0,pre:!0,section:!0,table:!0,ul:!0},rp:{rp:!0,rt:!0},rt:{rp:!0,rt:!0},tbody:{tbody:!0,tfoot:!0},td:{td:!0,th:!0},tfoot:{tbody:!0},th:{td:!0,th:!0},thead:{tbody:!0,tfoot:!0},tr:{tr:!0}};function Ne(e){return e==45||e==46||e==58||e>=65&&e<=90||e==95||e>=97&&e<=122||e>=161}var w=null,T=null,E=0;function D(e,t){let n=e.pos+t;if(E==n&&T==e)return w;let r=e.peek(t),i=``;for(;Ne(r);)i+=String.fromCharCode(r),r=e.peek(++t);return T=e,E=n,w=i?i.toLowerCase():r==Pe||r==Fe?void 0:null}var O=60,k=62,A=47,Pe=63,Fe=33,Ie=45;function j(e,t){this.name=e,this.parent=t}var Le=[h,ge,g,me,he],Re=new te({start:null,shift(e,t,n,r){return Le.indexOf(t)>-1?new j(D(r,1)||``,e):e},reduce(e,t){return t==y&&e?e.parent:e},reuse(e,t,n,r){let i=t.type.id;return i==h||i==De?new j(D(r,1)||``,e):e},strict:!1}),ze=new d((e,t)=>{if(e.next!=O){e.next<0&&t.context&&e.acceptToken(_);return}e.advance();let n=e.next==A;n&&e.advance();let r=D(e,0);if(r===void 0)return;if(!r)return e.acceptToken(n?xe:be);let i=t.context?t.context.name:null;if(n){if(r==i)return e.acceptToken(_e);if(i&&Me[i])return e.acceptToken(_,-2);if(t.dialectEnabled(ke))return e.acceptToken(ve);for(let e=t.context;e;e=e.parent)if(e.name==r)return;e.acceptToken(ye)}else{if(r==`script`)return e.acceptToken(g);if(r==`style`)return e.acceptToken(me);if(r==`textarea`)return e.acceptToken(he);if(je.hasOwnProperty(r))return e.acceptToken(ge);i&&C[i]&&C[i][r]?e.acceptToken(_,-1):e.acceptToken(h)}},{contextual:!0}),Be=new d(e=>{for(let t=0,n=0;;n++){if(e.next<0){n&&e.acceptToken(v);break}if(e.next==Ie)t++;else if(e.next==k&&t>=2){n>=3&&e.acceptToken(v,-2);break}else t=0;e.advance()}});function Ve(e){for(;e;e=e.parent)if(e.name==`svg`||e.name==`math`)return!0;return!1}var He=new d((e,t)=>{if(e.next==A&&e.peek(1)==k){let n=t.dialectEnabled(Ae)||Ve(t.context);e.acceptToken(n?pe:m,2)}else e.next==k&&e.acceptToken(m,1)});function M(e,t,n){let r=2+e.length;return new d(i=>{for(let a=0,o=0,s=0;;s++){if(i.next<0){s&&i.acceptToken(t);break}if(a==0&&i.next==O||a==1&&i.next==A||a>=2&&ao?i.acceptToken(t,-o):i.acceptToken(n,-(o-2));break}else if((i.next==10||i.next==13)&&s){i.acceptToken(t,1);break}else a=o=0;i.advance()}})}var Ue=M(`script`,se,ce),We=M(`style`,le,ue),Ge=M(`textarea`,de,fe),Ke=r({"Text RawText IncompleteTag IncompleteCloseTag":u.content,"StartTag StartCloseTag SelfClosingEndTag EndTag":u.angleBracket,TagName:u.tagName,"MismatchedCloseTag/TagName":[u.tagName,u.invalid],AttributeName:u.attributeName,"AttributeValue UnquotedAttributeValue":u.attributeValue,Is:u.definitionOperator,"EntityReference CharacterReference":u.character,Comment:u.blockComment,ProcessingInst:u.processingInstruction,DoctypeDecl:u.documentMeta}),qe=ee.deserialize({version:14,states:",xOVO!rOOO!ZQ#tO'#CrO!`Q#tO'#C{O!eQ#tO'#DOO!jQ#tO'#DRO!oQ#tO'#DTO!tOaO'#CqO#PObO'#CqO#[OdO'#CqO$kO!rO'#CqOOO`'#Cq'#CqO$rO$fO'#DUO$zQ#tO'#DWO%PQ#tO'#DXOOO`'#Dl'#DlOOO`'#DZ'#DZQVO!rOOO%UQ&rO,59^O%aQ&rO,59gO%lQ&rO,59jO%wQ&rO,59mO&SQ&rO,59oOOOa'#D_'#D_O&_OaO'#CyO&jOaO,59]OOOb'#D`'#D`O&rObO'#C|O&}ObO,59]OOOd'#Da'#DaO'VOdO'#DPO'bOdO,59]OOO`'#Db'#DbO'jO!rO,59]O'qQ#tO'#DSOOO`,59],59]OOOp'#Dc'#DcO'vO$fO,59pOOO`,59p,59pO(OQ#|O,59rO(TQ#|O,59sOOO`-E7X-E7XO(YQ&rO'#CtOOQW'#D['#D[O(hQ&rO1G.xOOOa1G.x1G.xOOO`1G/Z1G/ZO(sQ&rO1G/ROOOb1G/R1G/RO)OQ&rO1G/UOOOd1G/U1G/UO)ZQ&rO1G/XOOO`1G/X1G/XO)fQ&rO1G/ZOOOa-E7]-E7]O)qQ#tO'#CzOOO`1G.w1G.wOOOb-E7^-E7^O)vQ#tO'#C}OOOd-E7_-E7_O){Q#tO'#DQOOO`-E7`-E7`O*QQ#|O,59nOOOp-E7a-E7aOOO`1G/[1G/[OOO`1G/^1G/^OOO`1G/_1G/_O*VQ,UO,59`OOQW-E7Y-E7YOOOa7+$d7+$dOOO`7+$u7+$uOOOb7+$m7+$mOOOd7+$p7+$pOOO`7+$s7+$sO*bQ#|O,59fO*gQ#|O,59iO*lQ#|O,59lOOO`1G/Y1G/YO*qO7[O'#CwO+SOMhO'#CwOOQW1G.z1G.zOOO`1G/Q1G/QOOO`1G/T1G/TOOO`1G/W1G/WOOOO'#D]'#D]O+eO7[O,59cOOQW,59c,59cOOOO'#D^'#D^O+vOMhO,59cOOOO-E7Z-E7ZOOQW1G.}1G.}OOOO-E7[-E7[",stateData:`,c~O!_OS~OUSOVPOWQOXROYTO[]O][O^^O_^Oa^Ob^Oc^Od^Oy^O|_O!eZO~OgaO~OgbO~OgcO~OgdO~OgeO~O!XfOPmP![mP~O!YiOQpP![pP~O!ZlORsP![sP~OUSOVPOWQOXROYTOZqO[]O][O^^O_^Oa^Ob^Oc^Od^Oy^O!eZO~O![rO~P#gO!]sO!fuO~OgvO~OgwO~OS|OT}OiyO~OS!POT}OiyO~OS!ROT}OiyO~OS!TOT}OiyO~OS}OT}OiyO~O!XfOPmX![mX~OP!WO![!XO~O!YiOQpX![pX~OQ!ZO![!XO~O!ZlORsX![sX~OR!]O![!XO~O![!XO~P#gOg!_O~O!]sO!f!aO~OS!bO~OS!cO~Oj!dOShXThXihX~OS!fOT!gOiyO~OS!hOT!gOiyO~OS!iOT!gOiyO~OS!jOT!gOiyO~OS!gOT!gOiyO~Og!kO~Og!lO~Og!mO~OS!nO~Ol!qO!a!oO!c!pO~OS!rO~OS!sO~OS!tO~Ob!uOc!uOd!uO!a!wO!b!uO~Ob!xOc!xOd!xO!c!wO!d!xO~Ob!uOc!uOd!uO!a!{O!b!uO~Ob!xOc!xOd!xO!c!{O!d!xO~OT~cbd!ey|!e~`,goto:"%q!aPPPPPPPPPPPPPPPPPPPPP!b!hP!nPP!zP!}#Q#T#Z#^#a#g#j#m#s#y!bP!b!bP$P$V$m$s$y%P%V%]%cPPPPPPPP%iX^OX`pXUOX`pezabcde{!O!Q!S!UR!q!dRhUR!XhXVOX`pRkVR!XkXWOX`pRnWR!XnXXOX`pQrXR!XpXYOX`pQ`ORx`Q{aQ!ObQ!QcQ!SdQ!UeZ!e{!O!Q!S!UQ!v!oR!z!vQ!y!pR!|!yQgUR!VgQjVR!YjQmWR![mQpXR!^pQtZR!`tS_O`ToXp",nodeNames:`⚠ StartCloseTag StartCloseTag StartCloseTag EndTag SelfClosingEndTag StartTag StartTag StartTag StartTag StartTag StartCloseTag StartCloseTag StartCloseTag IncompleteTag IncompleteCloseTag Document Text EntityReference CharacterReference InvalidEntity Element OpenTag TagName Attribute AttributeName Is AttributeValue UnquotedAttributeValue ScriptText CloseTag OpenTag StyleText CloseTag OpenTag TextareaText CloseTag OpenTag CloseTag SelfClosingTag Comment ProcessingInst MismatchedCloseTag CloseTag DoctypeDecl`,maxTerm:68,context:Re,nodeProps:[[`closedBy`,-10,1,2,3,7,8,9,10,11,12,13,`EndTag`,6,`EndTag SelfClosingEndTag`,-4,22,31,34,37,`CloseTag`],[`openedBy`,4,`StartTag StartCloseTag`,5,`StartTag`,-4,30,33,36,38,`OpenTag`],[`group`,-10,14,15,18,19,20,21,40,41,42,43,`Entity`,17,`Entity TextContent`,-3,29,32,35,`TextContent Entity`],[`isolate`,-11,22,30,31,33,34,36,37,38,39,42,43,`ltr`,-3,27,28,40,``]],propSources:[Ke],skippedNodes:[0],repeatNodeCount:9,tokenData:"!]tw8twx7Sx!P8t!P!Q5u!Q!]8t!]!^/^!^!a7S!a#S8t#S#T;{#T#s8t#s$f5u$f;'S8t;'S;=`>V<%l?Ah8t?Ah?BY5u?BY?Mn8t?MnO5u!Z5zblWOX5uXZ7SZ[5u[^7S^p5uqr5urs7Sst+Ptw5uwx7Sx!]5u!]!^7w!^!a7S!a#S5u#S#T7S#T;'S5u;'S;=`8n<%lO5u!R7VVOp7Sqs7St!]7S!]!^7l!^;'S7S;'S;=`7q<%lO7S!R7qOb!R!R7tP;=`<%l7S!Z8OYlWb!ROX+PZ[+P^p+Pqr+Psw+Px!^+P!a#S+P#T;'S+P;'S;=`+t<%lO+P!Z8qP;=`<%l5u!_8{iiSlWOX5uXZ7SZ[5u[^7S^p5uqr8trs7Sst/^tw8twx7Sx!P8t!P!Q5u!Q!]8t!]!^:j!^!a7S!a#S8t#S#T;{#T#s8t#s$f5u$f;'S8t;'S;=`>V<%l?Ah8t?Ah?BY5u?BY?Mn8t?MnO5u!_:sbiSlWb!ROX+PZ[+P^p+Pqr/^sw/^x!P/^!P!Q+P!Q!^/^!a#S/^#S#T0m#T#s/^#s$f+P$f;'S/^;'S;=`1e<%l?Ah/^?Ah?BY+P?BY?Mn/^?MnO+P!VP<%l?Ah;{?Ah?BY7S?BY?Mn;{?MnO7S!V=dXiSb!Rqr0msw0mx!P0m!Q!^0m!a#s0m$f;'S0m;'S;=`1_<%l?Ah0m?BY?Mn0m!V>SP;=`<%l;{!_>YP;=`<%l8t!_>dhiSlWOX@OXZAYZ[@O[^AY^p@OqrBwrsAYswBwwxAYx!PBw!P!Q@O!Q!]Bw!]!^/^!^!aAY!a#SBw#S#TE{#T#sBw#s$f@O$f;'SBw;'S;=`HS<%l?AhBw?Ah?BY@O?BY?MnBw?MnO@O!Z@TalWOX@OXZAYZ[@O[^AY^p@Oqr@OrsAYsw@OwxAYx!]@O!]!^Az!^!aAY!a#S@O#S#TAY#T;'S@O;'S;=`Bq<%lO@O!RA]UOpAYq!]AY!]!^Ao!^;'SAY;'S;=`At<%lOAY!RAtOc!R!RAwP;=`<%lAY!ZBRYlWc!ROX+PZ[+P^p+Pqr+Psw+Px!^+P!a#S+P#T;'S+P;'S;=`+t<%lO+P!ZBtP;=`<%l@O!_COhiSlWOX@OXZAYZ[@O[^AY^p@OqrBwrsAYswBwwxAYx!PBw!P!Q@O!Q!]Bw!]!^Dj!^!aAY!a#SBw#S#TE{#T#sBw#s$f@O$f;'SBw;'S;=`HS<%l?AhBw?Ah?BY@O?BY?MnBw?MnO@O!_DsbiSlWc!ROX+PZ[+P^p+Pqr/^sw/^x!P/^!P!Q+P!Q!^/^!a#S/^#S#T0m#T#s/^#s$f+P$f;'S/^;'S;=`1e<%l?Ah/^?Ah?BY+P?BY?Mn/^?MnO+P!VFQbiSOpAYqrE{rsAYswE{wxAYx!PE{!P!QAY!Q!]E{!]!^GY!^!aAY!a#sE{#s$fAY$f;'SE{;'S;=`G|<%l?AhE{?Ah?BYAY?BY?MnE{?MnOAY!VGaXiSc!Rqr0msw0mx!P0m!Q!^0m!a#s0m$f;'S0m;'S;=`1_<%l?Ah0m?BY?Mn0m!VHPP;=`<%lE{!_HVP;=`<%lBw!ZHcW!cxaP!b`Or(trs'ksv(tw!^(t!^!_)e!_;'S(t;'S;=`*P<%lO(t!aIYliSaPlW!b`!dpOX$qXZ&XZ[$q[^&X^p$qpq&Xqr-_rs&}sv-_vw/^wx(tx}-_}!OKQ!O!P-_!P!Q$q!Q!^-_!^!_*V!_!a&X!a#S-_#S#T1k#T#s-_#s$f$q$f;'S-_;'S;=`3X<%l?Ah-_?Ah?BY$q?BY?Mn-_?MnO$q!aK_kiSaPlW!b`!dpOX$qXZ&XZ[$q[^&X^p$qpq&Xqr-_rs&}sv-_vw/^wx(tx!P-_!P!Q$q!Q!^-_!^!_*V!_!`&X!`!aMS!a#S-_#S#T1k#T#s-_#s$f$q$f;'S-_;'S;=`3X<%l?Ah-_?Ah?BY$q?BY?Mn-_?MnO$q!TM_XaP!b`!dp!fQOr&Xrs&}sv&Xwx(tx!^&X!^!_*V!_;'S&X;'S;=`*y<%lO&X!aNZ!ZiSgQaPlW!b`!dpOX$qXZ&XZ[$q[^&X^p$qpq&Xqr-_rs&}sv-_vw/^wx(tx}-_}!OMz!O!PMz!P!Q$q!Q![Mz![!]Mz!]!^-_!^!_*V!_!a&X!a!c-_!c!}Mz!}#R-_#R#SMz#S#T1k#T#oMz#o#s-_#s$f$q$f$}-_$}%OMz%O%W-_%W%oMz%o%p-_%p&aMz&a&b-_&b1pMz1p4UMz4U4dMz4d4e-_4e$ISMz$IS$I`-_$I`$IbMz$Ib$Je-_$Je$JgMz$Jg$Kh-_$Kh%#tMz%#t&/x-_&/x&EtMz&Et&FV-_&FV;'SMz;'S;:j!#|;:j;=`3X<%l?&r-_?&r?AhMz?Ah?BY$q?BY?MnMz?MnO$q!a!$PP;=`<%lMz!R!$ZY!b`!dpOq*Vqr!$yrs(Vsv*Vwx)ex!a*V!a!b!4t!b;'S*V;'S;=`*s<%lO*V!R!%Q]!b`!dpOr*Vrs(Vsv*Vwx)ex}*V}!O!%y!O!f*V!f!g!']!g#W*V#W#X!0`#X;'S*V;'S;=`*s<%lO*V!R!&QX!b`!dpOr*Vrs(Vsv*Vwx)ex}*V}!O!&m!O;'S*V;'S;=`*s<%lO*V!R!&vV!b`!dp!ePOr*Vrs(Vsv*Vwx)ex;'S*V;'S;=`*s<%lO*V!R!'dX!b`!dpOr*Vrs(Vsv*Vwx)ex!q*V!q!r!(P!r;'S*V;'S;=`*s<%lO*V!R!(WX!b`!dpOr*Vrs(Vsv*Vwx)ex!e*V!e!f!(s!f;'S*V;'S;=`*s<%lO*V!R!(zX!b`!dpOr*Vrs(Vsv*Vwx)ex!v*V!v!w!)g!w;'S*V;'S;=`*s<%lO*V!R!)nX!b`!dpOr*Vrs(Vsv*Vwx)ex!{*V!{!|!*Z!|;'S*V;'S;=`*s<%lO*V!R!*bX!b`!dpOr*Vrs(Vsv*Vwx)ex!r*V!r!s!*}!s;'S*V;'S;=`*s<%lO*V!R!+UX!b`!dpOr*Vrs(Vsv*Vwx)ex!g*V!g!h!+q!h;'S*V;'S;=`*s<%lO*V!R!+xY!b`!dpOr!+qrs!,hsv!+qvw!-Swx!.[x!`!+q!`!a!/j!a;'S!+q;'S;=`!0Y<%lO!+qq!,mV!dpOv!,hvx!-Sx!`!,h!`!a!-q!a;'S!,h;'S;=`!.U<%lO!,hP!-VTO!`!-S!`!a!-f!a;'S!-S;'S;=`!-k<%lO!-SP!-kO|PP!-nP;=`<%l!-Sq!-xS!dp|POv(Vx;'S(V;'S;=`(h<%lO(Vq!.XP;=`<%l!,ha!.aX!b`Or!.[rs!-Ssv!.[vw!-Sw!`!.[!`!a!.|!a;'S!.[;'S;=`!/d<%lO!.[a!/TT!b`|POr)esv)ew;'S)e;'S;=`)y<%lO)ea!/gP;=`<%l!.[!R!/sV!b`!dp|POr*Vrs(Vsv*Vwx)ex;'S*V;'S;=`*s<%lO*V!R!0]P;=`<%l!+q!R!0gX!b`!dpOr*Vrs(Vsv*Vwx)ex#c*V#c#d!1S#d;'S*V;'S;=`*s<%lO*V!R!1ZX!b`!dpOr*Vrs(Vsv*Vwx)ex#V*V#V#W!1v#W;'S*V;'S;=`*s<%lO*V!R!1}X!b`!dpOr*Vrs(Vsv*Vwx)ex#h*V#h#i!2j#i;'S*V;'S;=`*s<%lO*V!R!2qX!b`!dpOr*Vrs(Vsv*Vwx)ex#m*V#m#n!3^#n;'S*V;'S;=`*s<%lO*V!R!3eX!b`!dpOr*Vrs(Vsv*Vwx)ex#d*V#d#e!4Q#e;'S*V;'S;=`*s<%lO*V!R!4XX!b`!dpOr*Vrs(Vsv*Vwx)ex#X*V#X#Y!+q#Y;'S*V;'S;=`*s<%lO*V!R!4{Y!b`!dpOr!4trs!5ksv!4tvw!6Vwx!8]x!a!4t!a!b!:]!b;'S!4t;'S;=`!;r<%lO!4tq!5pV!dpOv!5kvx!6Vx!a!5k!a!b!7W!b;'S!5k;'S;=`!8V<%lO!5kP!6YTO!a!6V!a!b!6i!b;'S!6V;'S;=`!7Q<%lO!6VP!6lTO!`!6V!`!a!6{!a;'S!6V;'S;=`!7Q<%lO!6VP!7QOyPP!7TP;=`<%l!6Vq!7]V!dpOv!5kvx!6Vx!`!5k!`!a!7r!a;'S!5k;'S;=`!8V<%lO!5kq!7yS!dpyPOv(Vx;'S(V;'S;=`(h<%lO(Vq!8YP;=`<%l!5ka!8bX!b`Or!8]rs!6Vsv!8]vw!6Vw!a!8]!a!b!8}!b;'S!8];'S;=`!:V<%lO!8]a!9SX!b`Or!8]rs!6Vsv!8]vw!6Vw!`!8]!`!a!9o!a;'S!8];'S;=`!:V<%lO!8]a!9vT!b`yPOr)esv)ew;'S)e;'S;=`)y<%lO)ea!:YP;=`<%l!8]!R!:dY!b`!dpOr!4trs!5ksv!4tvw!6Vwx!8]x!`!4t!`!a!;S!a;'S!4t;'S;=`!;r<%lO!4t!R!;]V!b`!dpyPOr*Vrs(Vsv*Vwx)ex;'S*V;'S;=`*s<%lO*V!R!;uP;=`<%l!4t!V!{let n=e.type.id;if(n==we)return F(e,t,r);if(n==Te)return F(e,t,i);if(n==Ee)return F(e,t,a);if(n==y&&o.length){let n=e.node,r=n.firstChild,i=r&&P(r,t),a;if(i){for(let e of o)if(e.tag==i&&(!e.attrs||e.attrs(a||=N(r,t)))){let t=n.lastChild,i=t.type.id==Oe?t.from:n.to;if(i>r.to)return{parser:e.parser,overlay:[{from:r.to,to:i}]}}}}if(s&&n==b){let n=e.node,r;if(r=n.firstChild){let e=s[t.read(r.from,r.to)];if(e)for(let r of e){if(r.tagName&&r.tagName!=P(n.parent,t))continue;let e=n.lastChild;if(e.type.id==x){let t=e.from+1,n=e.lastChild,i=e.to-(n&&n.isError?0:1);if(i>t)return{parser:r.parser,overlay:[{from:t,to:i}],bracketed:!0}}else if(e.type.id==S)return{parser:r.parser,overlay:[{from:e.from,to:e.to}]}}}}return null})}var L=[`_blank`,`_self`,`_top`,`_parent`],R=[`ascii`,`utf-8`,`utf-16`,`latin1`,`latin1`],z=[`get`,`post`,`put`,`delete`],B=[`application/x-www-form-urlencoded`,`multipart/form-data`,`text/plain`],V=[`true`,`false`],H={},Je={a:{attrs:{href:null,ping:null,type:null,media:null,target:L,hreflang:null}},abbr:H,address:H,area:{attrs:{alt:null,coords:null,href:null,target:null,ping:null,media:null,hreflang:null,type:null,shape:[`default`,`rect`,`circle`,`poly`]}},article:H,aside:H,audio:{attrs:{src:null,mediagroup:null,crossorigin:[`anonymous`,`use-credentials`],preload:[`none`,`metadata`,`auto`],autoplay:[`autoplay`],loop:[`loop`],controls:[`controls`]}},b:H,base:{attrs:{href:null,target:L}},bdi:H,bdo:H,blockquote:{attrs:{cite:null}},body:H,br:H,button:{attrs:{form:null,formaction:null,name:null,value:null,autofocus:[`autofocus`],disabled:[`autofocus`],formenctype:B,formmethod:z,formnovalidate:[`novalidate`],formtarget:L,type:[`submit`,`reset`,`button`]}},canvas:{attrs:{width:null,height:null}},caption:H,center:H,cite:H,code:H,col:{attrs:{span:null}},colgroup:{attrs:{span:null}},command:{attrs:{type:[`command`,`checkbox`,`radio`],label:null,icon:null,radiogroup:null,command:null,title:null,disabled:[`disabled`],checked:[`checked`]}},data:{attrs:{value:null}},datagrid:{attrs:{disabled:[`disabled`],multiple:[`multiple`]}},datalist:{attrs:{data:null}},dd:H,del:{attrs:{cite:null,datetime:null}},details:{attrs:{open:[`open`]}},dfn:H,div:H,dl:H,dt:H,em:H,embed:{attrs:{src:null,type:null,width:null,height:null}},eventsource:{attrs:{src:null}},fieldset:{attrs:{disabled:[`disabled`],form:null,name:null}},figcaption:H,figure:H,footer:H,form:{attrs:{action:null,name:null,"accept-charset":R,autocomplete:[`on`,`off`],enctype:B,method:z,novalidate:[`novalidate`],target:L}},h1:H,h2:H,h3:H,h4:H,h5:H,h6:H,head:{children:[`title`,`base`,`link`,`style`,`meta`,`script`,`noscript`,`command`]},header:H,hgroup:H,hr:H,html:{attrs:{manifest:null}},i:H,iframe:{attrs:{src:null,srcdoc:null,name:null,width:null,height:null,sandbox:[`allow-top-navigation`,`allow-same-origin`,`allow-forms`,`allow-scripts`],seamless:[`seamless`]}},img:{attrs:{alt:null,src:null,ismap:null,usemap:null,width:null,height:null,crossorigin:[`anonymous`,`use-credentials`]}},input:{attrs:{alt:null,dirname:null,form:null,formaction:null,height:null,list:null,max:null,maxlength:null,min:null,name:null,pattern:null,placeholder:null,size:null,src:null,step:null,value:null,width:null,accept:[`audio/*`,`video/*`,`image/*`],autocomplete:[`on`,`off`],autofocus:[`autofocus`],checked:[`checked`],disabled:[`disabled`],formenctype:B,formmethod:z,formnovalidate:[`novalidate`],formtarget:L,multiple:[`multiple`],readonly:[`readonly`],required:[`required`],type:[`hidden`,`text`,`search`,`tel`,`url`,`email`,`password`,`datetime`,`date`,`month`,`week`,`time`,`datetime-local`,`number`,`range`,`color`,`checkbox`,`radio`,`file`,`submit`,`image`,`reset`,`button`]}},ins:{attrs:{cite:null,datetime:null}},kbd:H,keygen:{attrs:{challenge:null,form:null,name:null,autofocus:[`autofocus`],disabled:[`disabled`],keytype:[`RSA`]}},label:{attrs:{for:null,form:null}},legend:H,li:{attrs:{value:null}},link:{attrs:{href:null,type:null,hreflang:null,media:null,sizes:[`all`,`16x16`,`16x16 32x32`,`16x16 32x32 64x64`]}},map:{attrs:{name:null}},mark:H,menu:{attrs:{label:null,type:[`list`,`context`,`toolbar`]}},meta:{attrs:{content:null,charset:R,name:[`viewport`,`application-name`,`author`,`description`,`generator`,`keywords`],"http-equiv":[`content-language`,`content-type`,`default-style`,`refresh`]}},meter:{attrs:{value:null,min:null,low:null,high:null,max:null,optimum:null}},nav:H,noscript:H,object:{attrs:{data:null,type:null,name:null,usemap:null,form:null,width:null,height:null,typemustmatch:[`typemustmatch`]}},ol:{attrs:{reversed:[`reversed`],start:null,type:[`1`,`a`,`A`,`i`,`I`]},children:[`li`,`script`,`template`,`ul`,`ol`]},optgroup:{attrs:{disabled:[`disabled`],label:null}},option:{attrs:{disabled:[`disabled`],label:null,selected:[`selected`],value:null}},output:{attrs:{for:null,form:null,name:null}},p:H,param:{attrs:{name:null,value:null}},pre:H,progress:{attrs:{value:null,max:null}},q:{attrs:{cite:null}},rp:H,rt:H,ruby:H,samp:H,script:{attrs:{type:[`text/javascript`],src:null,async:[`async`],defer:[`defer`],charset:R}},section:H,select:{attrs:{form:null,name:null,size:null,autofocus:[`autofocus`],disabled:[`disabled`],multiple:[`multiple`]}},slot:{attrs:{name:null}},small:H,source:{attrs:{src:null,type:null,media:null}},span:H,strong:H,style:{attrs:{type:[`text/css`],media:null,scoped:null}},sub:H,summary:H,sup:H,table:H,tbody:H,td:{attrs:{colspan:null,rowspan:null,headers:null}},template:H,textarea:{attrs:{dirname:null,form:null,maxlength:null,name:null,placeholder:null,rows:null,cols:null,autofocus:[`autofocus`],disabled:[`disabled`],readonly:[`readonly`],required:[`required`],wrap:[`soft`,`hard`]}},tfoot:H,th:{attrs:{colspan:null,rowspan:null,headers:null,scope:[`row`,`col`,`rowgroup`,`colgroup`]}},thead:H,time:{attrs:{datetime:null}},title:H,tr:H,track:{attrs:{src:null,label:null,default:null,kind:[`subtitles`,`captions`,`descriptions`,`chapters`,`metadata`],srclang:null}},ul:{children:[`li`,`script`,`template`,`ul`,`ol`]},var:H,video:{attrs:{src:null,poster:null,width:null,height:null,crossorigin:[`anonymous`,`use-credentials`],preload:[`auto`,`metadata`,`none`],autoplay:[`autoplay`],mediagroup:[`movie`],muted:[`muted`],controls:[`controls`]}},wbr:H},U={accesskey:null,class:null,contenteditable:V,contextmenu:null,dir:[`ltr`,`rtl`,`auto`],draggable:[`true`,`false`,`auto`],dropzone:[`copy`,`move`,`link`,`string:`,`file:`],hidden:[`hidden`],id:null,inert:[`inert`],itemid:null,itemprop:null,itemref:null,itemscope:[`itemscope`],itemtype:null,lang:[`ar`,`bn`,`de`,`en-GB`,`en-US`,`es`,`fr`,`hi`,`id`,`ja`,`pa`,`pt`,`ru`,`tr`,`zh`],spellcheck:V,autocorrect:V,autocapitalize:V,style:null,tabindex:null,title:null,translate:[`yes`,`no`],rel:[`stylesheet`,`alternate`,`author`,`bookmark`,`help`,`license`,`next`,`nofollow`,`noreferrer`,`prefetch`,`prev`,`search`,`tag`],role:`alert application article banner button cell checkbox complementary contentinfo dialog document feed figure form grid gridcell heading img list listbox listitem main navigation region row rowgroup search switch tab table tabpanel textbox timer`.split(` `),"aria-activedescendant":null,"aria-atomic":V,"aria-autocomplete":[`inline`,`list`,`both`,`none`],"aria-busy":V,"aria-checked":[`true`,`false`,`mixed`,`undefined`],"aria-controls":null,"aria-describedby":null,"aria-disabled":V,"aria-dropeffect":null,"aria-expanded":[`true`,`false`,`undefined`],"aria-flowto":null,"aria-grabbed":[`true`,`false`,`undefined`],"aria-haspopup":V,"aria-hidden":V,"aria-invalid":[`true`,`false`,`grammar`,`spelling`],"aria-label":null,"aria-labelledby":null,"aria-level":null,"aria-live":[`off`,`polite`,`assertive`],"aria-multiline":V,"aria-multiselectable":V,"aria-owns":null,"aria-posinset":null,"aria-pressed":[`true`,`false`,`mixed`,`undefined`],"aria-readonly":V,"aria-relevant":null,"aria-required":V,"aria-selected":[`true`,`false`,`undefined`],"aria-setsize":null,"aria-sort":[`ascending`,`descending`,`none`,`other`],"aria-valuemax":null,"aria-valuemin":null,"aria-valuenow":null,"aria-valuetext":null},W=`beforeunload copy cut dragstart dragover dragleave dragenter dragend drag paste focus blur change click load mousedown mouseenter mouseleave mouseup keydown keyup resize scroll unload`.split(` `).map(e=>`on`+e);for(let e of W)U[e]=null;var G=class{constructor(e,t){this.tags={...Je,...e},this.globalAttrs={...U,...t},this.allTags=Object.keys(this.tags),this.globalAttrNames=Object.keys(this.globalAttrs)}};G.default=new G;function K(e,t,n=e.length){if(!t)return``;let r=t.firstChild,i=r&&r.getChild(`TagName`);return i?e.sliceString(i.from,Math.min(i.to,n)):``}function q(e,t=!1){for(;e;e=e.parent)if(e.name==`Element`)if(t)t=!1;else return e;return null}function J(e,t,n){return n.tags[K(e,q(t))]?.children||n.allTags}function Y(e,t){let n=[];for(let r=q(t);r&&!r.type.isTop;r=q(r.parent)){let i=K(e,r);if(i&&r.lastChild.name==`CloseTag`)break;i&&n.indexOf(i)<0&&(t.name==`EndTag`||t.from>=r.firstChild.to)&&n.push(i)}return n}var X=/^[:\-\.\w\u00b7-\uffff]*$/;function Ye(e,t,n,r,i){let a=/\s*>/.test(e.sliceDoc(i,i+5))?``:`>`,o=q(n,n.name==`StartTag`||n.name==`TagName`);return{from:r,to:i,options:J(e.doc,o,t).map(e=>({label:e,type:`type`})).concat(Y(e.doc,n).map((e,t)=>({label:`/`+e,apply:`/`+e+a,type:`type`,boost:99-t}))),validFor:/^\/?[:\-\.\w\u00b7-\uffff]*$/}}function Xe(e,t,n,r){let i=/\s*>/.test(e.sliceDoc(r,r+5))?``:`>`;return{from:n,to:r,options:Y(e.doc,t).map((e,t)=>({label:e,apply:e+i,type:`type`,boost:99-t})),validFor:X}}function Ze(e,t,n,r){let i=[],a=0;for(let r of J(e.doc,n,t))i.push({label:`<`+r,type:`type`});for(let t of Y(e.doc,n))i.push({label:``,type:`type`,boost:99-a++});return{from:r,to:r,options:i,validFor:/^<\/?[:\-\.\w\u00b7-\uffff]*$/}}function Qe(e,t,n,r,i){let a=q(n),o=a?t.tags[K(e.doc,a)]:null,s=o&&o.attrs?Object.keys(o.attrs):[];return{from:r,to:i,options:(o&&o.globalAttrs===!1?s:s.length?s.concat(t.globalAttrNames):t.globalAttrNames).map(e=>({label:e,type:`property`})),validFor:X}}function $e(e,t,n,r,i){let a=n.parent?.getChild(`AttributeName`),o=[],s;if(a){let c=e.sliceDoc(a.from,a.to),l=t.globalAttrs[c];if(!l){let r=q(n),i=r?t.tags[K(e.doc,r)]:null;l=i?.attrs&&i.attrs[c]}if(l){let t=e.sliceDoc(r,i).toLowerCase(),n=`"`,a=`"`;/^['"]/.test(t)?(s=t[0]==`"`?/^[^"]*$/:/^[^']*$/,n=``,a=e.sliceDoc(i,i+1)==t[0]?``:t[0],t=t.slice(1),r++):s=/^[^\s<>='"]*$/;for(let e of l)o.push({label:e,apply:n+e+a,type:`constant`})}}return{from:r,to:i,options:o,validFor:s}}function Z(e,t){let{state:n,pos:r}=t,i=l(n).resolveInner(r,-1),a=i.resolve(r);for(let e=r,t;a==i&&(t=i.childBefore(e));){let n=t.lastChild;if(!n||!n.type.isError||n.fromZ(r,e)}var nt=p.parser.configure({top:`SingleExpression`}),rt=[{tag:`script`,attrs:e=>e.type==`text/typescript`||e.lang==`ts`,parser:oe.parser},{tag:`script`,attrs:e=>e.type==`text/babel`||e.type==`text/jsx`,parser:ie.parser},{tag:`script`,attrs:e=>e.type==`text/typescript-jsx`,parser:re.parser},{tag:`script`,attrs(e){return/^(importmap|speculationrules|application\/(.+\+)?json)$/i.test(e.type)},parser:nt},{tag:`script`,attrs(e){return!e.type||/^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i.test(e.type)},parser:p.parser},{tag:`style`,attrs(e){return(!e.lang||e.lang==`css`)&&(!e.type||/^(text\/)?(x-)?(stylesheet|css)$/i.test(e.type))},parser:f.parser}],it=[{name:`style`,parser:f.parser.configure({top:`Styles`})}].concat(W.map(e=>({name:e,parser:p.parser}))),at=c.define({name:`html`,parser:qe.configure({props:[o.add({Element(e){let t=/^(\s*)(<\/)?/.exec(e.textAfter);return e.node.to<=e.pos+t[0].length?e.continue():e.lineIndent(e.node.from)+(t[2]?0:e.unit)},"OpenTag CloseTag SelfClosingTag"(e){return e.column(e.node.from)+e.unit},Document(e){if(e.pos+/\s*/.exec(e.textAfter)[0].lengthe.getChild(`TagName`)})]}),languageData:{commentTokens:{block:{open:``}},indentOnInput:/^\s*<\/\w+\W$/,wordChars:`-_`}}),Q=at.configure({wrap:I(rt,it)});function ot(e={}){let t=``,n;return e.matchClosingTags===!1&&(t=`noMatch`),e.selfClosingTags===!0&&(t=(t?t+` `:``)+`selfClosing`),(e.nestedLanguages&&e.nestedLanguages.length||e.nestedAttributes&&e.nestedAttributes.length)&&(n=I((e.nestedLanguages||[]).concat(rt),(e.nestedAttributes||[]).concat(it))),new a(n?at.configure({wrap:n,dialect:t}):t?Q.configure({dialect:t}):Q,[Q.data.of({autocomplete:tt(e)}),e.autoCloseTags===!1?[]:st,ae().support,ne().support])}var $=new Set(`area base br col command embed frame hr img input keygen link meta param source track wbr menuitem`.split(` `)),st=n.inputHandler.of((e,n,r,i,a)=>{if(e.composing||e.state.readOnly||n!=r||i!=`>`&&i!=`/`||!Q.isActiveAt(e.state,n,-1))return!1;let o=a(),{state:s}=o,c=s.changeByRange(e=>{let n=s.doc.sliceString(e.from-1,e.to)==i,{head:r}=e,a=l(s).resolveInner(r,-1),o;if(n&&i==`>`&&a.name==`EndTag`){let t=a.parent;if(t.parent?.lastChild?.name!=`CloseTag`&&(o=K(s.doc,t.parent,r))&&!$.has(o))return{range:e,changes:{from:r,to:r+ +(s.doc.sliceString(r,r+1)===`>`),insert:``}}}else if(n&&i==`/`&&a.name==`IncompleteCloseTag`){let e=a.parent;if(a.from==r-2&&e.lastChild?.name!=`CloseTag`&&(o=K(s.doc,e,r))&&!$.has(o)){let e=r+ +(s.doc.sliceString(r,r+1)===`>`),n=`${o}>`;return{range:t.cursor(r+n.length,-1),changes:{from:r,to:e,insert:n}}}}return{range:e}});return c.changes.empty?!1:(e.dispatch([o,s.update(c,{userEvent:`input.complete`,scrollIntoView:!0})]),!0)});export{ot as html,et as htmlCompletionSource}; \ No newline at end of file diff --git a/mcp/src/agents_remember/package_data/dashboard/assets/dist-BiIHlEw9.js b/mcp/src/agents_remember/package_data/dashboard/assets/dist-BzthboWn.js similarity index 99% rename from mcp/src/agents_remember/package_data/dashboard/assets/dist-BiIHlEw9.js rename to mcp/src/agents_remember/package_data/dashboard/assets/dist-BzthboWn.js index 64c7b64e..68f37af5 100644 --- a/mcp/src/agents_remember/package_data/dashboard/assets/dist-BiIHlEw9.js +++ b/mcp/src/agents_remember/package_data/dashboard/assets/dist-BzthboWn.js @@ -1,4 +1,4 @@ -import{C as e,D as t,b as n,d as r,f as i,i as a,l as o,m as s,t as ee,v as te,x as c}from"./index-CLJ0aycO.js";import{n as l,r as ne,t as re}from"./dist-B2R-c1vX.js";import{i as u,n as ie,r as ae}from"./dist-BxWz473c.js";var oe=1,d=194,f=195,se=196,p=197,ce=198,le=199,ue=200,de=2,m=3,h=201,g=24,fe=25,pe=49,me=50,he=55,ge=56,_e=57,ve=59,ye=60,be=61,xe=62,Se=63,Ce=65,we=238,Te=71,Ee=241,De=242,Oe=243,ke=244,Ae=245,_=246,v=247,y=248,b=72,x=249,S=250,C=251,je=252,Me=253,Ne=254,Pe=255,Fe=256,Ie=73,Le=77,Re=263,ze=112,Be=130,Ve=151,He=152,Ue=155,w=10,T=13,E=32,D=9,O=35,We=40,Ge=46,k=123,A=125,j=39,M=34,N=92,Ke=111,P=120,qe=78,Je=117,Ye=85,Xe=new Set([fe,pe,me,Re,Ce,Be,ge,_e,we,xe,Se,b,Ie,Le,ye,be,Ve,He,Ue,ze]);function F(e){return e==w||e==T}function I(e){return e>=48&&e<=57||e>=65&&e<=70||e>=97&&e<=102}var Ze=new l((e,t)=>{let n;if(e.next<0)e.acceptToken(le);else if(t.context.flags&L)F(e.next)&&e.acceptToken(ce,1);else if(((n=e.peek(-1))<0||F(n))&&t.canShift(p)){let t=0;for(;e.next==E||e.next==D;)e.advance(),t++;(e.next==w||e.next==T||e.next==O)&&e.acceptToken(p,-t)}else F(e.next)&&e.acceptToken(se,1)},{contextual:!0}),Qe=new l((e,t)=>{let n=t.context;if(n.flags)return;let r=e.peek(-1);if(r==w||r==T){let t=0,r=0;for(;;){if(e.next==E)t++;else if(e.next==D)t+=8-t%8;else break;e.advance(),r++}t!=n.indent&&e.next!=w&&e.next!=T&&e.next!=O&&(t[e,t|R])),tt=new re({start:$e,reduce(e,t,n,r){return e.flags&L&&Xe.has(t)||(t==Te||t==b)&&e.flags&R?e.parent:e},shift(e,t,n,r){return t==d?new U(e,et(r.read(r.pos,n.pos)),0):t==f?e.parent:t==g||t==he||t==ve||t==m?new U(e,0,L):W.has(t)?new U(e,0,W.get(t)|e.flags&L):e},hash(e){return e.hash}}),nt=new l(e=>{for(let t=0;t<5;t++){if(e.next!=`print`.charCodeAt(t))return;e.advance()}if(!/\w/.test(String.fromCharCode(e.next)))for(let t=0;;t++){let n=e.peek(t);if(!(n==E||n==D)){n!=We&&n!=Ge&&n!=w&&n!=T&&n!=O&&e.acceptToken(oe);return}}}),rt=new l((e,t)=>{let{flags:n}=t.context,r=n&z?M:j,i=(n&B)>0,a=!(n&V),o=(n&H)>0,s=e.pos;for(;!(e.next<0);)if(o&&e.next==k)if(e.peek(1)==k)e.advance(2);else{if(e.pos==s){e.acceptToken(m,1);return}break}else if(a&&e.next==N){if(e.pos==s){e.advance();let t=e.next;t>=0&&(e.advance(),it(e,t)),e.acceptToken(de);return}break}else if(e.next==N&&!a&&e.peek(1)>-1)e.advance(2);else if(e.next==r&&(!i||e.peek(1)==r&&e.peek(2)==r)){if(e.pos==s){e.acceptToken(h,i?3:1);return}break}else if(e.next==w){if(i)e.advance();else if(e.pos==s){e.acceptToken(h);return}break}else e.advance();e.pos>s&&e.acceptToken(ue)});function it(e,t){if(t==Ke)for(let t=0;t<2&&e.next>=48&&e.next<=55;t++)e.advance();else if(t==P)for(let t=0;t<2&&I(e.next);t++)e.advance();else if(t==Je)for(let t=0;t<4&&I(e.next);t++)e.advance();else if(t==Ye)for(let t=0;t<8&&I(e.next);t++)e.advance();else if(t==qe&&e.next==k){for(e.advance();e.next>=0&&e.next!=A&&e.next!=j&&e.next!=M&&e.next!=w;)e.advance();e.next==A&&e.advance()}}var at=n({'async "*" "**" FormatConversion FormatSpec':c.modifier,"for while if elif else try except finally return raise break continue with pass assert await yield match case":c.controlKeyword,"in not and or is del":c.operatorKeyword,"from def class global nonlocal lambda":c.definitionKeyword,import:c.moduleKeyword,"with as print":c.keyword,Boolean:c.bool,None:c.null,VariableName:c.variableName,"CallExpression/VariableName":c.function(c.variableName),"FunctionDefinition/VariableName":c.function(c.definition(c.variableName)),"ClassDefinition/VariableName":c.definition(c.className),PropertyName:c.propertyName,"CallExpression/MemberExpression/PropertyName":c.function(c.propertyName),Comment:c.lineComment,Number:c.number,String:c.string,FormatString:c.special(c.string),Escape:c.escape,UpdateOp:c.updateOperator,"ArithOp!":c.arithmeticOperator,BitOp:c.bitwiseOperator,CompareOp:c.compareOperator,AssignOp:c.definitionOperator,Ellipsis:c.punctuation,At:c.meta,"( )":c.paren,"[ ]":c.squareBracket,"{ }":c.brace,".":c.derefOperator,", ;":c.separator}),ot={__proto__:null,await:44,or:54,and:56,in:60,not:62,is:64,if:70,else:72,lambda:76,yield:94,from:96,async:102,for:104,None:162,True:164,False:164,del:178,pass:182,break:186,continue:190,return:194,raise:202,import:206,as:208,global:212,nonlocal:214,assert:218,type:223,elif:236,while:240,try:246,except:248,finally:250,with:254,def:258,class:268,match:279,case:285},st=ne.deserialize({version:14,states:"##jQ`QeOOP$}OSOOO&WQtO'#HUOOQS'#Co'#CoOOQS'#Cp'#CpO'vQdO'#CnO*UQtO'#HTOOQS'#HU'#HUOOQS'#DU'#DUOOQS'#HT'#HTO*rQdO'#D_O+VQdO'#DfO+gQdO'#DjO+zOWO'#DuO,VOWO'#DvO.[QtO'#GuOOQS'#Gu'#GuO'vQdO'#GtO0ZQtO'#GtOOQS'#Eb'#EbO0rQdO'#EcOOQS'#Gs'#GsO0|QdO'#GrOOQV'#Gr'#GrO1XQdO'#FYOOQS'#G^'#G^O1^QdO'#FXOOQV'#IS'#ISOOQV'#Gq'#GqOOQV'#Fq'#FqQ`QeOOO'vQdO'#CqO1lQdO'#C}O1sQdO'#DRO2RQdO'#HYO2cQtO'#EVO'vQdO'#EWOOQS'#EY'#EYOOQS'#E['#E[OOQS'#E^'#E^O2wQdO'#E`O3_QdO'#EdO3rQdO'#EfO3zQtO'#EfO1XQdO'#EiO0rQdO'#ElO1XQdO'#EnO0rQdO'#EtO0rQdO'#EwO4VQdO'#EyO4^QdO'#FOO4iQdO'#EzO0rQdO'#FOO1XQdO'#FQO1XQdO'#FVO4nQdO'#F[P4uOdO'#GpPOOO)CBd)CBdOOQS'#Ce'#CeOOQS'#Cf'#CfOOQS'#Cg'#CgOOQS'#Ch'#ChOOQS'#Ci'#CiOOQS'#Cj'#CjOOQS'#Cl'#ClO'vQdO,59OO'vQdO,59OO'vQdO,59OO'vQdO,59OO'vQdO,59OO'vQdO,59OO5TQdO'#DoOOQS,5:Y,5:YO5hQdO'#HdOOQS,5:],5:]O5uQ!fO,5:]O5zQtO,59YO1lQdO,59bO1lQdO,59bO1lQdO,59bO8jQdO,59bO8oQdO,59bO8vQdO,59jO8}QdO'#HTO:TQdO'#HSOOQS'#HS'#HSOOQS'#D['#D[O:lQdO,59aO'vQdO,59aO:zQdO,59aOOQS,59y,59yO;PQdO,5:RO'vQdO,5:ROOQS,5:Q,5:QO;_QdO,5:QO;dQdO,5:XO'vQdO,5:XO'vQdO,5:VOOQS,5:U,5:UO;uQdO,5:UO;zQdO,5:WOOOW'#Fy'#FyOOOOQS'#Ds'#DsOOQS1G/w1G/wOOQS1G.|1G.|O!/[QtO1G.|O!/cQtO1G.|O1lQdO1G.|O!0OQdO1G/UOOQS'#DZ'#DZO0rQdO,59tOOQS1G.{1G.{O!0VQdO1G/eO!0gQdO1G/eO!0oQdO1G/fO'vQdO'#H[O!0tQdO'#H[O!0yQtO1G.{O!1ZQdO,59iO!2aQdO,5=zO!2qQdO,5=zO!2yQdO1G/mO!3OQtO1G/mOOQS1G/l1G/lO!3`QdO,5=uO!4VQdO,5=uO0rQdO1G/qO!4tQdO1G/sO!4yQtO1G/sO!5ZQtO1G/qOOQS1G/p1G/pOOQS1G/r1G/rOOOW-E9w-E9wOOQS1G/{1G/{O!5kQdO'#HxO0rQdO'#HxO!5|QdO,5>cOOOW-E9x-E9xOOQS1G/|1G/|OOQS-E9{-E9{O!6[Q#xO1G2zO!6{QtO1G2zO'vQdO,5kOOQS1G1`1G1`O!8RQdO1G1`OOQS'#DV'#DVO0rQdO,5=qOOQS,5=q,5=qO!8WQdO'#FrO!8cQdO,59oO!8kQdO1G/XO!8uQtO,5=uOOQS1G3`1G3`OOQS,5:m,5:mO!9fQdO'#GtOOQS,5jO!;ZQdO,5>jO1XQdO,5>jO!;lQdO,5>iOOQS-E:R-E:RO!;qQdO1G0lO!;|QdO1G0lO!lO!lO!hO!=VQdO,5>hO!=hQdO'#EpO0rQdO1G0tO!=sQdO1G0tO!=xQgO1G0zO!AvQgO1G0}O!EqQdO,5>oO!E{QdO,5>oO!FTQtO,5>oO0rQdO1G1PO!F_QdO1G1PO4iQdO1G1UO!!vQdO1G1WOOQV,5;a,5;aO!FdQfO,5;aO!FiQgO1G1QO!JjQdO'#GZO4iQdO1G1QO4iQdO1G1QO!JzQdO,5>pO!KXQdO,5>pO1XQdO,5>pOOQV1G1U1G1UO!KaQdO'#FSO!KrQ!fO1G1WO!KzQdO1G1WOOQV1G1]1G1]O4iQdO1G1]O!LPQdO1G1]O!LXQdO'#F^OOQV1G1b1G1bO!#ZQtO1G1bPOOO1G2v1G2vP!L^OSO1G2vOOQS,5=},5=}OOQS'#Dp'#DpO0rQdO,5=}O!LfQdO,5=|O!LyQdO,5=|OOQS1G/u1G/uO!MRQdO,5>PO!McQdO,5>PO!MkQdO,5>PO!NOQdO,5>PO!N`QdO,5>POOQS1G3j1G3jOOQS7+$h7+$hO!8kQdO7+$pO#!RQdO1G.|O#!YQdO1G.|OOQS1G/`1G/`OOQS,5<`,5<`O'vQdO,5<`OOQS7+%P7+%PO#!aQdO7+%POOQS-E9r-E9rOOQS7+%Q7+%QO#!qQdO,5=vO'vQdO,5=vOOQS7+$g7+$gO#!vQdO7+%PO##OQdO7+%QO##TQdO1G3fOOQS7+%X7+%XO##eQdO1G3fO##mQdO7+%XOOQS,5<_,5<_O'vQdO,5<_O##rQdO1G3aOOQS-E9q-E9qO#$iQdO7+%]OOQS7+%_7+%_O#$wQdO1G3aO#%fQdO7+%_O#%kQdO1G3gO#%{QdO1G3gO#&TQdO7+%]O#&YQdO,5>dO#&sQdO,5>dO#&sQdO,5>dOOQS'#Dx'#DxO#'UO&jO'#DzO#'aO`O'#HyOOOW1G3}1G3}O#'fQdO1G3}O#'nQdO1G3}O#'yQ#xO7+(fO#(jQtO1G2UP#)TQdO'#GOOOQS,5nQdO,5sQdO1G4OOOQS-E9y-E9yO#?^QdO1G4OO<[QdO'#H{OOOO'#D{'#D{OOOO'#F|'#F|O#?oO&jO,5:fOOOW,5>e,5>eOOOW7+)i7+)iO#?zQdO7+)iO#@SQdO1G2zO#@mQdO1G2zP'vQdO'#FuO0rQdO<mO#BQQdO,5>mOOQS1G0v1G0vOOQS<rO#KgQdO,5>rO#KrQdO,5>rO#K}QdO,5>qO#L`QdO,5>qOOQS1G1Y1G1YOOQS,5;p,5;pOOQV<VAN>VO$ oQdO<cAN>cO0rQdO1G1|O$!PQtO1G1|P$!ZQdO'#FvOOQS1G2R1G2RP$!hQdO'#F{O$!uQdO7+)jO$#`QdO,5>gOOOO-E9z-E9zOOOW<tO$4{QdO,5>tO1XQdO,5vO$)nQdO,5>vOOQS1G1p1G1pOOQS,5<[,5<[OOQU7+'P7+'PO$+zQdO1G/iO$)nQdO,5wO$8zQdO,5>wOOQS1G1s1G1sOOQS7+'S7+'SP$)nQdO'#GdO$9SQdO1G4bO$9^QdO1G4bO$9fQdO1G4bOOQS7+%T7+%TO$9tQdO1G1tO$:SQtO'#FaO$:ZQdO,5<}OOQS,5<},5<}O$:iQdO1G4cOOQS-E:a-E:aO$)nQdO,5<|O$:pQdO,5<|O$:uQdO7+)|OOQS-E:`-E:`O$;PQdO7+)|O$)nQdO,5S~O%cOS%^OSSOS%]PQ~OPdOVaOfoOhYOopOs!POvqO!PrO!Q{O!T!SO!U!RO!XZO!][O!h`O!r`O!s`O!t`O!{tO!}uO#PvO#RwO#TxO#XyO#ZzO#^|O#_|O#a}O#c!OO#l!QO#o!TO#s!UO#u!VO#z!WO#}hO$P!XO%oRO%pRO%tSO%uWO&Z]O&[]O&]]O&^]O&_]O&`]O&a]O&b]O&c^O&d^O&e^O&f^O&g^O&h^O&i^O&j^O~O%]!YO~OV!aO_!aOa!bOh!iO!X!kO!f!mO%j![O%k!]O%l!^O%m!_O%n!_O%o!`O%p!`O%q!aO%r!aO%s!aO~Ok%xXl%xXm%xXn%xXo%xXp%xXs%xXz%xX{%xX!x%xX#g%xX%[%xX%_%xX%z%xXg%xX!T%xX!U%xX%{%xX!W%xX![%xX!Q%xX#[%xXt%xX!m%xX~P%SOfoOhYO!XZO!][O!h`O!r`O!s`O!t`O%oRO%pRO%tSO%uWO&Z]O&[]O&]]O&^]O&_]O&`]O&a]O&b]O&c^O&d^O&e^O&f^O&g^O&h^O&i^O&j^O~Oz%wX{%wX#g%wX%[%wX%_%wX%z%wX~Ok!pOl!qOm!oOn!oOo!rOp!sOs!tO!x%wX~P)pOV!zOg!|Oo0cOv0qO!PrO~P'vOV#OOo0cOv0qO!W#PO~P'vOV#SOa#TOo0cOv0qO![#UO~P'vOQ#XO%`#XO%a#ZO~OQ#^OR#[O%`#^O%a#`O~OV%iX_%iXa%iXh%iXk%iXl%iXm%iXn%iXo%iXp%iXs%iXz%iX!X%iX!f%iX%j%iX%k%iX%l%iX%m%iX%n%iX%o%iX%p%iX%q%iX%r%iX%s%iXg%iX!T%iX!U%iX~O&Z]O&[]O&]]O&^]O&_]O&`]O&a]O&b]O&c^O&d^O&e^O&f^O&g^O&h^O&i^O&j^O{%iX!x%iX#g%iX%[%iX%_%iX%z%iX%{%iX!W%iX![%iX!Q%iX#[%iXt%iX!m%iX~P,eOz#dO{%hX!x%hX#g%hX%[%hX%_%hX%z%hX~Oo0cOv0qO~P'vO#g#gO%[#iO%_#iO~O%uWO~O!T#nO#u!VO#z!WO#}hO~OopO~P'vOV#sOa#tO%uWO{wP~OV#xOo0cOv0qO!Q#yO~P'vO{#{O!x$QO%z#|O#g!yX%[!yX%_!yX~OV#xOo0cOv0qO#g#SX%[#SX%_#SX~P'vOo0cOv0qO#g#WX%[#WX%_#WX~P'vOh$WO%uWO~O!f$YO!r$YO%uWO~OV$eO~P'vO!U$gO#s$hO#u$iO~O{$jO~OV$qO~P'vOS$sO%[$rO%_$rO%c$tO~OV$}Oa$}Og%POo0cOv0qO~P'vOo0cOv0qO{%SO~P'vO&Y%UO~Oa!bOh!iO!X!kO!f!mOVba_bakbalbambanbaobapbasbazba{ba!xba#gba%[ba%_ba%jba%kba%lba%mba%nba%oba%pba%qba%rba%sba%zbagba!Tba!Uba%{ba!Wba![ba!Qba#[batba!mba~On%ZO~Oo%ZO~P'vOo0cO~P'vOk0eOl0fOm0dOn0dOo0mOp0nOs0rOg%wX!T%wX!U%wX%{%wX!W%wX![%wX!Q%wX#[%wX!m%wX~P)pO%{%]Og%vXz%vX!T%vX!U%vX!W%vX{%vX~Og%_Oz%`O!T%dO!U%cO~Og%_O~Oz%gO!T%dO!U%cO!W&SX~O!W%kO~Oz%lO{%nO!T%dO!U%cO![%}X~O![%rO~O![%sO~OQ#XO%`#XO%a%uO~OV%wOo0cOv0qO!PrO~P'vOQ#^OR#[O%`#^O%a%zO~OV!qa_!qaa!qah!qak!qal!qam!qan!qao!qap!qas!qaz!qa{!qa!X!qa!f!qa!x!qa#g!qa%[!qa%_!qa%j!qa%k!qa%l!qa%m!qa%n!qa%o!qa%p!qa%q!qa%r!qa%s!qa%z!qag!qa!T!qa!U!qa%{!qa!W!qa![!qa!Q!qa#[!qat!qa!m!qa~P#yOz%|O{%ha!x%ha#g%ha%[%ha%_%ha%z%ha~P%SOV&OOopOvqO{%ha!x%ha#g%ha%[%ha%_%ha%z%ha~P'vOz%|O{%ha!x%ha#g%ha%[%ha%_%ha%z%ha~OPdOVaOopOvqO!PrO!Q{O!{tO!}uO#PvO#RwO#TxO#XyO#ZzO#^|O#_|O#a}O#c!OO#g$zX%[$zX%_$zX~P'vO#g#gO%[&TO%_&TO~O!f&UOh&sX%[&sXz&sX#[&sX#g&sX%_&sX#Z&sXg&sX~Oh!iO%[&WO~Okealeameaneaoeapeaseazea{ea!xea#gea%[ea%_ea%zeagea!Tea!Uea%{ea!Wea![ea!Qea#[eatea!mea~P%SOsqazqa{qa#gqa%[qa%_qa%zqa~Ok!pOl!qOm!oOn!oOo!rOp!sO!xqa~PEcO%z&YOz%yX{%yX~O%uWOz%yX{%yX~Oz&]O{wX~O{&_O~Oz%lO#g%}X%[%}X%_%}Xg%}X{%}X![%}X!m%}X%z%}X~OV0lOo0cOv0qO!PrO~P'vO%z#|O#gUa%[Ua%_Ua~Oz&hO#g&PX%[&PX%_&PXn&PX~P%SOz&kO!Q&jO#g#Wa%[#Wa%_#Wa~Oz&lO#[&nO#g&rX%[&rX%_&rXg&rX~O!f$YO!r$YO#Z&qO%uWO~O#Z&qO~Oz&sO#g&tX%[&tX%_&tX~Oz&uO#g&pX%[&pX%_&pX{&pX~O!X&wO%z&xO~Oz&|On&wX~P%SOn'PO~OPdOVaOopOvqO!PrO!Q{O!{tO!}uO#PvO#RwO#TxO#XyO#ZzO#^|O#_|O#a}O#c!OO%['UO~P'vOt'YO#p'WO#q'XOP#naV#naf#nah#nao#nas#nav#na!P#na!Q#na!T#na!U#na!X#na!]#na!h#na!r#na!s#na!t#na!{#na!}#na#P#na#R#na#T#na#X#na#Z#na#^#na#_#na#a#na#c#na#l#na#o#na#s#na#u#na#z#na#}#na$P#na%X#na%o#na%p#na%t#na%u#na&Z#na&[#na&]#na&^#na&_#na&`#na&a#na&b#na&c#na&d#na&e#na&f#na&g#na&h#na&i#na&j#na%Z#na%_#na~Oz'ZO#[']O{&xX~Oh'_O!X&wO~Oh!iO{$jO!X&wO~O{'eO~P%SO%['hO%_'hO~OS'iO%['hO%_'hO~OV!aO_!aOa!bOh!iO!X!kO!f!mO%l!^O%m!_O%n!_O%o!`O%p!`O%q!aO%r!aO%s!aOkWilWimWinWioWipWisWizWi{Wi!xWi#gWi%[Wi%_Wi%jWi%zWigWi!TWi!UWi%{Wi!WWi![Wi!QWi#[WitWi!mWi~O%k!]O~P!#uO%kWi~P!#uOV!aO_!aOa!bOh!iO!X!kO!f!mO%o!`O%p!`O%q!aO%r!aO%s!aOkWilWimWinWioWipWisWizWi{Wi!xWi#gWi%[Wi%_Wi%jWi%kWi%lWi%zWigWi!TWi!UWi%{Wi!WWi![Wi!QWi#[WitWi!mWi~O%m!_O%n!_O~P!&pO%mWi%nWi~P!&pOa!bOh!iO!X!kO!f!mOkWilWimWinWioWipWisWizWi{Wi!xWi#gWi%[Wi%_Wi%jWi%kWi%lWi%mWi%nWi%oWi%pWi%zWigWi!TWi!UWi%{Wi!WWi![Wi!QWi#[WitWi!mWi~OV!aO_!aO%q!aO%r!aO%s!aO~P!)nOVWi_Wi%qWi%rWi%sWi~P!)nO!T%dO!U%cOg&VXz&VX~O%z'kO%{'kO~P,eOz'mOg&UX~Og'oO~Oz'pO{'rO!W&XX~Oo0cOv0qOz'pO{'sO!W&XX~P'vO!W'uO~Om!oOn!oOo!rOp!sOkjisjizji{ji!xji#gji%[ji%_ji%zji~Ol!qO~P!.aOlji~P!.aOk0eOl0fOm0dOn0dOo0mOp0nO~Ot'wO~P!/jOV'|Og'}Oo0cOv0qO~P'vOg'}Oz(OO~Og(QO~O!U(SO~Og(TOz(OO!T%dO!U%cO~P%SOk0eOl0fOm0dOn0dOo0mOp0nOgqa!Tqa!Uqa%{qa!Wqa![qa!Qqa#[qatqa!mqa~PEcOV'|Oo0cOv0qO!W&Sa~P'vOz(WO!W&Sa~O!W(XO~Oz(WO!T%dO!U%cO!W&Sa~P%SOV(]Oo0cOv0qO![%}a#g%}a%[%}a%_%}ag%}a{%}a!m%}a%z%}a~P'vOz(^O![%}a#g%}a%[%}a%_%}ag%}a{%}a!m%}a%z%}a~O![(aO~Oz(^O!T%dO!U%cO![%}a~P%SOz(dO!T%dO!U%cO![&Ta~P%SOz(gO{&lX![&lX!m&lX%z&lX~O{(kO![(mO!m(nO%z(jO~OV&OOopOvqO{%hi!x%hi#g%hi%[%hi%_%hi%z%hi~P'vOz(pO{%hi!x%hi#g%hi%[%hi%_%hi%z%hi~O!f&UOh&sa%[&saz&sa#[&sa#g&sa%_&sa#Z&sag&sa~O%[(uO~OV#sOa#tO%uWO~Oz&]O{wa~OopOvqO~P'vOz(^O#g%}a%[%}a%_%}ag%}a{%}a![%}a!m%}a%z%}a~P%SOz(zO#g%hX%[%hX%_%hX%z%hX~O%z#|O#gUi%[Ui%_Ui~O#g&Pa%[&Pa%_&Pan&Pa~P'vOz(}O#g&Pa%[&Pa%_&Pan&Pa~O%uWO#g&ra%[&ra%_&rag&ra~Oz)SO#g&ra%[&ra%_&rag&ra~Og)VO~OV)WOh$WO%uWO~O#Z)XO~O%uWO#g&ta%[&ta%_&ta~Oz)ZO#g&ta%[&ta%_&ta~Oo0cOv0qO#g&pa%[&pa%_&pa{&pa~P'vOz)^O#g&pa%[&pa%_&pa{&pa~OV)`Oa)`O%uWO~O%z)eO~Ot)hO#j)gOP#hiV#hif#hih#hio#his#hiv#hi!P#hi!Q#hi!T#hi!U#hi!X#hi!]#hi!h#hi!r#hi!s#hi!t#hi!{#hi!}#hi#P#hi#R#hi#T#hi#X#hi#Z#hi#^#hi#_#hi#a#hi#c#hi#l#hi#o#hi#s#hi#u#hi#z#hi#}#hi$P#hi%X#hi%o#hi%p#hi%t#hi%u#hi&Z#hi&[#hi&]#hi&^#hi&_#hi&`#hi&a#hi&b#hi&c#hi&d#hi&e#hi&f#hi&g#hi&h#hi&i#hi&j#hi%Z#hi%_#hi~Ot)iOP#kiV#kif#kih#kio#kis#kiv#ki!P#ki!Q#ki!T#ki!U#ki!X#ki!]#ki!h#ki!r#ki!s#ki!t#ki!{#ki!}#ki#P#ki#R#ki#T#ki#X#ki#Z#ki#^#ki#_#ki#a#ki#c#ki#l#ki#o#ki#s#ki#u#ki#z#ki#}#ki$P#ki%X#ki%o#ki%p#ki%t#ki%u#ki&Z#ki&[#ki&]#ki&^#ki&_#ki&`#ki&a#ki&b#ki&c#ki&d#ki&e#ki&f#ki&g#ki&h#ki&i#ki&j#ki%Z#ki%_#ki~OV)kOn&wa~P'vOz)lOn&wa~Oz)lOn&wa~P%SOn)pO~O%Y)tO~Ot)wO#p'WO#q)vOP#niV#nif#nih#nio#nis#niv#ni!P#ni!Q#ni!T#ni!U#ni!X#ni!]#ni!h#ni!r#ni!s#ni!t#ni!{#ni!}#ni#P#ni#R#ni#T#ni#X#ni#Z#ni#^#ni#_#ni#a#ni#c#ni#l#ni#o#ni#s#ni#u#ni#z#ni#}#ni$P#ni%X#ni%o#ni%p#ni%t#ni%u#ni&Z#ni&[#ni&]#ni&^#ni&_#ni&`#ni&a#ni&b#ni&c#ni&d#ni&e#ni&f#ni&g#ni&h#ni&i#ni&j#ni%Z#ni%_#ni~OV)zOo0cOv0qO{$jO~P'vOo0cOv0qO{&xa~P'vOz*OO{&xa~OV*SOa*TOg*WO%q*UO%uWO~O{$jO&{*YO~Oh'_O~Oh!iO{$jO~O%[*_O~O%[*aO%_*aO~OV$}Oa$}Oo0cOv0qOg&Ua~P'vOz*dOg&Ua~Oo0cOv0qO{*gO!W&Xa~P'vOz*hO!W&Xa~Oo0cOv0qOz*hO{*kO!W&Xa~P'vOo0cOv0qOz*hO!W&Xa~P'vOz*hO{*kO!W&Xa~Om0dOn0dOo0mOp0nOgjikjisjizji!Tji!Uji%{ji!Wji{ji![ji#gji%[ji%_ji!Qji#[jitji!mji%zji~Ol0fO~P!NkOlji~P!NkOV'|Og*pOo0cOv0qO~P'vOn*rO~Og*pOz*tO~Og*uO~OV'|Oo0cOv0qO!W&Si~P'vOz*vO!W&Si~O!W*wO~OV(]Oo0cOv0qO![%}i#g%}i%[%}i%_%}ig%}i{%}i!m%}i%z%}i~P'vOz*zO!T%dO!U%cO![&Ti~Oz*}O![%}i#g%}i%[%}i%_%}ig%}i{%}i!m%}i%z%}i~O![+OO~Oa+QOo0cOv0qO![&Ti~P'vOz*zO![&Ti~O![+SO~OV+UOo0cOv0qO{&la![&la!m&la%z&la~P'vOz+VO{&la![&la!m&la%z&la~O!]+YO&n+[O![!nX~O![+^O~O{(kO![+_O~O{(kO![+_O!m+`O~OV&OOopOvqO{%hq!x%hq#g%hq%[%hq%_%hq%z%hq~P'vOz$ri{$ri!x$ri#g$ri%[$ri%_$ri%z$ri~P%SOV&OOopOvqO~P'vOV&OOo0cOv0qO#g%ha%[%ha%_%ha%z%ha~P'vOz+aO#g%ha%[%ha%_%ha%z%ha~Oz$ia#g$ia%[$ia%_$ian$ia~P%SO#g&Pi%[&Pi%_&Pin&Pi~P'vOz+dO#g#Wq%[#Wq%_#Wq~O#[+eOz$va#g$va%[$va%_$vag$va~O%uWO#g&ri%[&ri%_&rig&ri~Oz+gO#g&ri%[&ri%_&rig&ri~OV+iOh$WO%uWO~O%uWO#g&ti%[&ti%_&ti~Oo0cOv0qO#g&pi%[&pi%_&pi{&pi~P'vO{#{Oz#eX!W#eX~Oz+mO!W&uX~O!W+oO~Ot+rO#j)gOP#hqV#hqf#hqh#hqo#hqs#hqv#hq!P#hq!Q#hq!T#hq!U#hq!X#hq!]#hq!h#hq!r#hq!s#hq!t#hq!{#hq!}#hq#P#hq#R#hq#T#hq#X#hq#Z#hq#^#hq#_#hq#a#hq#c#hq#l#hq#o#hq#s#hq#u#hq#z#hq#}#hq$P#hq%X#hq%o#hq%p#hq%t#hq%u#hq&Z#hq&[#hq&]#hq&^#hq&_#hq&`#hq&a#hq&b#hq&c#hq&d#hq&e#hq&f#hq&g#hq&h#hq&i#hq&j#hq%Z#hq%_#hq~On$|az$|a~P%SOV)kOn&wi~P'vOz+yOn&wi~Oz,TO{$jO#[,TO~O#q,VOP#nqV#nqf#nqh#nqo#nqs#nqv#nq!P#nq!Q#nq!T#nq!U#nq!X#nq!]#nq!h#nq!r#nq!s#nq!t#nq!{#nq!}#nq#P#nq#R#nq#T#nq#X#nq#Z#nq#^#nq#_#nq#a#nq#c#nq#l#nq#o#nq#s#nq#u#nq#z#nq#}#nq$P#nq%X#nq%o#nq%p#nq%t#nq%u#nq&Z#nq&[#nq&]#nq&^#nq&_#nq&`#nq&a#nq&b#nq&c#nq&d#nq&e#nq&f#nq&g#nq&h#nq&i#nq&j#nq%Z#nq%_#nq~O#[,WOz%Oa{%Oa~Oo0cOv0qO{&xi~P'vOz,YO{&xi~O{#{O%z,[Og&zXz&zX~O%uWOg&zXz&zX~Oz,`Og&yX~Og,bO~O%Y,eO~O!T%dO!U%cOg&Viz&Vi~OV$}Oa$}Oo0cOv0qOg&Ui~P'vO{,hOz$la!W$la~Oo0cOv0qO{,iOz$la!W$la~P'vOo0cOv0qO{*gO!W&Xi~P'vOz,lO!W&Xi~Oo0cOv0qOz,lO!W&Xi~P'vOz,lO{,oO!W&Xi~Og$hiz$hi!W$hi~P%SOV'|Oo0cOv0qO~P'vOn,qO~OV'|Og,rOo0cOv0qO~P'vOV'|Oo0cOv0qO!W&Sq~P'vOz$gi![$gi#g$gi%[$gi%_$gig$gi{$gi!m$gi%z$gi~P%SOV(]Oo0cOv0qO~P'vOa+QOo0cOv0qO![&Tq~P'vOz,sO![&Tq~O![,tO~OV(]Oo0cOv0qO![%}q#g%}q%[%}q%_%}qg%}q{%}q!m%}q%z%}q~P'vO{,uO~OV+UOo0cOv0qO{&li![&li!m&li%z&li~P'vOz,zO{&li![&li!m&li%z&li~O!]+YO&n+[O![!na~O{(kO![,}O~OV&OOo0cOv0qO#g%hi%[%hi%_%hi%z%hi~P'vOz-OO#g%hi%[%hi%_%hi%z%hi~O%uWO#g&rq%[&rq%_&rqg&rq~Oz-RO#g&rq%[&rq%_&rqg&rq~OV)`Oa)`O%uWO!W&ua~Oz-TO!W&ua~On$|iz$|i~P%SOV)kO~P'vOV)kOn&wq~P'vOt-XOP#myV#myf#myh#myo#mys#myv#my!P#my!Q#my!T#my!U#my!X#my!]#my!h#my!r#my!s#my!t#my!{#my!}#my#P#my#R#my#T#my#X#my#Z#my#^#my#_#my#a#my#c#my#l#my#o#my#s#my#u#my#z#my#}#my$P#my%X#my%o#my%p#my%t#my%u#my&Z#my&[#my&]#my&^#my&_#my&`#my&a#my&b#my&c#my&d#my&e#my&f#my&g#my&h#my&i#my&j#my%Z#my%_#my~O%Z-]O%_-]O~P`O#q-^OP#nyV#nyf#nyh#nyo#nys#nyv#ny!P#ny!Q#ny!T#ny!U#ny!X#ny!]#ny!h#ny!r#ny!s#ny!t#ny!{#ny!}#ny#P#ny#R#ny#T#ny#X#ny#Z#ny#^#ny#_#ny#a#ny#c#ny#l#ny#o#ny#s#ny#u#ny#z#ny#}#ny$P#ny%X#ny%o#ny%p#ny%t#ny%u#ny&Z#ny&[#ny&]#ny&^#ny&_#ny&`#ny&a#ny&b#ny&c#ny&d#ny&e#ny&f#ny&g#ny&h#ny&i#ny&j#ny%Z#ny%_#ny~Oz-aO{$jO#[-aO~Oo0cOv0qO{&xq~P'vOz-dO{&xq~O%z,[Og&zaz&za~O{#{Og&zaz&za~OV*SOa*TO%q*UO%uWOg&ya~Oz-hOg&ya~O$S-lO~OV$}Oa$}Oo0cOv0qO~P'vOo0cOv0qO{-mOz$li!W$li~P'vOo0cOv0qOz$li!W$li~P'vO{-mOz$li!W$li~Oo0cOv0qO{*gO~P'vOo0cOv0qO{*gO!W&Xq~P'vOz-pO!W&Xq~Oo0cOv0qOz-pO!W&Xq~P'vOs-sO!T%dO!U%cOg&Oq!W&Oq![&Oqz&Oq~P!/jOa+QOo0cOv0qO![&Ty~P'vOz$ji![$ji~P%SOa+QOo0cOv0qO~P'vOV+UOo0cOv0qO~P'vOV+UOo0cOv0qO{&lq![&lq!m&lq%z&lq~P'vO{(kO![-xO!m-yO%z-wO~OV&OOo0cOv0qO#g%hq%[%hq%_%hq%z%hq~P'vO%uWO#g&ry%[&ry%_&ryg&ry~OV)`Oa)`O%uWO!W&ui~Ot-}OP#m!RV#m!Rf#m!Rh#m!Ro#m!Rs#m!Rv#m!R!P#m!R!Q#m!R!T#m!R!U#m!R!X#m!R!]#m!R!h#m!R!r#m!R!s#m!R!t#m!R!{#m!R!}#m!R#P#m!R#R#m!R#T#m!R#X#m!R#Z#m!R#^#m!R#_#m!R#a#m!R#c#m!R#l#m!R#o#m!R#s#m!R#u#m!R#z#m!R#}#m!R$P#m!R%X#m!R%o#m!R%p#m!R%t#m!R%u#m!R&Z#m!R&[#m!R&]#m!R&^#m!R&_#m!R&`#m!R&a#m!R&b#m!R&c#m!R&d#m!R&e#m!R&f#m!R&g#m!R&h#m!R&i#m!R&j#m!R%Z#m!R%_#m!R~Oo0cOv0qO{&xy~P'vOV*SOa*TO%q*UO%uWOg&yi~O$S-lO%Z.VO%_.VO~OV.aOh._O!X.^O!].`O!h.YO!s.[O!t.[O%p.XO%uWO&Z]O&[]O&]]O&^]O&_]O&`]O&a]O&b]O~Oo0cOv0qOz$lq!W$lq~P'vO{.fOz$lq!W$lq~Oo0cOv0qO{*gO!W&Xy~P'vOz.gO!W&Xy~Oo0cOv.kO~P'vOs-sO!T%dO!U%cOg&Oy!W&Oy![&Oyz&Oy~P!/jO{(kO![.nO~O{(kO![.nO!m.oO~OV*SOa*TO%q*UO%uWO~Oh.tO!f.rOz$TX#[$TX%j$TXg$TX~Os$TX{$TX!W$TX![$TX~P$-bO%o.vO%p.vOs$UXz$UX{$UX#[$UX%j$UX!W$UXg$UX![$UX~O!h.xO~Oz.|O#[/OO%j.yOs&|X{&|X!W&|Xg&|X~Oa/RO~P$)zOh.tOs&}Xz&}X{&}X#[&}X%j&}X!W&}Xg&}X![&}X~Os/VO{$jO~Oo0cOv0qOz$ly!W$ly~P'vOo0cOv0qO{*gO!W&X!R~P'vOz/ZO!W&X!R~Og&RXs&RX!T&RX!U&RX!W&RX![&RXz&RX~P!/jOs-sO!T%dO!U%cOg&Qa!W&Qa![&Qaz&Qa~O{(kO![/^O~O!f.rOh$[as$[az$[a{$[a#[$[a%j$[a!W$[ag$[a![$[a~O!h/eO~O%o.vO%p.vOs$Uaz$Ua{$Ua#[$Ua%j$Ua!W$Uag$Ua![$Ua~O%j.yOs$Yaz$Ya{$Ya#[$Ya!W$Yag$Ya![$Ya~Os&|a{&|a!W&|ag&|a~P$)nOz/jOs&|a{&|a!W&|ag&|a~O!W/mO~Og/mO~O{/oO~O![/pO~Oo0cOv0qO{*gO!W&X!Z~P'vO{/sO~O%z/tO~P$-bOz/uO#[/OO%j.yOg'PX~Oz/uOg'PX~Og/wO~O!h/xO~O#[/OOs%Saz%Sa{%Sa%j%Sa!W%Sag%Sa![%Sa~O#[/OO%j.yOs%Waz%Wa{%Wa!W%Wag%Wa~Os&|i{&|i!W&|ig&|i~P$)nOz/zO#[/OO%j.yO!['Oa~Og'Pa~P$)nOz0SOg'Pa~Oa0UO!['Oi~P$)zOz0WO!['Oi~Oz0WO#[/OO%j.yO!['Oi~O#[/OO%j.yOg$biz$bi~O%z0ZO~P$-bO#[/OO%j.yOg%Vaz%Va~Og'Pi~P$)nO{0^O~Oa0UO!['Oq~P$)zOz0`O!['Oq~O#[/OO%j.yOz%Ui![%Ui~Oa0UO~P$)zOa0UO!['Oy~P$)zO#[/OO%j.yOg$ciz$ci~O#[/OO%j.yOz%Uq![%Uq~Oz+aO#g%ha%[%ha%_%ha%z%ha~P%SOV&OOo0cOv0qO~P'vOn0hO~Oo0hO~P'vO{0iO~Ot0jO~P!/jO&]&Z&j&h&i&g&f&d&e&c&b&`&a&_&^&[%u~",goto:"!=j'QPPPPPP'RP'Z*s+[+t,_,y-fP.SP'Z.r.r'ZPPP'Z2[PPPPPP2[5PPP5PP7b7k=sPP=v>h>kPP'Z'ZPP>zPP'Z'ZPP'Z'Z'Z'Z'Z?O?w'ZP?zP@QDXGuGyPG|HWH['ZPPPH_Hk'RP'R'RP'RP'RP'RP'RP'R'R'RP'RPP'RPP'RP'RPHqH}IVPI^IdPI^PI^I^PPPI^PKrPK{LVL]KrPI^LfPI^PLmLsPLwM]MzNeLwLwNkNxLwLwLwLw! ^! d! g! l! o! y!!P!!]!!o!!u!#P!#V!#s!#y!$P!$Z!$a!$g!$y!%T!%Z!%a!%k!%q!%w!%}!&T!&Z!&e!&k!&u!&{!'U!'[!'k!'s!'}!(UPPPPPPPPPPP!([!(_!(e!(n!(x!)TPPPPPPPPPPPP!-u!/Z!3^!6oPP!6w!7W!7a!8Y!8P!8c!8i!8l!8o!8r!8z!9jPPPPPPPPPPPPPPPPP!9m!9q!9wP!:]!:a!:m!:v!;S!;j!;m!;p!;v!;|!_![!]Do!]!^Es!^!_FZ!_!`Gk!`!aHX!a!b%T!b!cIf!c!dJU!d!eK^!e!hJU!h!i!#f!i!tJU!t!u!,|!u!wJU!w!x!.t!x!}JU!}#O!0S#O#P&o#P#Q!0j#Q#R!1Q#R#SJU#S#T%T#T#UJU#U#VK^#V#YJU#Y#Z!#f#Z#fJU#f#g!,|#g#iJU#i#j!.t#j#oJU#o#p!1n#p#q!1s#q#r!2a#r#s!2f#s$g%T$g;'SJU;'S;=`KW<%lOJU`%YT&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%T`%lP;=`<%l%To%v]&n`%c_OX%TXY%oY[%T[]%o]p%Tpq%oq#O%T#O#P&o#P#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%To&tX&n`OY%TYZ%oZ]%T]^%o^#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tc'f[&n`O!_%T!_!`([!`#T%T#T#U(r#U#f%T#f#g(r#g#h(r#h#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tc(cTmR&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tc(yT!mR&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk)aV&n`&[ZOr%Trs)vs#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk){V&n`Or%Trs*bs#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk*iT&n`&^ZO#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%To+PZS_&n`OY*xYZ%TZ]*x]^%T^#o*x#o#p+r#p#q*x#q#r+r#r;'S*x;'S;=`,^<%lO*x_+wTS_OY+rZ]+r^;'S+r;'S;=`,W<%lO+r_,ZP;=`<%l+ro,aP;=`<%l*xj,kV%rQ&n`O!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tj-XT!xY&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tj-oV%lQ&n`O!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk.]V&n`&ZZOw%Twx.rx#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk.wV&n`Ow%Twx/^x#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk/eT&n`&]ZO#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk/{ThZ&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tc0cTgR&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk0yXVZ&n`Oz%Tz{1f{!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk1mVaR&n`O!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk2ZV%oZ&n`O!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tc2wTzR&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%To3_W%pZ&n`O!_%T!_!`-Q!`!a3w!a#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Td4OT&{S&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk4fX!fQ&n`O!O%T!O!P5R!P!Q%T!Q![6T![#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk5WV&n`O!O%T!O!P5m!P#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk5tT!rZ&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Ti6[a!hX&n`O!Q%T!Q![6T![!g%T!g!h7a!h!l%T!l!m9s!m#R%T#R#S6T#S#X%T#X#Y7a#Y#^%T#^#_9s#_#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Ti7fZ&n`O{%T{|8X|}%T}!O8X!O!Q%T!Q![8s![#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Ti8^V&n`O!Q%T!Q![8s![#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Ti8z]!hX&n`O!Q%T!Q![8s![!l%T!l!m9s!m#R%T#R#S8s#S#^%T#^#_9s#_#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Ti9zT!hX&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk:bX%qR&n`O!P%T!P!Q:}!Q!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tj;UV%sQ&n`O!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Ti;ro!hX&n`O!O%T!O!P=s!P!Q%T!Q![>_![!d%T!d!e?q!e!g%T!g!h7a!h!l%T!l!m9s!m!q%T!q!rA]!r!z%T!z!{Bq!{#R%T#R#S>_#S#U%T#U#V?q#V#X%T#X#Y7a#Y#^%T#^#_9s#_#c%T#c#dA]#d#l%T#l#mBq#m#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Ti=xV&n`O!Q%T!Q![6T![#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Ti>fc!hX&n`O!O%T!O!P=s!P!Q%T!Q![>_![!g%T!g!h7a!h!l%T!l!m9s!m#R%T#R#S>_#S#X%T#X#Y7a#Y#^%T#^#_9s#_#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Ti?vY&n`O!Q%T!Q!R@f!R!S@f!S#R%T#R#S@f#S#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Ti@mY!hX&n`O!Q%T!Q!R@f!R!S@f!S#R%T#R#S@f#S#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TiAbX&n`O!Q%T!Q!YA}!Y#R%T#R#SA}#S#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TiBUX!hX&n`O!Q%T!Q!YA}!Y#R%T#R#SA}#S#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TiBv]&n`O!Q%T!Q![Co![!c%T!c!iCo!i#R%T#R#SCo#S#T%T#T#ZCo#Z#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TiCv]!hX&n`O!Q%T!Q![Co![!c%T!c!iCo!i#R%T#R#SCo#S#T%T#T#ZCo#Z#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%ToDvV{_&n`O!_%T!_!`E]!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TcEdT%{R&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TkEzT#gZ&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TkFbXmR&n`O!^%T!^!_F}!_!`([!`!a([!a#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TjGUV%mQ&n`O!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TkGrV%zZ&n`O!_%T!_!`([!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TkH`WmR&n`O!_%T!_!`([!`!aHx!a#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TjIPV%nQ&n`O!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TkIoV_Q#}P&n`O!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%ToJ_]&n`&YS%uZO!Q%T!Q![JU![!c%T!c!}JU!}#R%T#R#SJU#S#T%T#T#oJU#p#q%T#r$g%T$g;'SJU;'S;=`KW<%lOJUoKZP;=`<%lJUoKge&n`&YS%uZOr%Trs)Ysw%Twx.Ux!Q%T!Q![JU![!c%T!c!tJU!t!uLx!u!}JU!}#R%T#R#SJU#S#T%T#T#fJU#f#gLx#g#oJU#p#q%T#r$g%T$g;'SJU;'S;=`KW<%lOJUoMRa&n`&YS%uZOr%TrsNWsw%Twx! vx!Q%T!Q![JU![!c%T!c!}JU!}#R%T#R#SJU#S#T%T#T#oJU#p#q%T#r$g%T$g;'SJU;'S;=`KW<%lOJUkN_V&n`&`ZOr%TrsNts#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TkNyV&n`Or%Trs! `s#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk! gT&n`&bZO#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk! }V&n`&_ZOw%Twx!!dx#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!!iV&n`Ow%Twx!#Ox#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!#VT&n`&aZO#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%To!#oe&n`&YS%uZOr%Trs!%Qsw%Twx!&px!Q%T!Q![JU![!c%T!c!tJU!t!u!(`!u!}JU!}#R%T#R#SJU#S#T%T#T#fJU#f#g!(`#g#oJU#p#q%T#r$g%T$g;'SJU;'S;=`KW<%lOJUk!%XV&n`&dZOr%Trs!%ns#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!%sV&n`Or%Trs!&Ys#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!&aT&n`&fZO#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!&wV&n`&cZOw%Twx!'^x#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!'cV&n`Ow%Twx!'xx#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!(PT&n`&eZO#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%To!(ia&n`&YS%uZOr%Trs!)nsw%Twx!+^x!Q%T!Q![JU![!c%T!c!}JU!}#R%T#R#SJU#S#T%T#T#oJU#p#q%T#r$g%T$g;'SJU;'S;=`KW<%lOJUk!)uV&n`&hZOr%Trs!*[s#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!*aV&n`Or%Trs!*vs#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!*}T&n`&jZO#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!+eV&n`&gZOw%Twx!+zx#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!,PV&n`Ow%Twx!,fx#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!,mT&n`&iZO#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%To!-Vi&n`&YS%uZOr%TrsNWsw%Twx! vx!Q%T!Q![JU![!c%T!c!dJU!d!eLx!e!hJU!h!i!(`!i!}JU!}#R%T#R#SJU#S#T%T#T#UJU#U#VLx#V#YJU#Y#Z!(`#Z#oJU#p#q%T#r$g%T$g;'SJU;'S;=`KW<%lOJUo!.}a&n`&YS%uZOr%Trs)Ysw%Twx.Ux!Q%T!Q![JU![!c%T!c!}JU!}#R%T#R#SJU#S#T%T#T#oJU#p#q%T#r$g%T$g;'SJU;'S;=`KW<%lOJUk!0ZT!XZ&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tc!0qT!WR&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tj!1XV%kQ&n`O!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%T~!1sO!]~k!1zV%jR&n`O!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%T~!2fO![~i!2mT%tX&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%T",tokenizers:[nt,Qe,Ze,rt,0,1,2,3,4],topRules:{Script:[0,5]},specialized:[{term:221,get:e=>ot[e]||-1}],tokenPrec:7668}),G=new t,K=new Set([`Script`,`Body`,`FunctionDefinition`,`ClassDefinition`,`LambdaExpression`,`ForStatement`,`MatchClause`]);function q(e){return(t,n,r)=>{if(r)return!1;let i=t.node.getChild(`VariableName`);return i&&n(i,e),!0}}var ct={FunctionDefinition:q(`function`),ClassDefinition:q(`class`),ForStatement(e,t,n){if(n){for(let n=e.node.firstChild;n;n=n.nextSibling)if(n.name==`VariableName`)t(n,`variable`);else if(n.name==`in`)break}},ImportStatement(e,t){let{node:n}=e,r=n.firstChild?.name==`from`;for(let e=n.getChild(`import`);e;e=e.nextSibling)e.name==`VariableName`&&e.nextSibling?.name!=`as`&&t(e,r?`variable`:`namespace`)},AssignStatement(e,t){for(let n=e.node.firstChild;n;n=n.nextSibling)if(n.name==`VariableName`)t(n,`variable`);else if(n.name==`:`||n.name==`AssignOp`)break},ParamList(e,t){for(let n=null,r=e.node.firstChild;r;r=r.nextSibling)r.name==`VariableName`&&(!n||!/\*|AssignOp/.test(n.name))&&t(r,`variable`),n=r},CapturePattern:q(`variable`),AsPattern:q(`variable`),__proto__:null};function J(t,n){let r=G.get(n);if(r)return r;let i=[],a=!0;function o(e,n){let r=t.sliceString(e.from,e.to);i.push({label:r,type:n})}return n.cursor(e.IncludeAnonymous).iterate(e=>{if(e.name){let t=ct[e.name];if(t&&t(e,o,a)||!a&&K.has(e.name))return!1;a=!1}else if(e.to-e.from>8192){for(let n of J(t,e.node))i.push(n);return!1}}),G.set(n,i),i}var Y=/^[\w\xa1-\uffff][\w\d\xa1-\uffff]*$/,X=[`String`,`FormatString`,`Comment`,`PropertyName`];function lt(e){let t=te(e.state).resolveInner(e.pos,-1);if(X.indexOf(t.name)>-1)return null;let n=t.name==`VariableName`||t.to-t.from<20&&Y.test(e.state.sliceDoc(t.from,t.to));if(!n&&!e.explicit)return null;let r=[];for(let n=t;n;n=n.parent)K.has(n.name)&&(r=r.concat(J(e.state.doc,n)));return{options:r,from:n?t.from:e.pos,validFor:Y}}var ut=[`__annotations__`,`__builtins__`,`__debug__`,`__doc__`,`__import__`,`__name__`,`__loader__`,`__package__`,`__spec__`,`False`,`None`,`True`].map(e=>({label:e,type:`constant`})).concat(`ArithmeticError.AssertionError.AttributeError.BaseException.BlockingIOError.BrokenPipeError.BufferError.BytesWarning.ChildProcessError.ConnectionAbortedError.ConnectionError.ConnectionRefusedError.ConnectionResetError.DeprecationWarning.EOFError.Ellipsis.EncodingWarning.EnvironmentError.Exception.FileExistsError.FileNotFoundError.FloatingPointError.FutureWarning.GeneratorExit.IOError.ImportError.ImportWarning.IndentationError.IndexError.InterruptedError.IsADirectoryError.KeyError.KeyboardInterrupt.LookupError.MemoryError.ModuleNotFoundError.NameError.NotADirectoryError.NotImplemented.NotImplementedError.OSError.OverflowError.PendingDeprecationWarning.PermissionError.ProcessLookupError.RecursionError.ReferenceError.ResourceWarning.RuntimeError.RuntimeWarning.StopAsyncIteration.StopIteration.SyntaxError.SyntaxWarning.SystemError.SystemExit.TabError.TimeoutError.TypeError.UnboundLocalError.UnicodeDecodeError.UnicodeEncodeError.UnicodeError.UnicodeTranslateError.UnicodeWarning.UserWarning.ValueError.Warning.ZeroDivisionError`.split(`.`).map(e=>({label:e,type:`type`}))).concat([`bool`,`bytearray`,`bytes`,`classmethod`,`complex`,`float`,`frozenset`,`int`,`list`,`map`,`memoryview`,`object`,`range`,`set`,`staticmethod`,`str`,`super`,`tuple`,`type`].map(e=>({label:e,type:`class`}))).concat(`abs.aiter.all.anext.any.ascii.bin.breakpoint.callable.chr.compile.delattr.dict.dir.divmod.enumerate.eval.exec.exit.filter.format.getattr.globals.hasattr.hash.help.hex.id.input.isinstance.issubclass.iter.len.license.locals.max.min.next.oct.open.ord.pow.print.property.quit.repr.reversed.round.setattr.slice.sorted.sum.vars.zip`.split(`.`).map(e=>({label:e,type:`function`}))),dt=[u("def ${name}(${params}):\n ${}",{label:`def`,detail:`function`,type:`keyword`}),u("for ${name} in ${collection}:\n ${}",{label:`for`,detail:`loop`,type:`keyword`}),u("while ${}:\n ${}",{label:`while`,detail:`loop`,type:`keyword`}),u(`try: +import{C as e,D as t,b as n,d as r,f as i,i as a,l as o,m as s,t as ee,v as te,x as c}from"./index-DPVcGf1z.js";import{n as l,r as ne,t as re}from"./dist-T_roqrwM.js";import{i as u,n as ie,r as ae}from"./dist-bWbpkuNe.js";var oe=1,d=194,f=195,se=196,p=197,ce=198,le=199,ue=200,de=2,m=3,h=201,g=24,fe=25,pe=49,me=50,he=55,ge=56,_e=57,ve=59,ye=60,be=61,xe=62,Se=63,Ce=65,we=238,Te=71,Ee=241,De=242,Oe=243,ke=244,Ae=245,_=246,v=247,y=248,b=72,x=249,S=250,C=251,je=252,Me=253,Ne=254,Pe=255,Fe=256,Ie=73,Le=77,Re=263,ze=112,Be=130,Ve=151,He=152,Ue=155,w=10,T=13,E=32,D=9,O=35,We=40,Ge=46,k=123,A=125,j=39,M=34,N=92,Ke=111,P=120,qe=78,Je=117,Ye=85,Xe=new Set([fe,pe,me,Re,Ce,Be,ge,_e,we,xe,Se,b,Ie,Le,ye,be,Ve,He,Ue,ze]);function F(e){return e==w||e==T}function I(e){return e>=48&&e<=57||e>=65&&e<=70||e>=97&&e<=102}var Ze=new l((e,t)=>{let n;if(e.next<0)e.acceptToken(le);else if(t.context.flags&L)F(e.next)&&e.acceptToken(ce,1);else if(((n=e.peek(-1))<0||F(n))&&t.canShift(p)){let t=0;for(;e.next==E||e.next==D;)e.advance(),t++;(e.next==w||e.next==T||e.next==O)&&e.acceptToken(p,-t)}else F(e.next)&&e.acceptToken(se,1)},{contextual:!0}),Qe=new l((e,t)=>{let n=t.context;if(n.flags)return;let r=e.peek(-1);if(r==w||r==T){let t=0,r=0;for(;;){if(e.next==E)t++;else if(e.next==D)t+=8-t%8;else break;e.advance(),r++}t!=n.indent&&e.next!=w&&e.next!=T&&e.next!=O&&(t[e,t|R])),tt=new re({start:$e,reduce(e,t,n,r){return e.flags&L&&Xe.has(t)||(t==Te||t==b)&&e.flags&R?e.parent:e},shift(e,t,n,r){return t==d?new U(e,et(r.read(r.pos,n.pos)),0):t==f?e.parent:t==g||t==he||t==ve||t==m?new U(e,0,L):W.has(t)?new U(e,0,W.get(t)|e.flags&L):e},hash(e){return e.hash}}),nt=new l(e=>{for(let t=0;t<5;t++){if(e.next!=`print`.charCodeAt(t))return;e.advance()}if(!/\w/.test(String.fromCharCode(e.next)))for(let t=0;;t++){let n=e.peek(t);if(!(n==E||n==D)){n!=We&&n!=Ge&&n!=w&&n!=T&&n!=O&&e.acceptToken(oe);return}}}),rt=new l((e,t)=>{let{flags:n}=t.context,r=n&z?M:j,i=(n&B)>0,a=!(n&V),o=(n&H)>0,s=e.pos;for(;!(e.next<0);)if(o&&e.next==k)if(e.peek(1)==k)e.advance(2);else{if(e.pos==s){e.acceptToken(m,1);return}break}else if(a&&e.next==N){if(e.pos==s){e.advance();let t=e.next;t>=0&&(e.advance(),it(e,t)),e.acceptToken(de);return}break}else if(e.next==N&&!a&&e.peek(1)>-1)e.advance(2);else if(e.next==r&&(!i||e.peek(1)==r&&e.peek(2)==r)){if(e.pos==s){e.acceptToken(h,i?3:1);return}break}else if(e.next==w){if(i)e.advance();else if(e.pos==s){e.acceptToken(h);return}break}else e.advance();e.pos>s&&e.acceptToken(ue)});function it(e,t){if(t==Ke)for(let t=0;t<2&&e.next>=48&&e.next<=55;t++)e.advance();else if(t==P)for(let t=0;t<2&&I(e.next);t++)e.advance();else if(t==Je)for(let t=0;t<4&&I(e.next);t++)e.advance();else if(t==Ye)for(let t=0;t<8&&I(e.next);t++)e.advance();else if(t==qe&&e.next==k){for(e.advance();e.next>=0&&e.next!=A&&e.next!=j&&e.next!=M&&e.next!=w;)e.advance();e.next==A&&e.advance()}}var at=n({'async "*" "**" FormatConversion FormatSpec':c.modifier,"for while if elif else try except finally return raise break continue with pass assert await yield match case":c.controlKeyword,"in not and or is del":c.operatorKeyword,"from def class global nonlocal lambda":c.definitionKeyword,import:c.moduleKeyword,"with as print":c.keyword,Boolean:c.bool,None:c.null,VariableName:c.variableName,"CallExpression/VariableName":c.function(c.variableName),"FunctionDefinition/VariableName":c.function(c.definition(c.variableName)),"ClassDefinition/VariableName":c.definition(c.className),PropertyName:c.propertyName,"CallExpression/MemberExpression/PropertyName":c.function(c.propertyName),Comment:c.lineComment,Number:c.number,String:c.string,FormatString:c.special(c.string),Escape:c.escape,UpdateOp:c.updateOperator,"ArithOp!":c.arithmeticOperator,BitOp:c.bitwiseOperator,CompareOp:c.compareOperator,AssignOp:c.definitionOperator,Ellipsis:c.punctuation,At:c.meta,"( )":c.paren,"[ ]":c.squareBracket,"{ }":c.brace,".":c.derefOperator,", ;":c.separator}),ot={__proto__:null,await:44,or:54,and:56,in:60,not:62,is:64,if:70,else:72,lambda:76,yield:94,from:96,async:102,for:104,None:162,True:164,False:164,del:178,pass:182,break:186,continue:190,return:194,raise:202,import:206,as:208,global:212,nonlocal:214,assert:218,type:223,elif:236,while:240,try:246,except:248,finally:250,with:254,def:258,class:268,match:279,case:285},st=ne.deserialize({version:14,states:"##jQ`QeOOP$}OSOOO&WQtO'#HUOOQS'#Co'#CoOOQS'#Cp'#CpO'vQdO'#CnO*UQtO'#HTOOQS'#HU'#HUOOQS'#DU'#DUOOQS'#HT'#HTO*rQdO'#D_O+VQdO'#DfO+gQdO'#DjO+zOWO'#DuO,VOWO'#DvO.[QtO'#GuOOQS'#Gu'#GuO'vQdO'#GtO0ZQtO'#GtOOQS'#Eb'#EbO0rQdO'#EcOOQS'#Gs'#GsO0|QdO'#GrOOQV'#Gr'#GrO1XQdO'#FYOOQS'#G^'#G^O1^QdO'#FXOOQV'#IS'#ISOOQV'#Gq'#GqOOQV'#Fq'#FqQ`QeOOO'vQdO'#CqO1lQdO'#C}O1sQdO'#DRO2RQdO'#HYO2cQtO'#EVO'vQdO'#EWOOQS'#EY'#EYOOQS'#E['#E[OOQS'#E^'#E^O2wQdO'#E`O3_QdO'#EdO3rQdO'#EfO3zQtO'#EfO1XQdO'#EiO0rQdO'#ElO1XQdO'#EnO0rQdO'#EtO0rQdO'#EwO4VQdO'#EyO4^QdO'#FOO4iQdO'#EzO0rQdO'#FOO1XQdO'#FQO1XQdO'#FVO4nQdO'#F[P4uOdO'#GpPOOO)CBd)CBdOOQS'#Ce'#CeOOQS'#Cf'#CfOOQS'#Cg'#CgOOQS'#Ch'#ChOOQS'#Ci'#CiOOQS'#Cj'#CjOOQS'#Cl'#ClO'vQdO,59OO'vQdO,59OO'vQdO,59OO'vQdO,59OO'vQdO,59OO'vQdO,59OO5TQdO'#DoOOQS,5:Y,5:YO5hQdO'#HdOOQS,5:],5:]O5uQ!fO,5:]O5zQtO,59YO1lQdO,59bO1lQdO,59bO1lQdO,59bO8jQdO,59bO8oQdO,59bO8vQdO,59jO8}QdO'#HTO:TQdO'#HSOOQS'#HS'#HSOOQS'#D['#D[O:lQdO,59aO'vQdO,59aO:zQdO,59aOOQS,59y,59yO;PQdO,5:RO'vQdO,5:ROOQS,5:Q,5:QO;_QdO,5:QO;dQdO,5:XO'vQdO,5:XO'vQdO,5:VOOQS,5:U,5:UO;uQdO,5:UO;zQdO,5:WOOOW'#Fy'#FyOOOOQS'#Ds'#DsOOQS1G/w1G/wOOQS1G.|1G.|O!/[QtO1G.|O!/cQtO1G.|O1lQdO1G.|O!0OQdO1G/UOOQS'#DZ'#DZO0rQdO,59tOOQS1G.{1G.{O!0VQdO1G/eO!0gQdO1G/eO!0oQdO1G/fO'vQdO'#H[O!0tQdO'#H[O!0yQtO1G.{O!1ZQdO,59iO!2aQdO,5=zO!2qQdO,5=zO!2yQdO1G/mO!3OQtO1G/mOOQS1G/l1G/lO!3`QdO,5=uO!4VQdO,5=uO0rQdO1G/qO!4tQdO1G/sO!4yQtO1G/sO!5ZQtO1G/qOOQS1G/p1G/pOOQS1G/r1G/rOOOW-E9w-E9wOOQS1G/{1G/{O!5kQdO'#HxO0rQdO'#HxO!5|QdO,5>cOOOW-E9x-E9xOOQS1G/|1G/|OOQS-E9{-E9{O!6[Q#xO1G2zO!6{QtO1G2zO'vQdO,5kOOQS1G1`1G1`O!8RQdO1G1`OOQS'#DV'#DVO0rQdO,5=qOOQS,5=q,5=qO!8WQdO'#FrO!8cQdO,59oO!8kQdO1G/XO!8uQtO,5=uOOQS1G3`1G3`OOQS,5:m,5:mO!9fQdO'#GtOOQS,5jO!;ZQdO,5>jO1XQdO,5>jO!;lQdO,5>iOOQS-E:R-E:RO!;qQdO1G0lO!;|QdO1G0lO!lO!lO!hO!=VQdO,5>hO!=hQdO'#EpO0rQdO1G0tO!=sQdO1G0tO!=xQgO1G0zO!AvQgO1G0}O!EqQdO,5>oO!E{QdO,5>oO!FTQtO,5>oO0rQdO1G1PO!F_QdO1G1PO4iQdO1G1UO!!vQdO1G1WOOQV,5;a,5;aO!FdQfO,5;aO!FiQgO1G1QO!JjQdO'#GZO4iQdO1G1QO4iQdO1G1QO!JzQdO,5>pO!KXQdO,5>pO1XQdO,5>pOOQV1G1U1G1UO!KaQdO'#FSO!KrQ!fO1G1WO!KzQdO1G1WOOQV1G1]1G1]O4iQdO1G1]O!LPQdO1G1]O!LXQdO'#F^OOQV1G1b1G1bO!#ZQtO1G1bPOOO1G2v1G2vP!L^OSO1G2vOOQS,5=},5=}OOQS'#Dp'#DpO0rQdO,5=}O!LfQdO,5=|O!LyQdO,5=|OOQS1G/u1G/uO!MRQdO,5>PO!McQdO,5>PO!MkQdO,5>PO!NOQdO,5>PO!N`QdO,5>POOQS1G3j1G3jOOQS7+$h7+$hO!8kQdO7+$pO#!RQdO1G.|O#!YQdO1G.|OOQS1G/`1G/`OOQS,5<`,5<`O'vQdO,5<`OOQS7+%P7+%PO#!aQdO7+%POOQS-E9r-E9rOOQS7+%Q7+%QO#!qQdO,5=vO'vQdO,5=vOOQS7+$g7+$gO#!vQdO7+%PO##OQdO7+%QO##TQdO1G3fOOQS7+%X7+%XO##eQdO1G3fO##mQdO7+%XOOQS,5<_,5<_O'vQdO,5<_O##rQdO1G3aOOQS-E9q-E9qO#$iQdO7+%]OOQS7+%_7+%_O#$wQdO1G3aO#%fQdO7+%_O#%kQdO1G3gO#%{QdO1G3gO#&TQdO7+%]O#&YQdO,5>dO#&sQdO,5>dO#&sQdO,5>dOOQS'#Dx'#DxO#'UO&jO'#DzO#'aO`O'#HyOOOW1G3}1G3}O#'fQdO1G3}O#'nQdO1G3}O#'yQ#xO7+(fO#(jQtO1G2UP#)TQdO'#GOOOQS,5nQdO,5sQdO1G4OOOQS-E9y-E9yO#?^QdO1G4OO<[QdO'#H{OOOO'#D{'#D{OOOO'#F|'#F|O#?oO&jO,5:fOOOW,5>e,5>eOOOW7+)i7+)iO#?zQdO7+)iO#@SQdO1G2zO#@mQdO1G2zP'vQdO'#FuO0rQdO<mO#BQQdO,5>mOOQS1G0v1G0vOOQS<rO#KgQdO,5>rO#KrQdO,5>rO#K}QdO,5>qO#L`QdO,5>qOOQS1G1Y1G1YOOQS,5;p,5;pOOQV<VAN>VO$ oQdO<cAN>cO0rQdO1G1|O$!PQtO1G1|P$!ZQdO'#FvOOQS1G2R1G2RP$!hQdO'#F{O$!uQdO7+)jO$#`QdO,5>gOOOO-E9z-E9zOOOW<tO$4{QdO,5>tO1XQdO,5vO$)nQdO,5>vOOQS1G1p1G1pOOQS,5<[,5<[OOQU7+'P7+'PO$+zQdO1G/iO$)nQdO,5wO$8zQdO,5>wOOQS1G1s1G1sOOQS7+'S7+'SP$)nQdO'#GdO$9SQdO1G4bO$9^QdO1G4bO$9fQdO1G4bOOQS7+%T7+%TO$9tQdO1G1tO$:SQtO'#FaO$:ZQdO,5<}OOQS,5<},5<}O$:iQdO1G4cOOQS-E:a-E:aO$)nQdO,5<|O$:pQdO,5<|O$:uQdO7+)|OOQS-E:`-E:`O$;PQdO7+)|O$)nQdO,5S~O%cOS%^OSSOS%]PQ~OPdOVaOfoOhYOopOs!POvqO!PrO!Q{O!T!SO!U!RO!XZO!][O!h`O!r`O!s`O!t`O!{tO!}uO#PvO#RwO#TxO#XyO#ZzO#^|O#_|O#a}O#c!OO#l!QO#o!TO#s!UO#u!VO#z!WO#}hO$P!XO%oRO%pRO%tSO%uWO&Z]O&[]O&]]O&^]O&_]O&`]O&a]O&b]O&c^O&d^O&e^O&f^O&g^O&h^O&i^O&j^O~O%]!YO~OV!aO_!aOa!bOh!iO!X!kO!f!mO%j![O%k!]O%l!^O%m!_O%n!_O%o!`O%p!`O%q!aO%r!aO%s!aO~Ok%xXl%xXm%xXn%xXo%xXp%xXs%xXz%xX{%xX!x%xX#g%xX%[%xX%_%xX%z%xXg%xX!T%xX!U%xX%{%xX!W%xX![%xX!Q%xX#[%xXt%xX!m%xX~P%SOfoOhYO!XZO!][O!h`O!r`O!s`O!t`O%oRO%pRO%tSO%uWO&Z]O&[]O&]]O&^]O&_]O&`]O&a]O&b]O&c^O&d^O&e^O&f^O&g^O&h^O&i^O&j^O~Oz%wX{%wX#g%wX%[%wX%_%wX%z%wX~Ok!pOl!qOm!oOn!oOo!rOp!sOs!tO!x%wX~P)pOV!zOg!|Oo0cOv0qO!PrO~P'vOV#OOo0cOv0qO!W#PO~P'vOV#SOa#TOo0cOv0qO![#UO~P'vOQ#XO%`#XO%a#ZO~OQ#^OR#[O%`#^O%a#`O~OV%iX_%iXa%iXh%iXk%iXl%iXm%iXn%iXo%iXp%iXs%iXz%iX!X%iX!f%iX%j%iX%k%iX%l%iX%m%iX%n%iX%o%iX%p%iX%q%iX%r%iX%s%iXg%iX!T%iX!U%iX~O&Z]O&[]O&]]O&^]O&_]O&`]O&a]O&b]O&c^O&d^O&e^O&f^O&g^O&h^O&i^O&j^O{%iX!x%iX#g%iX%[%iX%_%iX%z%iX%{%iX!W%iX![%iX!Q%iX#[%iXt%iX!m%iX~P,eOz#dO{%hX!x%hX#g%hX%[%hX%_%hX%z%hX~Oo0cOv0qO~P'vO#g#gO%[#iO%_#iO~O%uWO~O!T#nO#u!VO#z!WO#}hO~OopO~P'vOV#sOa#tO%uWO{wP~OV#xOo0cOv0qO!Q#yO~P'vO{#{O!x$QO%z#|O#g!yX%[!yX%_!yX~OV#xOo0cOv0qO#g#SX%[#SX%_#SX~P'vOo0cOv0qO#g#WX%[#WX%_#WX~P'vOh$WO%uWO~O!f$YO!r$YO%uWO~OV$eO~P'vO!U$gO#s$hO#u$iO~O{$jO~OV$qO~P'vOS$sO%[$rO%_$rO%c$tO~OV$}Oa$}Og%POo0cOv0qO~P'vOo0cOv0qO{%SO~P'vO&Y%UO~Oa!bOh!iO!X!kO!f!mOVba_bakbalbambanbaobapbasbazba{ba!xba#gba%[ba%_ba%jba%kba%lba%mba%nba%oba%pba%qba%rba%sba%zbagba!Tba!Uba%{ba!Wba![ba!Qba#[batba!mba~On%ZO~Oo%ZO~P'vOo0cO~P'vOk0eOl0fOm0dOn0dOo0mOp0nOs0rOg%wX!T%wX!U%wX%{%wX!W%wX![%wX!Q%wX#[%wX!m%wX~P)pO%{%]Og%vXz%vX!T%vX!U%vX!W%vX{%vX~Og%_Oz%`O!T%dO!U%cO~Og%_O~Oz%gO!T%dO!U%cO!W&SX~O!W%kO~Oz%lO{%nO!T%dO!U%cO![%}X~O![%rO~O![%sO~OQ#XO%`#XO%a%uO~OV%wOo0cOv0qO!PrO~P'vOQ#^OR#[O%`#^O%a%zO~OV!qa_!qaa!qah!qak!qal!qam!qan!qao!qap!qas!qaz!qa{!qa!X!qa!f!qa!x!qa#g!qa%[!qa%_!qa%j!qa%k!qa%l!qa%m!qa%n!qa%o!qa%p!qa%q!qa%r!qa%s!qa%z!qag!qa!T!qa!U!qa%{!qa!W!qa![!qa!Q!qa#[!qat!qa!m!qa~P#yOz%|O{%ha!x%ha#g%ha%[%ha%_%ha%z%ha~P%SOV&OOopOvqO{%ha!x%ha#g%ha%[%ha%_%ha%z%ha~P'vOz%|O{%ha!x%ha#g%ha%[%ha%_%ha%z%ha~OPdOVaOopOvqO!PrO!Q{O!{tO!}uO#PvO#RwO#TxO#XyO#ZzO#^|O#_|O#a}O#c!OO#g$zX%[$zX%_$zX~P'vO#g#gO%[&TO%_&TO~O!f&UOh&sX%[&sXz&sX#[&sX#g&sX%_&sX#Z&sXg&sX~Oh!iO%[&WO~Okealeameaneaoeapeaseazea{ea!xea#gea%[ea%_ea%zeagea!Tea!Uea%{ea!Wea![ea!Qea#[eatea!mea~P%SOsqazqa{qa#gqa%[qa%_qa%zqa~Ok!pOl!qOm!oOn!oOo!rOp!sO!xqa~PEcO%z&YOz%yX{%yX~O%uWOz%yX{%yX~Oz&]O{wX~O{&_O~Oz%lO#g%}X%[%}X%_%}Xg%}X{%}X![%}X!m%}X%z%}X~OV0lOo0cOv0qO!PrO~P'vO%z#|O#gUa%[Ua%_Ua~Oz&hO#g&PX%[&PX%_&PXn&PX~P%SOz&kO!Q&jO#g#Wa%[#Wa%_#Wa~Oz&lO#[&nO#g&rX%[&rX%_&rXg&rX~O!f$YO!r$YO#Z&qO%uWO~O#Z&qO~Oz&sO#g&tX%[&tX%_&tX~Oz&uO#g&pX%[&pX%_&pX{&pX~O!X&wO%z&xO~Oz&|On&wX~P%SOn'PO~OPdOVaOopOvqO!PrO!Q{O!{tO!}uO#PvO#RwO#TxO#XyO#ZzO#^|O#_|O#a}O#c!OO%['UO~P'vOt'YO#p'WO#q'XOP#naV#naf#nah#nao#nas#nav#na!P#na!Q#na!T#na!U#na!X#na!]#na!h#na!r#na!s#na!t#na!{#na!}#na#P#na#R#na#T#na#X#na#Z#na#^#na#_#na#a#na#c#na#l#na#o#na#s#na#u#na#z#na#}#na$P#na%X#na%o#na%p#na%t#na%u#na&Z#na&[#na&]#na&^#na&_#na&`#na&a#na&b#na&c#na&d#na&e#na&f#na&g#na&h#na&i#na&j#na%Z#na%_#na~Oz'ZO#[']O{&xX~Oh'_O!X&wO~Oh!iO{$jO!X&wO~O{'eO~P%SO%['hO%_'hO~OS'iO%['hO%_'hO~OV!aO_!aOa!bOh!iO!X!kO!f!mO%l!^O%m!_O%n!_O%o!`O%p!`O%q!aO%r!aO%s!aOkWilWimWinWioWipWisWizWi{Wi!xWi#gWi%[Wi%_Wi%jWi%zWigWi!TWi!UWi%{Wi!WWi![Wi!QWi#[WitWi!mWi~O%k!]O~P!#uO%kWi~P!#uOV!aO_!aOa!bOh!iO!X!kO!f!mO%o!`O%p!`O%q!aO%r!aO%s!aOkWilWimWinWioWipWisWizWi{Wi!xWi#gWi%[Wi%_Wi%jWi%kWi%lWi%zWigWi!TWi!UWi%{Wi!WWi![Wi!QWi#[WitWi!mWi~O%m!_O%n!_O~P!&pO%mWi%nWi~P!&pOa!bOh!iO!X!kO!f!mOkWilWimWinWioWipWisWizWi{Wi!xWi#gWi%[Wi%_Wi%jWi%kWi%lWi%mWi%nWi%oWi%pWi%zWigWi!TWi!UWi%{Wi!WWi![Wi!QWi#[WitWi!mWi~OV!aO_!aO%q!aO%r!aO%s!aO~P!)nOVWi_Wi%qWi%rWi%sWi~P!)nO!T%dO!U%cOg&VXz&VX~O%z'kO%{'kO~P,eOz'mOg&UX~Og'oO~Oz'pO{'rO!W&XX~Oo0cOv0qOz'pO{'sO!W&XX~P'vO!W'uO~Om!oOn!oOo!rOp!sOkjisjizji{ji!xji#gji%[ji%_ji%zji~Ol!qO~P!.aOlji~P!.aOk0eOl0fOm0dOn0dOo0mOp0nO~Ot'wO~P!/jOV'|Og'}Oo0cOv0qO~P'vOg'}Oz(OO~Og(QO~O!U(SO~Og(TOz(OO!T%dO!U%cO~P%SOk0eOl0fOm0dOn0dOo0mOp0nOgqa!Tqa!Uqa%{qa!Wqa![qa!Qqa#[qatqa!mqa~PEcOV'|Oo0cOv0qO!W&Sa~P'vOz(WO!W&Sa~O!W(XO~Oz(WO!T%dO!U%cO!W&Sa~P%SOV(]Oo0cOv0qO![%}a#g%}a%[%}a%_%}ag%}a{%}a!m%}a%z%}a~P'vOz(^O![%}a#g%}a%[%}a%_%}ag%}a{%}a!m%}a%z%}a~O![(aO~Oz(^O!T%dO!U%cO![%}a~P%SOz(dO!T%dO!U%cO![&Ta~P%SOz(gO{&lX![&lX!m&lX%z&lX~O{(kO![(mO!m(nO%z(jO~OV&OOopOvqO{%hi!x%hi#g%hi%[%hi%_%hi%z%hi~P'vOz(pO{%hi!x%hi#g%hi%[%hi%_%hi%z%hi~O!f&UOh&sa%[&saz&sa#[&sa#g&sa%_&sa#Z&sag&sa~O%[(uO~OV#sOa#tO%uWO~Oz&]O{wa~OopOvqO~P'vOz(^O#g%}a%[%}a%_%}ag%}a{%}a![%}a!m%}a%z%}a~P%SOz(zO#g%hX%[%hX%_%hX%z%hX~O%z#|O#gUi%[Ui%_Ui~O#g&Pa%[&Pa%_&Pan&Pa~P'vOz(}O#g&Pa%[&Pa%_&Pan&Pa~O%uWO#g&ra%[&ra%_&rag&ra~Oz)SO#g&ra%[&ra%_&rag&ra~Og)VO~OV)WOh$WO%uWO~O#Z)XO~O%uWO#g&ta%[&ta%_&ta~Oz)ZO#g&ta%[&ta%_&ta~Oo0cOv0qO#g&pa%[&pa%_&pa{&pa~P'vOz)^O#g&pa%[&pa%_&pa{&pa~OV)`Oa)`O%uWO~O%z)eO~Ot)hO#j)gOP#hiV#hif#hih#hio#his#hiv#hi!P#hi!Q#hi!T#hi!U#hi!X#hi!]#hi!h#hi!r#hi!s#hi!t#hi!{#hi!}#hi#P#hi#R#hi#T#hi#X#hi#Z#hi#^#hi#_#hi#a#hi#c#hi#l#hi#o#hi#s#hi#u#hi#z#hi#}#hi$P#hi%X#hi%o#hi%p#hi%t#hi%u#hi&Z#hi&[#hi&]#hi&^#hi&_#hi&`#hi&a#hi&b#hi&c#hi&d#hi&e#hi&f#hi&g#hi&h#hi&i#hi&j#hi%Z#hi%_#hi~Ot)iOP#kiV#kif#kih#kio#kis#kiv#ki!P#ki!Q#ki!T#ki!U#ki!X#ki!]#ki!h#ki!r#ki!s#ki!t#ki!{#ki!}#ki#P#ki#R#ki#T#ki#X#ki#Z#ki#^#ki#_#ki#a#ki#c#ki#l#ki#o#ki#s#ki#u#ki#z#ki#}#ki$P#ki%X#ki%o#ki%p#ki%t#ki%u#ki&Z#ki&[#ki&]#ki&^#ki&_#ki&`#ki&a#ki&b#ki&c#ki&d#ki&e#ki&f#ki&g#ki&h#ki&i#ki&j#ki%Z#ki%_#ki~OV)kOn&wa~P'vOz)lOn&wa~Oz)lOn&wa~P%SOn)pO~O%Y)tO~Ot)wO#p'WO#q)vOP#niV#nif#nih#nio#nis#niv#ni!P#ni!Q#ni!T#ni!U#ni!X#ni!]#ni!h#ni!r#ni!s#ni!t#ni!{#ni!}#ni#P#ni#R#ni#T#ni#X#ni#Z#ni#^#ni#_#ni#a#ni#c#ni#l#ni#o#ni#s#ni#u#ni#z#ni#}#ni$P#ni%X#ni%o#ni%p#ni%t#ni%u#ni&Z#ni&[#ni&]#ni&^#ni&_#ni&`#ni&a#ni&b#ni&c#ni&d#ni&e#ni&f#ni&g#ni&h#ni&i#ni&j#ni%Z#ni%_#ni~OV)zOo0cOv0qO{$jO~P'vOo0cOv0qO{&xa~P'vOz*OO{&xa~OV*SOa*TOg*WO%q*UO%uWO~O{$jO&{*YO~Oh'_O~Oh!iO{$jO~O%[*_O~O%[*aO%_*aO~OV$}Oa$}Oo0cOv0qOg&Ua~P'vOz*dOg&Ua~Oo0cOv0qO{*gO!W&Xa~P'vOz*hO!W&Xa~Oo0cOv0qOz*hO{*kO!W&Xa~P'vOo0cOv0qOz*hO!W&Xa~P'vOz*hO{*kO!W&Xa~Om0dOn0dOo0mOp0nOgjikjisjizji!Tji!Uji%{ji!Wji{ji![ji#gji%[ji%_ji!Qji#[jitji!mji%zji~Ol0fO~P!NkOlji~P!NkOV'|Og*pOo0cOv0qO~P'vOn*rO~Og*pOz*tO~Og*uO~OV'|Oo0cOv0qO!W&Si~P'vOz*vO!W&Si~O!W*wO~OV(]Oo0cOv0qO![%}i#g%}i%[%}i%_%}ig%}i{%}i!m%}i%z%}i~P'vOz*zO!T%dO!U%cO![&Ti~Oz*}O![%}i#g%}i%[%}i%_%}ig%}i{%}i!m%}i%z%}i~O![+OO~Oa+QOo0cOv0qO![&Ti~P'vOz*zO![&Ti~O![+SO~OV+UOo0cOv0qO{&la![&la!m&la%z&la~P'vOz+VO{&la![&la!m&la%z&la~O!]+YO&n+[O![!nX~O![+^O~O{(kO![+_O~O{(kO![+_O!m+`O~OV&OOopOvqO{%hq!x%hq#g%hq%[%hq%_%hq%z%hq~P'vOz$ri{$ri!x$ri#g$ri%[$ri%_$ri%z$ri~P%SOV&OOopOvqO~P'vOV&OOo0cOv0qO#g%ha%[%ha%_%ha%z%ha~P'vOz+aO#g%ha%[%ha%_%ha%z%ha~Oz$ia#g$ia%[$ia%_$ian$ia~P%SO#g&Pi%[&Pi%_&Pin&Pi~P'vOz+dO#g#Wq%[#Wq%_#Wq~O#[+eOz$va#g$va%[$va%_$vag$va~O%uWO#g&ri%[&ri%_&rig&ri~Oz+gO#g&ri%[&ri%_&rig&ri~OV+iOh$WO%uWO~O%uWO#g&ti%[&ti%_&ti~Oo0cOv0qO#g&pi%[&pi%_&pi{&pi~P'vO{#{Oz#eX!W#eX~Oz+mO!W&uX~O!W+oO~Ot+rO#j)gOP#hqV#hqf#hqh#hqo#hqs#hqv#hq!P#hq!Q#hq!T#hq!U#hq!X#hq!]#hq!h#hq!r#hq!s#hq!t#hq!{#hq!}#hq#P#hq#R#hq#T#hq#X#hq#Z#hq#^#hq#_#hq#a#hq#c#hq#l#hq#o#hq#s#hq#u#hq#z#hq#}#hq$P#hq%X#hq%o#hq%p#hq%t#hq%u#hq&Z#hq&[#hq&]#hq&^#hq&_#hq&`#hq&a#hq&b#hq&c#hq&d#hq&e#hq&f#hq&g#hq&h#hq&i#hq&j#hq%Z#hq%_#hq~On$|az$|a~P%SOV)kOn&wi~P'vOz+yOn&wi~Oz,TO{$jO#[,TO~O#q,VOP#nqV#nqf#nqh#nqo#nqs#nqv#nq!P#nq!Q#nq!T#nq!U#nq!X#nq!]#nq!h#nq!r#nq!s#nq!t#nq!{#nq!}#nq#P#nq#R#nq#T#nq#X#nq#Z#nq#^#nq#_#nq#a#nq#c#nq#l#nq#o#nq#s#nq#u#nq#z#nq#}#nq$P#nq%X#nq%o#nq%p#nq%t#nq%u#nq&Z#nq&[#nq&]#nq&^#nq&_#nq&`#nq&a#nq&b#nq&c#nq&d#nq&e#nq&f#nq&g#nq&h#nq&i#nq&j#nq%Z#nq%_#nq~O#[,WOz%Oa{%Oa~Oo0cOv0qO{&xi~P'vOz,YO{&xi~O{#{O%z,[Og&zXz&zX~O%uWOg&zXz&zX~Oz,`Og&yX~Og,bO~O%Y,eO~O!T%dO!U%cOg&Viz&Vi~OV$}Oa$}Oo0cOv0qOg&Ui~P'vO{,hOz$la!W$la~Oo0cOv0qO{,iOz$la!W$la~P'vOo0cOv0qO{*gO!W&Xi~P'vOz,lO!W&Xi~Oo0cOv0qOz,lO!W&Xi~P'vOz,lO{,oO!W&Xi~Og$hiz$hi!W$hi~P%SOV'|Oo0cOv0qO~P'vOn,qO~OV'|Og,rOo0cOv0qO~P'vOV'|Oo0cOv0qO!W&Sq~P'vOz$gi![$gi#g$gi%[$gi%_$gig$gi{$gi!m$gi%z$gi~P%SOV(]Oo0cOv0qO~P'vOa+QOo0cOv0qO![&Tq~P'vOz,sO![&Tq~O![,tO~OV(]Oo0cOv0qO![%}q#g%}q%[%}q%_%}qg%}q{%}q!m%}q%z%}q~P'vO{,uO~OV+UOo0cOv0qO{&li![&li!m&li%z&li~P'vOz,zO{&li![&li!m&li%z&li~O!]+YO&n+[O![!na~O{(kO![,}O~OV&OOo0cOv0qO#g%hi%[%hi%_%hi%z%hi~P'vOz-OO#g%hi%[%hi%_%hi%z%hi~O%uWO#g&rq%[&rq%_&rqg&rq~Oz-RO#g&rq%[&rq%_&rqg&rq~OV)`Oa)`O%uWO!W&ua~Oz-TO!W&ua~On$|iz$|i~P%SOV)kO~P'vOV)kOn&wq~P'vOt-XOP#myV#myf#myh#myo#mys#myv#my!P#my!Q#my!T#my!U#my!X#my!]#my!h#my!r#my!s#my!t#my!{#my!}#my#P#my#R#my#T#my#X#my#Z#my#^#my#_#my#a#my#c#my#l#my#o#my#s#my#u#my#z#my#}#my$P#my%X#my%o#my%p#my%t#my%u#my&Z#my&[#my&]#my&^#my&_#my&`#my&a#my&b#my&c#my&d#my&e#my&f#my&g#my&h#my&i#my&j#my%Z#my%_#my~O%Z-]O%_-]O~P`O#q-^OP#nyV#nyf#nyh#nyo#nys#nyv#ny!P#ny!Q#ny!T#ny!U#ny!X#ny!]#ny!h#ny!r#ny!s#ny!t#ny!{#ny!}#ny#P#ny#R#ny#T#ny#X#ny#Z#ny#^#ny#_#ny#a#ny#c#ny#l#ny#o#ny#s#ny#u#ny#z#ny#}#ny$P#ny%X#ny%o#ny%p#ny%t#ny%u#ny&Z#ny&[#ny&]#ny&^#ny&_#ny&`#ny&a#ny&b#ny&c#ny&d#ny&e#ny&f#ny&g#ny&h#ny&i#ny&j#ny%Z#ny%_#ny~Oz-aO{$jO#[-aO~Oo0cOv0qO{&xq~P'vOz-dO{&xq~O%z,[Og&zaz&za~O{#{Og&zaz&za~OV*SOa*TO%q*UO%uWOg&ya~Oz-hOg&ya~O$S-lO~OV$}Oa$}Oo0cOv0qO~P'vOo0cOv0qO{-mOz$li!W$li~P'vOo0cOv0qOz$li!W$li~P'vO{-mOz$li!W$li~Oo0cOv0qO{*gO~P'vOo0cOv0qO{*gO!W&Xq~P'vOz-pO!W&Xq~Oo0cOv0qOz-pO!W&Xq~P'vOs-sO!T%dO!U%cOg&Oq!W&Oq![&Oqz&Oq~P!/jOa+QOo0cOv0qO![&Ty~P'vOz$ji![$ji~P%SOa+QOo0cOv0qO~P'vOV+UOo0cOv0qO~P'vOV+UOo0cOv0qO{&lq![&lq!m&lq%z&lq~P'vO{(kO![-xO!m-yO%z-wO~OV&OOo0cOv0qO#g%hq%[%hq%_%hq%z%hq~P'vO%uWO#g&ry%[&ry%_&ryg&ry~OV)`Oa)`O%uWO!W&ui~Ot-}OP#m!RV#m!Rf#m!Rh#m!Ro#m!Rs#m!Rv#m!R!P#m!R!Q#m!R!T#m!R!U#m!R!X#m!R!]#m!R!h#m!R!r#m!R!s#m!R!t#m!R!{#m!R!}#m!R#P#m!R#R#m!R#T#m!R#X#m!R#Z#m!R#^#m!R#_#m!R#a#m!R#c#m!R#l#m!R#o#m!R#s#m!R#u#m!R#z#m!R#}#m!R$P#m!R%X#m!R%o#m!R%p#m!R%t#m!R%u#m!R&Z#m!R&[#m!R&]#m!R&^#m!R&_#m!R&`#m!R&a#m!R&b#m!R&c#m!R&d#m!R&e#m!R&f#m!R&g#m!R&h#m!R&i#m!R&j#m!R%Z#m!R%_#m!R~Oo0cOv0qO{&xy~P'vOV*SOa*TO%q*UO%uWOg&yi~O$S-lO%Z.VO%_.VO~OV.aOh._O!X.^O!].`O!h.YO!s.[O!t.[O%p.XO%uWO&Z]O&[]O&]]O&^]O&_]O&`]O&a]O&b]O~Oo0cOv0qOz$lq!W$lq~P'vO{.fOz$lq!W$lq~Oo0cOv0qO{*gO!W&Xy~P'vOz.gO!W&Xy~Oo0cOv.kO~P'vOs-sO!T%dO!U%cOg&Oy!W&Oy![&Oyz&Oy~P!/jO{(kO![.nO~O{(kO![.nO!m.oO~OV*SOa*TO%q*UO%uWO~Oh.tO!f.rOz$TX#[$TX%j$TXg$TX~Os$TX{$TX!W$TX![$TX~P$-bO%o.vO%p.vOs$UXz$UX{$UX#[$UX%j$UX!W$UXg$UX![$UX~O!h.xO~Oz.|O#[/OO%j.yOs&|X{&|X!W&|Xg&|X~Oa/RO~P$)zOh.tOs&}Xz&}X{&}X#[&}X%j&}X!W&}Xg&}X![&}X~Os/VO{$jO~Oo0cOv0qOz$ly!W$ly~P'vOo0cOv0qO{*gO!W&X!R~P'vOz/ZO!W&X!R~Og&RXs&RX!T&RX!U&RX!W&RX![&RXz&RX~P!/jOs-sO!T%dO!U%cOg&Qa!W&Qa![&Qaz&Qa~O{(kO![/^O~O!f.rOh$[as$[az$[a{$[a#[$[a%j$[a!W$[ag$[a![$[a~O!h/eO~O%o.vO%p.vOs$Uaz$Ua{$Ua#[$Ua%j$Ua!W$Uag$Ua![$Ua~O%j.yOs$Yaz$Ya{$Ya#[$Ya!W$Yag$Ya![$Ya~Os&|a{&|a!W&|ag&|a~P$)nOz/jOs&|a{&|a!W&|ag&|a~O!W/mO~Og/mO~O{/oO~O![/pO~Oo0cOv0qO{*gO!W&X!Z~P'vO{/sO~O%z/tO~P$-bOz/uO#[/OO%j.yOg'PX~Oz/uOg'PX~Og/wO~O!h/xO~O#[/OOs%Saz%Sa{%Sa%j%Sa!W%Sag%Sa![%Sa~O#[/OO%j.yOs%Waz%Wa{%Wa!W%Wag%Wa~Os&|i{&|i!W&|ig&|i~P$)nOz/zO#[/OO%j.yO!['Oa~Og'Pa~P$)nOz0SOg'Pa~Oa0UO!['Oi~P$)zOz0WO!['Oi~Oz0WO#[/OO%j.yO!['Oi~O#[/OO%j.yOg$biz$bi~O%z0ZO~P$-bO#[/OO%j.yOg%Vaz%Va~Og'Pi~P$)nO{0^O~Oa0UO!['Oq~P$)zOz0`O!['Oq~O#[/OO%j.yOz%Ui![%Ui~Oa0UO~P$)zOa0UO!['Oy~P$)zO#[/OO%j.yOg$ciz$ci~O#[/OO%j.yOz%Uq![%Uq~Oz+aO#g%ha%[%ha%_%ha%z%ha~P%SOV&OOo0cOv0qO~P'vOn0hO~Oo0hO~P'vO{0iO~Ot0jO~P!/jO&]&Z&j&h&i&g&f&d&e&c&b&`&a&_&^&[%u~",goto:"!=j'QPPPPPP'RP'Z*s+[+t,_,y-fP.SP'Z.r.r'ZPPP'Z2[PPPPPP2[5PPP5PP7b7k=sPP=v>h>kPP'Z'ZPP>zPP'Z'ZPP'Z'Z'Z'Z'Z?O?w'ZP?zP@QDXGuGyPG|HWH['ZPPPH_Hk'RP'R'RP'RP'RP'RP'RP'R'R'RP'RPP'RPP'RP'RPHqH}IVPI^IdPI^PI^I^PPPI^PKrPK{LVL]KrPI^LfPI^PLmLsPLwM]MzNeLwLwNkNxLwLwLwLw! ^! d! g! l! o! y!!P!!]!!o!!u!#P!#V!#s!#y!$P!$Z!$a!$g!$y!%T!%Z!%a!%k!%q!%w!%}!&T!&Z!&e!&k!&u!&{!'U!'[!'k!'s!'}!(UPPPPPPPPPPP!([!(_!(e!(n!(x!)TPPPPPPPPPPPP!-u!/Z!3^!6oPP!6w!7W!7a!8Y!8P!8c!8i!8l!8o!8r!8z!9jPPPPPPPPPPPPPPPPP!9m!9q!9wP!:]!:a!:m!:v!;S!;j!;m!;p!;v!;|!_![!]Do!]!^Es!^!_FZ!_!`Gk!`!aHX!a!b%T!b!cIf!c!dJU!d!eK^!e!hJU!h!i!#f!i!tJU!t!u!,|!u!wJU!w!x!.t!x!}JU!}#O!0S#O#P&o#P#Q!0j#Q#R!1Q#R#SJU#S#T%T#T#UJU#U#VK^#V#YJU#Y#Z!#f#Z#fJU#f#g!,|#g#iJU#i#j!.t#j#oJU#o#p!1n#p#q!1s#q#r!2a#r#s!2f#s$g%T$g;'SJU;'S;=`KW<%lOJU`%YT&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%T`%lP;=`<%l%To%v]&n`%c_OX%TXY%oY[%T[]%o]p%Tpq%oq#O%T#O#P&o#P#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%To&tX&n`OY%TYZ%oZ]%T]^%o^#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tc'f[&n`O!_%T!_!`([!`#T%T#T#U(r#U#f%T#f#g(r#g#h(r#h#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tc(cTmR&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tc(yT!mR&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk)aV&n`&[ZOr%Trs)vs#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk){V&n`Or%Trs*bs#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk*iT&n`&^ZO#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%To+PZS_&n`OY*xYZ%TZ]*x]^%T^#o*x#o#p+r#p#q*x#q#r+r#r;'S*x;'S;=`,^<%lO*x_+wTS_OY+rZ]+r^;'S+r;'S;=`,W<%lO+r_,ZP;=`<%l+ro,aP;=`<%l*xj,kV%rQ&n`O!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tj-XT!xY&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tj-oV%lQ&n`O!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk.]V&n`&ZZOw%Twx.rx#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk.wV&n`Ow%Twx/^x#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk/eT&n`&]ZO#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk/{ThZ&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tc0cTgR&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk0yXVZ&n`Oz%Tz{1f{!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk1mVaR&n`O!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk2ZV%oZ&n`O!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tc2wTzR&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%To3_W%pZ&n`O!_%T!_!`-Q!`!a3w!a#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Td4OT&{S&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk4fX!fQ&n`O!O%T!O!P5R!P!Q%T!Q![6T![#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk5WV&n`O!O%T!O!P5m!P#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk5tT!rZ&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Ti6[a!hX&n`O!Q%T!Q![6T![!g%T!g!h7a!h!l%T!l!m9s!m#R%T#R#S6T#S#X%T#X#Y7a#Y#^%T#^#_9s#_#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Ti7fZ&n`O{%T{|8X|}%T}!O8X!O!Q%T!Q![8s![#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Ti8^V&n`O!Q%T!Q![8s![#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Ti8z]!hX&n`O!Q%T!Q![8s![!l%T!l!m9s!m#R%T#R#S8s#S#^%T#^#_9s#_#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Ti9zT!hX&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk:bX%qR&n`O!P%T!P!Q:}!Q!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tj;UV%sQ&n`O!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Ti;ro!hX&n`O!O%T!O!P=s!P!Q%T!Q![>_![!d%T!d!e?q!e!g%T!g!h7a!h!l%T!l!m9s!m!q%T!q!rA]!r!z%T!z!{Bq!{#R%T#R#S>_#S#U%T#U#V?q#V#X%T#X#Y7a#Y#^%T#^#_9s#_#c%T#c#dA]#d#l%T#l#mBq#m#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Ti=xV&n`O!Q%T!Q![6T![#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Ti>fc!hX&n`O!O%T!O!P=s!P!Q%T!Q![>_![!g%T!g!h7a!h!l%T!l!m9s!m#R%T#R#S>_#S#X%T#X#Y7a#Y#^%T#^#_9s#_#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Ti?vY&n`O!Q%T!Q!R@f!R!S@f!S#R%T#R#S@f#S#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Ti@mY!hX&n`O!Q%T!Q!R@f!R!S@f!S#R%T#R#S@f#S#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TiAbX&n`O!Q%T!Q!YA}!Y#R%T#R#SA}#S#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TiBUX!hX&n`O!Q%T!Q!YA}!Y#R%T#R#SA}#S#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TiBv]&n`O!Q%T!Q![Co![!c%T!c!iCo!i#R%T#R#SCo#S#T%T#T#ZCo#Z#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TiCv]!hX&n`O!Q%T!Q![Co![!c%T!c!iCo!i#R%T#R#SCo#S#T%T#T#ZCo#Z#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%ToDvV{_&n`O!_%T!_!`E]!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TcEdT%{R&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TkEzT#gZ&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TkFbXmR&n`O!^%T!^!_F}!_!`([!`!a([!a#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TjGUV%mQ&n`O!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TkGrV%zZ&n`O!_%T!_!`([!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TkH`WmR&n`O!_%T!_!`([!`!aHx!a#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TjIPV%nQ&n`O!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TkIoV_Q#}P&n`O!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%ToJ_]&n`&YS%uZO!Q%T!Q![JU![!c%T!c!}JU!}#R%T#R#SJU#S#T%T#T#oJU#p#q%T#r$g%T$g;'SJU;'S;=`KW<%lOJUoKZP;=`<%lJUoKge&n`&YS%uZOr%Trs)Ysw%Twx.Ux!Q%T!Q![JU![!c%T!c!tJU!t!uLx!u!}JU!}#R%T#R#SJU#S#T%T#T#fJU#f#gLx#g#oJU#p#q%T#r$g%T$g;'SJU;'S;=`KW<%lOJUoMRa&n`&YS%uZOr%TrsNWsw%Twx! vx!Q%T!Q![JU![!c%T!c!}JU!}#R%T#R#SJU#S#T%T#T#oJU#p#q%T#r$g%T$g;'SJU;'S;=`KW<%lOJUkN_V&n`&`ZOr%TrsNts#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TkNyV&n`Or%Trs! `s#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk! gT&n`&bZO#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk! }V&n`&_ZOw%Twx!!dx#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!!iV&n`Ow%Twx!#Ox#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!#VT&n`&aZO#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%To!#oe&n`&YS%uZOr%Trs!%Qsw%Twx!&px!Q%T!Q![JU![!c%T!c!tJU!t!u!(`!u!}JU!}#R%T#R#SJU#S#T%T#T#fJU#f#g!(`#g#oJU#p#q%T#r$g%T$g;'SJU;'S;=`KW<%lOJUk!%XV&n`&dZOr%Trs!%ns#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!%sV&n`Or%Trs!&Ys#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!&aT&n`&fZO#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!&wV&n`&cZOw%Twx!'^x#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!'cV&n`Ow%Twx!'xx#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!(PT&n`&eZO#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%To!(ia&n`&YS%uZOr%Trs!)nsw%Twx!+^x!Q%T!Q![JU![!c%T!c!}JU!}#R%T#R#SJU#S#T%T#T#oJU#p#q%T#r$g%T$g;'SJU;'S;=`KW<%lOJUk!)uV&n`&hZOr%Trs!*[s#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!*aV&n`Or%Trs!*vs#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!*}T&n`&jZO#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!+eV&n`&gZOw%Twx!+zx#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!,PV&n`Ow%Twx!,fx#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!,mT&n`&iZO#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%To!-Vi&n`&YS%uZOr%TrsNWsw%Twx! vx!Q%T!Q![JU![!c%T!c!dJU!d!eLx!e!hJU!h!i!(`!i!}JU!}#R%T#R#SJU#S#T%T#T#UJU#U#VLx#V#YJU#Y#Z!(`#Z#oJU#p#q%T#r$g%T$g;'SJU;'S;=`KW<%lOJUo!.}a&n`&YS%uZOr%Trs)Ysw%Twx.Ux!Q%T!Q![JU![!c%T!c!}JU!}#R%T#R#SJU#S#T%T#T#oJU#p#q%T#r$g%T$g;'SJU;'S;=`KW<%lOJUk!0ZT!XZ&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tc!0qT!WR&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tj!1XV%kQ&n`O!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%T~!1sO!]~k!1zV%jR&n`O!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%T~!2fO![~i!2mT%tX&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%T",tokenizers:[nt,Qe,Ze,rt,0,1,2,3,4],topRules:{Script:[0,5]},specialized:[{term:221,get:e=>ot[e]||-1}],tokenPrec:7668}),G=new t,K=new Set([`Script`,`Body`,`FunctionDefinition`,`ClassDefinition`,`LambdaExpression`,`ForStatement`,`MatchClause`]);function q(e){return(t,n,r)=>{if(r)return!1;let i=t.node.getChild(`VariableName`);return i&&n(i,e),!0}}var ct={FunctionDefinition:q(`function`),ClassDefinition:q(`class`),ForStatement(e,t,n){if(n){for(let n=e.node.firstChild;n;n=n.nextSibling)if(n.name==`VariableName`)t(n,`variable`);else if(n.name==`in`)break}},ImportStatement(e,t){let{node:n}=e,r=n.firstChild?.name==`from`;for(let e=n.getChild(`import`);e;e=e.nextSibling)e.name==`VariableName`&&e.nextSibling?.name!=`as`&&t(e,r?`variable`:`namespace`)},AssignStatement(e,t){for(let n=e.node.firstChild;n;n=n.nextSibling)if(n.name==`VariableName`)t(n,`variable`);else if(n.name==`:`||n.name==`AssignOp`)break},ParamList(e,t){for(let n=null,r=e.node.firstChild;r;r=r.nextSibling)r.name==`VariableName`&&(!n||!/\*|AssignOp/.test(n.name))&&t(r,`variable`),n=r},CapturePattern:q(`variable`),AsPattern:q(`variable`),__proto__:null};function J(t,n){let r=G.get(n);if(r)return r;let i=[],a=!0;function o(e,n){let r=t.sliceString(e.from,e.to);i.push({label:r,type:n})}return n.cursor(e.IncludeAnonymous).iterate(e=>{if(e.name){let t=ct[e.name];if(t&&t(e,o,a)||!a&&K.has(e.name))return!1;a=!1}else if(e.to-e.from>8192){for(let n of J(t,e.node))i.push(n);return!1}}),G.set(n,i),i}var Y=/^[\w\xa1-\uffff][\w\d\xa1-\uffff]*$/,X=[`String`,`FormatString`,`Comment`,`PropertyName`];function lt(e){let t=te(e.state).resolveInner(e.pos,-1);if(X.indexOf(t.name)>-1)return null;let n=t.name==`VariableName`||t.to-t.from<20&&Y.test(e.state.sliceDoc(t.from,t.to));if(!n&&!e.explicit)return null;let r=[];for(let n=t;n;n=n.parent)K.has(n.name)&&(r=r.concat(J(e.state.doc,n)));return{options:r,from:n?t.from:e.pos,validFor:Y}}var ut=[`__annotations__`,`__builtins__`,`__debug__`,`__doc__`,`__import__`,`__name__`,`__loader__`,`__package__`,`__spec__`,`False`,`None`,`True`].map(e=>({label:e,type:`constant`})).concat(`ArithmeticError.AssertionError.AttributeError.BaseException.BlockingIOError.BrokenPipeError.BufferError.BytesWarning.ChildProcessError.ConnectionAbortedError.ConnectionError.ConnectionRefusedError.ConnectionResetError.DeprecationWarning.EOFError.Ellipsis.EncodingWarning.EnvironmentError.Exception.FileExistsError.FileNotFoundError.FloatingPointError.FutureWarning.GeneratorExit.IOError.ImportError.ImportWarning.IndentationError.IndexError.InterruptedError.IsADirectoryError.KeyError.KeyboardInterrupt.LookupError.MemoryError.ModuleNotFoundError.NameError.NotADirectoryError.NotImplemented.NotImplementedError.OSError.OverflowError.PendingDeprecationWarning.PermissionError.ProcessLookupError.RecursionError.ReferenceError.ResourceWarning.RuntimeError.RuntimeWarning.StopAsyncIteration.StopIteration.SyntaxError.SyntaxWarning.SystemError.SystemExit.TabError.TimeoutError.TypeError.UnboundLocalError.UnicodeDecodeError.UnicodeEncodeError.UnicodeError.UnicodeTranslateError.UnicodeWarning.UserWarning.ValueError.Warning.ZeroDivisionError`.split(`.`).map(e=>({label:e,type:`type`}))).concat([`bool`,`bytearray`,`bytes`,`classmethod`,`complex`,`float`,`frozenset`,`int`,`list`,`map`,`memoryview`,`object`,`range`,`set`,`staticmethod`,`str`,`super`,`tuple`,`type`].map(e=>({label:e,type:`class`}))).concat(`abs.aiter.all.anext.any.ascii.bin.breakpoint.callable.chr.compile.delattr.dict.dir.divmod.enumerate.eval.exec.exit.filter.format.getattr.globals.hasattr.hash.help.hex.id.input.isinstance.issubclass.iter.len.license.locals.max.min.next.oct.open.ord.pow.print.property.quit.repr.reversed.round.setattr.slice.sorted.sum.vars.zip`.split(`.`).map(e=>({label:e,type:`function`}))),dt=[u("def ${name}(${params}):\n ${}",{label:`def`,detail:`function`,type:`keyword`}),u("for ${name} in ${collection}:\n ${}",{label:`for`,detail:`loop`,type:`keyword`}),u("while ${}:\n ${}",{label:`while`,detail:`loop`,type:`keyword`}),u(`try: \${} except \${error}: \${}`,{label:`try`,detail:`/ except block`,type:`keyword`}),u(`if \${}: diff --git a/mcp/src/agents_remember/package_data/dashboard/assets/dist-17r-i6rR.js b/mcp/src/agents_remember/package_data/dashboard/assets/dist-C-e5rk9l.js similarity index 99% rename from mcp/src/agents_remember/package_data/dashboard/assets/dist-17r-i6rR.js rename to mcp/src/agents_remember/package_data/dashboard/assets/dist-C-e5rk9l.js index 161484ee..719ba112 100644 --- a/mcp/src/agents_remember/package_data/dashboard/assets/dist-17r-i6rR.js +++ b/mcp/src/agents_remember/package_data/dashboard/assets/dist-C-e5rk9l.js @@ -1,4 +1,4 @@ -import{$ as e,C as t,D as n,I as r,M as i,_ as a,b as o,c as s,d as c,f as ee,i as te,l as ne,m as re,s as l,t as ie,u,v as d,x as f}from"./index-CLJ0aycO.js";import{i as p,n as m,r as ae,t as oe}from"./dist-B2R-c1vX.js";import{i as h,n as g,r as se}from"./dist-BxWz473c.js";var ce=316,le=317,_=1,ue=2,de=3,fe=4,pe=318,me=320,he=321,ge=5,_e=6,ve=0,v=[9,10,11,12,13,32,133,160,5760,8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,8232,8233,8239,8287,12288],y=125,ye=59,b=47,x=42,S=43,C=45,w=60,T=44,E=63,D=46,O=91,k=new oe({start:!1,shift(e,t){return t==ge||t==_e||t==me?e:t==he},strict:!1}),A=new m((e,t)=>{let{next:n}=e;(n==y||n==-1||t.context)&&e.acceptToken(pe)},{contextual:!0,fallback:!0}),j=new m((e,t)=>{let{next:n}=e,r;v.indexOf(n)>-1||n==b&&((r=e.peek(1))==b||r==x)||n!=y&&n!=ye&&n!=-1&&!t.context&&e.acceptToken(ce)},{contextual:!0}),M=new m((e,t)=>{e.next==O&&!t.context&&e.acceptToken(le)},{contextual:!0}),N=new m((e,t)=>{let{next:n}=e;if(n==S||n==C){if(e.advance(),n==e.next){e.advance();let n=!t.context&&t.canShift(_);e.acceptToken(n?_:ue)}}else n==E&&e.peek(1)==D&&(e.advance(),e.advance(),(e.next<48||e.next>57)&&e.acceptToken(de))},{contextual:!0});function P(e,t){return e>=65&&e<=90||e>=97&&e<=122||e==95||e>=192||!t&&e>=48&&e<=57}var be=new m((e,t)=>{if(e.next!=w||!t.dialectEnabled(ve)||(e.advance(),e.next==b))return;let n=0;for(;v.indexOf(e.next)>-1;)e.advance(),n++;if(P(e.next,!0)){for(e.advance(),n++;P(e.next,!1);)e.advance(),n++;for(;v.indexOf(e.next)>-1;)e.advance(),n++;if(e.next==T)return;for(let t=0;;t++){if(t==7){if(!P(e.next,!0))return;break}if(e.next!=`extends`.charCodeAt(t))break;e.advance(),n++}}e.acceptToken(fe,-n)}),xe=o({"get set async static":f.modifier,"for while do if else switch try catch finally return throw break continue default case defer":f.controlKeyword,"in of await yield void typeof delete instanceof as satisfies":f.operatorKeyword,"let var const using function class extends":f.definitionKeyword,"import export from":f.moduleKeyword,"with debugger new":f.keyword,TemplateString:f.special(f.string),super:f.atom,BooleanLiteral:f.bool,this:f.self,null:f.null,Star:f.modifier,VariableName:f.variableName,"CallExpression/VariableName TaggedTemplateExpression/VariableName":f.function(f.variableName),VariableDefinition:f.definition(f.variableName),Label:f.labelName,PropertyName:f.propertyName,PrivatePropertyName:f.special(f.propertyName),"CallExpression/MemberExpression/PropertyName":f.function(f.propertyName),"FunctionDeclaration/VariableDefinition":f.function(f.definition(f.variableName)),"ClassDeclaration/VariableDefinition":f.definition(f.className),"NewExpression/VariableName":f.className,PropertyDefinition:f.definition(f.propertyName),PrivatePropertyDefinition:f.definition(f.special(f.propertyName)),UpdateOp:f.updateOperator,"LineComment Hashbang":f.lineComment,BlockComment:f.blockComment,Number:f.number,String:f.string,Escape:f.escape,ArithOp:f.arithmeticOperator,LogicOp:f.logicOperator,BitOp:f.bitwiseOperator,CompareOp:f.compareOperator,RegExp:f.regexp,Equals:f.definitionOperator,Arrow:f.function(f.punctuation),": Spread":f.punctuation,"( )":f.paren,"[ ]":f.squareBracket,"{ }":f.brace,"InterpolationStart InterpolationEnd":f.special(f.brace),".":f.derefOperator,", ;":f.separator,"@":f.meta,TypeName:f.typeName,TypeDefinition:f.definition(f.typeName),"type enum interface implements namespace module declare":f.definitionKeyword,"abstract global Privacy readonly override":f.modifier,"is keyof unique infer asserts":f.operatorKeyword,JSXAttributeValue:f.attributeValue,JSXText:f.content,"JSXStartTag JSXStartCloseTag JSXSelfCloseEndTag JSXEndTag":f.angleBracket,"JSXIdentifier JSXNameSpacedName":f.tagName,"JSXAttribute/JSXIdentifier JSXAttribute/JSXNameSpacedName":f.attributeName,"JSXBuiltin/JSXIdentifier":f.standard(f.tagName)}),Se={__proto__:null,export:20,as:25,from:33,default:36,async:41,function:42,in:52,out:55,const:56,extends:60,this:64,true:72,false:72,null:84,void:88,typeof:92,super:108,new:142,delete:154,yield:163,await:167,class:172,public:235,private:235,protected:235,readonly:237,instanceof:256,satisfies:259,import:292,keyof:349,unique:353,infer:359,asserts:395,is:397,abstract:417,implements:419,type:421,let:424,var:426,using:429,interface:435,enum:439,namespace:445,module:447,declare:451,global:455,defer:471,for:476,of:485,while:488,with:492,do:496,if:500,else:502,switch:506,case:512,try:518,catch:522,finally:526,return:530,throw:534,break:538,continue:542,debugger:546},Ce={__proto__:null,async:129,get:131,set:133,declare:195,public:197,private:197,protected:197,static:199,abstract:201,override:203,readonly:209,accessor:211,new:401},we={__proto__:null,"<":193},Te=ae.deserialize({version:14,states:"$F|Q%TQlOOO%[QlOOO'_QpOOP(lO`OOO*zQ!0MxO'#CiO+RO#tO'#CjO+aO&jO'#CjO+oO#@ItO'#DaO.QQlO'#DgO.bQlO'#DrO%[QlO'#DzO0fQlO'#ESOOQ!0Lf'#E['#E[O1PQ`O'#EXOOQO'#Ep'#EpOOQO'#Il'#IlO1XQ`O'#GsO1dQ`O'#EoO1iQ`O'#EoO3hQ!0MxO'#JrO6[Q!0MxO'#JsO6uQ`O'#F]O6zQ,UO'#FtOOQ!0Lf'#Ff'#FfO7VO7dO'#FfO9XQMhO'#F|O9`Q`O'#F{OOQ!0Lf'#Js'#JsOOQ!0Lb'#Jr'#JrO9eQ`O'#GwOOQ['#K_'#K_O9pQ`O'#IYO9uQ!0LrO'#IZOOQ['#J`'#J`OOQ['#I_'#I_Q`QlOOQ`QlOOO9}Q!L^O'#DvO:UQlO'#EOO:]QlO'#EQO9kQ`O'#GsO:dQMhO'#CoO:rQ`O'#EnO:}Q`O'#EyO;hQMhO'#FeO;xQ`O'#GsOOQO'#K`'#K`O;}Q`O'#K`O<]Q`O'#G{O<]Q`O'#G|O<]Q`O'#HOO9kQ`O'#HRO=SQ`O'#HUO>kQ`O'#CeO>{Q`O'#HcO?TQ`O'#HiO?TQ`O'#HkO`QlO'#HmO?TQ`O'#HoO?TQ`O'#HrO?YQ`O'#HxO?_Q!0LsO'#IOO%[QlO'#IQO?jQ!0LsO'#ISO?uQ!0LsO'#IUO9uQ!0LrO'#IWO@QQ!0MxO'#CiOASQpO'#DlQOQ`OOO%[QlO'#EQOAjQ`O'#ETO:dQMhO'#EnOAuQ`O'#EnOBQQ!bO'#FeOOQ['#Cg'#CgOOQ!0Lb'#Dq'#DqOOQ!0Lb'#Jv'#JvO%[QlO'#JvOOQO'#Jy'#JyOOQO'#Ih'#IhOCQQpO'#EgOOQ!0Lb'#Ef'#EfOOQ!0Lb'#J}'#J}OC|Q!0MSO'#EgODWQpO'#EWOOQO'#Jx'#JxODlQpO'#JyOEyQpO'#EWODWQpO'#EgPFWO&2DjO'#CbPOOO)CD})CD}OOOO'#I`'#I`OFcO#tO,59UOOQ!0Lh,59U,59UOOOO'#Ia'#IaOFqO&jO,59UOGPQ!L^O'#DcOOOO'#Ic'#IcOGWO#@ItO,59{OOQ!0Lf,59{,59{OGfQlO'#IdOGyQ`O'#JtOIxQ!fO'#JtO+}QlO'#JtOJPQ`O,5:ROJgQ`O'#EpOJtQ`O'#KTOKPQ`O'#KSOKPQ`O'#KSOKXQ`O,5;^OK^Q`O'#KROOQ!0Ln,5:^,5:^OKeQlO,5:^OMcQ!0MxO,5:fONSQ`O,5:nONmQ!0LrO'#KQONtQ`O'#KPO9eQ`O'#KPO! YQ`O'#KPO! bQ`O,5;]O! gQ`O'#KPO!#lQ!fO'#JsOOQ!0Lh'#Ci'#CiO%[QlO'#ESO!$[Q!fO,5:sOOQS'#Jz'#JzOOQO-EtOOQ['#Jh'#JhOOQ[,5>u,5>uOOQ[-E<]-E<]O!TO`QlO,5>VO!LOQ`O,5>XO`QlO,5>ZO!LTQ`O,5>^O!LYQlO,5>dOOQ[,5>j,5>jO%[QlO,5>jO9uQ!0LrO,5>lOOQ[,5>n,5>nO#!dQ`O,5>nOOQ[,5>p,5>pO#!dQ`O,5>pOOQ[,5>r,5>rO##QQpO'#D_O%[QlO'#JvO##sQpO'#JvO##}QpO'#DmO#$`QpO'#DmO#&qQlO'#DmO#&xQ`O'#JuO#'QQ`O,5:WO#'VQ`O'#EtO#'eQ`O'#KUO#'mQ`O,5;_O#'rQpO'#DmO#(PQpO'#EVOOQ!0Lf,5:o,5:oO%[QlO,5:oO#(WQ`O,5:oO?YQ`O,5;YO!CUQpO,5;YO!C^QMhO,5;YO:dQMhO,5;YO#(`Q`O,5@bO#(eQ07dO,5:sOOQO-EPO$6^Q`O,5>POOQ[1G3i1G3iO`QlO1G3iOOQ[1G3o1G3oOOQ[1G3q1G3qO?TQ`O1G3sO$6cQlO1G3uO$:gQlO'#HtOOQ[1G3x1G3xO$:tQ`O'#HzO?YQ`O'#H|OOQ[1G4O1G4OO$:|QlO1G4OO9uQ!0LrO1G4UOOQ[1G4W1G4WOOQ!0Lb'#G_'#G_O9uQ!0LrO1G4YO9uQ!0LrO1G4[O$?TQ`O,5@bO!)[QlO,5;`O9eQ`O,5;`O?YQ`O,5:XO!)[QlO,5:XO!CUQpO,5:XO$?YQ?MtO,5:XOOQO,5;`,5;`O$?dQpO'#IeO$?zQ`O,5@aOOQ!0Lf1G/r1G/rO$@SQpO'#IkO$@^Q`O,5@pOOQ!0Lb1G0y1G0yO#$`QpO,5:XOOQO'#Ig'#IgO$@fQpO,5:qOOQ!0Ln,5:q,5:qO#(ZQ`O1G0ZOOQ!0Lf1G0Z1G0ZO%[QlO1G0ZOOQ!0Lf1G0t1G0tO?YQ`O1G0tO!CUQpO1G0tO!C^QMhO1G0tOOQ!0Lb1G5|1G5|O!ByQ!0LrO1G0^OOQO1G0m1G0mO%[QlO1G0mO$@mQ!0LrO1G0mO$@xQ!0LrO1G0mO!CUQpO1G0^ODWQpO1G0^O$AWQ!0LrO1G0mOOQO1G0^1G0^O$AlQ!0MxO1G0mPOOO-E<[-E<[POOO1G.h1G.hOOOO1G/i1G/iO$AvQ!bO,5QQpO,5@}OOQ!0Lb1G3c1G3cOOQ[7+$V7+$VO@zQ`O7+$VO9uQ!0LrO7+$VO%>]Q`O7+$VO%[QlO1G6lO%[QlO1G6mO%>bQ!0LrO1G6lO%>lQlO1G3kO%>sQ`O1G3kO%>xQlO1G3kOOQ[7+)T7+)TO9uQ!0LrO7+)_O`QlO7+)aOOQ['#Kh'#KhOOQ['#JS'#JSO%?PQlO,5>`OOQ[,5>`,5>`O%[QlO'#HuO%?^Q`O'#HwOOQ[,5>f,5>fO9eQ`O,5>fOOQ[,5>h,5>hOOQ[7+)j7+)jOOQ[7+)p7+)pOOQ[7+)t7+)tOOQ[7+)v7+)vO%?cQpO1G5|O%?}Q?MtO1G0zO%@XQ`O1G0zOOQO1G/s1G/sO%@dQ?MtO1G/sO?YQ`O1G/sO!)[QlO'#DmOOQO,5?P,5?POOQO-ERQ`O7+,WO&>WQ`O7+,XO%[QlO7+,WO%[QlO7+,XOOQ[7+)V7+)VO&>]Q`O7+)VO&>bQlO7+)VO&>iQ`O7+)VOOQ[<nQ`O,5>aOOQ[,5>c,5>cO&>sQ`O1G4QO9eQ`O7+&fO!)[QlO7+&fOOQO7+%_7+%_O&>xQ?MtO1G6ZO?YQ`O7+%_OOQ!0Lf<yQ?MvO,5?aO'@|Q?MvO,5?cO'CPQ?MvO7+'|O'DuQMjOG27TOOQO<VO!l$xO#jROe!iOpkOrPO(T)]O(VTO(YUO(aVO(o[O~O!]$_Oa$qa'z$qa'w$qa!k$qa!Y$qa!_$qa%i$qa!g$qa~Ol)dO~P!&zOh%VOp%WOr%XOs$tOt$tOz%YO|%ZO!O%]O!S${O!_$|O!i%bO!l$xO#j%cO$W%`O$t%^O$v%_O$y%aO(T(vO(VTO(YUO(a$uO(y$}O(z%PO~Og(pP~P!,TO!Q)iO!g)hO!_$^X$Z$^X$]$^X$_$^X$f$^X~O!g)hO!_({X$Z({X$]({X$_({X$f({X~O!Q)iO~P!.^O!Q)iO!_({X$Z({X$]({X$_({X$f({X~O!_)kO$Z)oO$])jO$_)jO$f)pO~O![)sO~P!)[O$]$hO$_$gO$f)wO~On$zX!Q$zX#S$zX'y$zX(y$zX(z$zX~OgmXg$zXnmX!]mX#`mX~P!0SOx)yO(b)zO(c)|O~On*VO!Q*OO'y*PO(y$}O(z%PO~Og)}O~P!1WOg*WO~Oh%VOr%XOs$tOt$tOz%YO|%ZO!OVO!l$xO#jVO!l$xO#jROe!iOpkOrPO(VTO(YUO(aVO(o[O~O(T=QO~P#$qO!]-]O!^(iX~O!^-_O~O!g-VO#`-UO!]#hX!^#hX~O!]-`O!^(xX~O!^-bO~O!c-cO!d-cO(U!lO~P#$`O!^-fO~P'_On-iO!_'`O~O!Y-nO~Os!{a!b!{a!c!{a!d!{a#T!{a#U!{a#V!{a#W!{a#X!{a#[!{a#]!{a(U!{a(V!{a(Y!{a(e!{a(o!{a~P!#vO!p-sO#`-qO~PChO!c-uO!d-uO(U!lO~PDWOa%nO#`-qO'z%nO~Oa%nO!g#vO#`-qO'z%nO~Oa%nO!g#vO!p-sO#`-qO'z%nO(r'pO~O(P'xO(Q'xO(R-zO~Ov-{O~O!Y'Wa!]'Wa~P!:tO![.PO!Y'WX!]'WX~P%[O!](VO!Y(ha~O!Y(ha~PHRO!](^O!Y(va~O!S%hO![.TO!_%iO(T%gO!Y'^X!]'^X~O#`.VO!](ta!k(taa(ta'z(ta~O!g#vO~P#,wO!](jO!k(sa~O!S%hO!_%iO#j.ZO(T%gO~Op.`O!S%hO![.]O!_%iO!|]O#i._O#j.]O(T%gO!]'aX!k'aX~OR.dO!l#xO~Oh%VOn.gO!_'`O%i.fO~Oa#ci!]#ci'z#ci'w#ci!Y#ci!k#civ#ci!_#ci%i#ci!g#ci~P!:tOn>]O!Q*OO'y*PO(y$}O(z%PO~O#k#_aa#_a#`#_a'z#_a!]#_a!k#_a!_#_a!Y#_a~P#/sO#k(`XP(`XR(`X[(`Xa(`Xj(`Xr(`X!S(`X!l(`X!p(`X#R(`X#n(`X#o(`X#p(`X#q(`X#r(`X#s(`X#t(`X#u(`X#v(`X#x(`X#z(`X#{(`X'z(`X(a(`X(r(`X!k(`X!Y(`X'w(`Xv(`X!_(`X%i(`X!g(`X~P!6kO!].tO!k(kX~P!:tO!k.wO~O!Y.yO~OP$[OR#zO!Q#yO!S#{O!l#xO!p$[O(aVO[#mia#mij#mir#mi!]#mi#R#mi#o#mi#p#mi#q#mi#r#mi#s#mi#t#mi#u#mi#v#mi#x#mi#z#mi#{#mi'z#mi(r#mi(y#mi(z#mi'w#mi!Y#mi!k#miv#mi!_#mi%i#mi!g#mi~O#n#mi~P#3cO#n$OO~P#3cOP$[OR#zOr$aO!Q#yO!S#{O!l#xO!p$[O#n$OO#o$PO#p$PO#q$PO(aVO[#mia#mij#mi!]#mi#R#mi#s#mi#t#mi#u#mi#v#mi#x#mi#z#mi#{#mi'z#mi(r#mi(y#mi(z#mi'w#mi!Y#mi!k#miv#mi!_#mi%i#mi!g#mi~O#r#mi~P#6QO#r$QO~P#6QOP$[OR#zO[$cOj$ROr$aO!Q#yO!S#{O!l#xO!p$[O#R$RO#n$OO#o$PO#p$PO#q$PO#r$QO#s$RO#t$RO#u$bO(aVOa#mi!]#mi#x#mi#z#mi#{#mi'z#mi(r#mi(y#mi(z#mi'w#mi!Y#mi!k#miv#mi!_#mi%i#mi!g#mi~O#v#mi~P#8oOP$[OR#zO[$cOj$ROr$aO!Q#yO!S#{O!l#xO!p$[O#R$RO#n$OO#o$PO#p$PO#q$PO#r$QO#s$RO#t$RO#u$bO#v$SO(aVO(z#}Oa#mi!]#mi#z#mi#{#mi'z#mi(r#mi(y#mi'w#mi!Y#mi!k#miv#mi!_#mi%i#mi!g#mi~O#x$UO~P#;VO#x#mi~P#;VO#v$SO~P#8oOP$[OR#zO[$cOj$ROr$aO!Q#yO!S#{O!l#xO!p$[O#R$RO#n$OO#o$PO#p$PO#q$PO#r$QO#s$RO#t$RO#u$bO#v$SO#x$UO(aVO(y#|O(z#}Oa#mi!]#mi#{#mi'z#mi(r#mi'w#mi!Y#mi!k#miv#mi!_#mi%i#mi!g#mi~O#z#mi~P#={O#z$WO~P#={OP]XR]X[]Xj]Xr]X!Q]X!S]X!l]X!p]X#R]X#S]X#`]X#kfX#n]X#o]X#p]X#q]X#r]X#s]X#t]X#u]X#v]X#x]X#z]X#{]X$Q]X(a]X(r]X(y]X(z]X!]]X!^]X~O$O]X~P#@jOP$[OR#zO[]O!Q*OO'y*PO(y$}O(z%POP#miR#mi!S#mi!l#mi!p#mi#n#mi#o#mi#p#mi#q#mi(a#mi~P#EyO!]/POg(pX~P!1WOg/RO~Oa$Pi!]$Pi'z$Pi'w$Pi!Y$Pi!k$Piv$Pi!_$Pi%i$Pi!g$Pi~P!:tO$]/SO$_/SO~O$]/TO$_/TO~O!g)hO#`/UO!_$cX$Z$cX$]$cX$_$cX$f$cX~O![/VO~O!_)kO$Z/XO$])jO$_)jO$f/YO~O!]VO!l$xO#j^O!Q*OO'y*PO(y$}O(z%POP#miR#mi!S#mi!l#mi!p#mi#n#mi#o#mi#p#mi#q#mi(a#mi~P&,QO#S$dOP(`XR(`X[(`Xj(`Xn(`Xr(`X!Q(`X!S(`X!l(`X!p(`X#R(`X#n(`X#o(`X#p(`X#q(`X#r(`X#s(`X#t(`X#u(`X#v(`X#x(`X#z(`X#{(`X$O(`X'y(`X(a(`X(r(`X(y(`X(z(`X!](`X!^(`X~O$O$Pi!]$Pi!^$Pi~P#BwO$O!ri!^!ri~P$+oOg']a!]']a~P!1WO!^7nO~O!]'da!^'da~P#BwO!Y7oO~P#/sO!g#vO(r'pO!]'ea!k'ea~O!]/pO!k)Oi~O!]/pO!g#vO!k)Oi~Og$|q!]$|q#`$|q$O$|q~P!1WO!Y'ga!]'ga~P#/sO!g7vO~O!]/yO!Y)Pi~P#/sO!]/yO!Y)Pi~O!Y7yO~Oh%VOr8OO!l%eO(r'pO~Oj8QO!g#vO~Or8TO!g#vO(r'pO~O!Q*OO'y*PO(z%POn'ja(y'ja!]'ja#`'ja~Og'ja$O'ja~P&5RO!Q*OO'y*POn'la(y'la(z'la!]'la#`'la~Og'la$O'la~P&5tOg(_q!](_q~P!1WO#`8VOg(_q!](_q~P!1WO!Y8WO~Og%Oq!]%Oq#`%Oq$O%Oq~P!1WOa$oy!]$oy'z$oy'w$oy!Y$oy!k$oyv$oy!_$oy%i$oy!g$oy~P!:tO!g6rO~O!]5[O!_)Qa~O!_'`OP$TaR$Ta[$Taj$Tar$Ta!Q$Ta!S$Ta!]$Ta!l$Ta!p$Ta#R$Ta#n$Ta#o$Ta#p$Ta#q$Ta#r$Ta#s$Ta#t$Ta#u$Ta#v$Ta#x$Ta#z$Ta#{$Ta(a$Ta(r$Ta(y$Ta(z$Ta~O%i7WO~P&8fO%^8[Oa%[i!_%[i'z%[i!]%[i~Oa#cy!]#cy'z#cy'w#cy!Y#cy!k#cyv#cy!_#cy%i#cy!g#cy~P!:tO[8^O~Ob8`O(T+qO(VTO(YUO~O!]1TO!^)Xi~O`8dO~O(e(|O!]'pX!^'pX~O!]5uO!^)Ua~O!^8nO~P%;eO(o!sO~P$&YO#[8oO~O!_1oO~O!_1oO%i8qO~On8tO!_1oO%i8qO~O[8yO!]'sa!^'sa~O!]1zO!^)Vi~O!k8}O~O!k9OO~O!k9RO~O!k9RO~P%[Oa9TO~O!g9UO~O!k9VO~O!](wi!^(wi~P#BwOa%nO#`9_O'z%nO~O!](ty!k(tya(ty'z(ty~P!:tO!](jO!k(sy~O%i9bO~P&8fO!_'`O%i9bO~O#k$|qP$|qR$|q[$|qa$|qj$|qr$|q!S$|q!]$|q!l$|q!p$|q#R$|q#n$|q#o$|q#p$|q#q$|q#r$|q#s$|q#t$|q#u$|q#v$|q#x$|q#z$|q#{$|q'z$|q(a$|q(r$|q!k$|q!Y$|q'w$|q#`$|qv$|q!_$|q%i$|q!g$|q~P#/sO#k'jaP'jaR'ja['jaa'jaj'jar'ja!S'ja!l'ja!p'ja#R'ja#n'ja#o'ja#p'ja#q'ja#r'ja#s'ja#t'ja#u'ja#v'ja#x'ja#z'ja#{'ja'z'ja(a'ja(r'ja!k'ja!Y'ja'w'jav'ja!_'ja%i'ja!g'ja~P&5RO#k'laP'laR'la['laa'laj'lar'la!S'la!l'la!p'la#R'la#n'la#o'la#p'la#q'la#r'la#s'la#t'la#u'la#v'la#x'la#z'la#{'la'z'la(a'la(r'la!k'la!Y'la'w'lav'la!_'la%i'la!g'la~P&5tO#k%OqP%OqR%Oq[%Oqa%Oqj%Oqr%Oq!S%Oq!]%Oq!l%Oq!p%Oq#R%Oq#n%Oq#o%Oq#p%Oq#q%Oq#r%Oq#s%Oq#t%Oq#u%Oq#v%Oq#x%Oq#z%Oq#{%Oq'z%Oq(a%Oq(r%Oq!k%Oq!Y%Oq'w%Oq#`%Oqv%Oq!_%Oq%i%Oq!g%Oq~P#/sO!]'Yi!k'Yi~P!:tO$O#cq!]#cq!^#cq~P#BwO(y$}OP%aaR%aa[%aaj%aar%aa!S%aa!l%aa!p%aa#R%aa#n%aa#o%aa#p%aa#q%aa#r%aa#s%aa#t%aa#u%aa#v%aa#x%aa#z%aa#{%aa$O%aa(a%aa(r%aa!]%aa!^%aa~On%aa!Q%aa'y%aa(z%aa~P&IyO(z%POP%caR%ca[%caj%car%ca!S%ca!l%ca!p%ca#R%ca#n%ca#o%ca#p%ca#q%ca#r%ca#s%ca#t%ca#u%ca#v%ca#x%ca#z%ca#{%ca$O%ca(a%ca(r%ca!]%ca!^%ca~On%ca!Q%ca'y%ca(y%ca~P&LQOn>^O!Q*OO'y*PO(z%PO~P&IyOn>^O!Q*OO'y*PO(y$}O~P&LQOR0kO!Q0kO!S0lO#S$dOP}a[}aj}an}ar}a!l}a!p}a#R}a#n}a#o}a#p}a#q}a#r}a#s}a#t}a#u}a#v}a#x}a#z}a#{}a$O}a'y}a(a}a(r}a(y}a(z}a!]}a!^}a~O!Q*OO'y*POP$saR$sa[$saj$san$sar$sa!S$sa!l$sa!p$sa#R$sa#n$sa#o$sa#p$sa#q$sa#r$sa#s$sa#t$sa#u$sa#v$sa#x$sa#z$sa#{$sa$O$sa(a$sa(r$sa(y$sa(z$sa!]$sa!^$sa~O!Q*OO'y*POP$uaR$ua[$uaj$uan$uar$ua!S$ua!l$ua!p$ua#R$ua#n$ua#o$ua#p$ua#q$ua#r$ua#s$ua#t$ua#u$ua#v$ua#x$ua#z$ua#{$ua$O$ua(a$ua(r$ua(y$ua(z$ua!]$ua!^$ua~On>^O!Q*OO'y*PO(y$}O(z%PO~OP%TaR%Ta[%Taj%Tar%Ta!S%Ta!l%Ta!p%Ta#R%Ta#n%Ta#o%Ta#p%Ta#q%Ta#r%Ta#s%Ta#t%Ta#u%Ta#v%Ta#x%Ta#z%Ta#{%Ta$O%Ta(a%Ta(r%Ta!]%Ta!^%Ta~P''VO$O$mq!]$mq!^$mq~P#BwO$O$oq!]$oq!^$oq~P#BwO!^9oO~O$O9pO~P!1WO!g#vO!]'ei!k'ei~O!g#vO(r'pO!]'ei!k'ei~O!]/pO!k)Oq~O!Y'gi!]'gi~P#/sO!]/yO!Y)Pq~Or9wO!g#vO(r'pO~O[9yO!Y9xO~P#/sO!Y9xO~Oj:PO!g#vO~Og(_y!](_y~P!1WO!]'na!_'na~P#/sOa%[q!_%[q'z%[q!]%[q~P#/sO[:UO~O!]1TO!^)Xq~O`:YO~O#`:ZO!]'pa!^'pa~O!]5uO!^)Ui~P#BwO!S:]O~O!_1oO%i:`O~O(VTO(YUO(e:eO~O!]1zO!^)Vq~O!k:hO~O!k:iO~O!k:jO~O!k:jO~P%[O#`:mO!]#hy!^#hy~O!]#hy!^#hy~P#BwO%i:rO~P&8fO!_'`O%i:rO~O$O#|y!]#|y!^#|y~P#BwOP$|iR$|i[$|ij$|ir$|i!S$|i!l$|i!p$|i#R$|i#n$|i#o$|i#p$|i#q$|i#r$|i#s$|i#t$|i#u$|i#v$|i#x$|i#z$|i#{$|i$O$|i(a$|i(r$|i!]$|i!^$|i~P''VO!Q*OO'y*PO(z%POP'iaR'ia['iaj'ian'iar'ia!S'ia!l'ia!p'ia#R'ia#n'ia#o'ia#p'ia#q'ia#r'ia#s'ia#t'ia#u'ia#v'ia#x'ia#z'ia#{'ia$O'ia(a'ia(r'ia(y'ia!]'ia!^'ia~O!Q*OO'y*POP'kaR'ka['kaj'kan'kar'ka!S'ka!l'ka!p'ka#R'ka#n'ka#o'ka#p'ka#q'ka#r'ka#s'ka#t'ka#u'ka#v'ka#x'ka#z'ka#{'ka$O'ka(a'ka(r'ka(y'ka(z'ka!]'ka!^'ka~O(y$}OP%aiR%ai[%aij%ain%air%ai!Q%ai!S%ai!l%ai!p%ai#R%ai#n%ai#o%ai#p%ai#q%ai#r%ai#s%ai#t%ai#u%ai#v%ai#x%ai#z%ai#{%ai$O%ai'y%ai(a%ai(r%ai(z%ai!]%ai!^%ai~O(z%POP%ciR%ci[%cij%cin%cir%ci!Q%ci!S%ci!l%ci!p%ci#R%ci#n%ci#o%ci#p%ci#q%ci#r%ci#s%ci#t%ci#u%ci#v%ci#x%ci#z%ci#{%ci$O%ci'y%ci(a%ci(r%ci(y%ci!]%ci!^%ci~O$O$oy!]$oy!^$oy~P#BwO$O#cy!]#cy!^#cy~P#BwO!g#vO!]'eq!k'eq~O!]/pO!k)Oy~O!Y'gq!]'gq~P#/sOr:|O!g#vO(r'pO~O[;QO!Y;PO~P#/sO!Y;PO~Og(_!R!](_!R~P!1WOa%[y!_%[y'z%[y!]%[y~P#/sO!]1TO!^)Xy~O!]5uO!^)Uq~O(T;XO~O!_1oO%i;[O~O!k;_O~O%i;dO~P&8fOP$|qR$|q[$|qj$|qr$|q!S$|q!l$|q!p$|q#R$|q#n$|q#o$|q#p$|q#q$|q#r$|q#s$|q#t$|q#u$|q#v$|q#x$|q#z$|q#{$|q$O$|q(a$|q(r$|q!]$|q!^$|q~P''VO!Q*OO'y*PO(z%POP'jaR'ja['jaj'jan'jar'ja!S'ja!l'ja!p'ja#R'ja#n'ja#o'ja#p'ja#q'ja#r'ja#s'ja#t'ja#u'ja#v'ja#x'ja#z'ja#{'ja$O'ja(a'ja(r'ja(y'ja!]'ja!^'ja~O!Q*OO'y*POP'laR'la['laj'lan'lar'la!S'la!l'la!p'la#R'la#n'la#o'la#p'la#q'la#r'la#s'la#t'la#u'la#v'la#x'la#z'la#{'la$O'la(a'la(r'la(y'la(z'la!]'la!^'la~OP%OqR%Oq[%Oqj%Oqr%Oq!S%Oq!l%Oq!p%Oq#R%Oq#n%Oq#o%Oq#p%Oq#q%Oq#r%Oq#s%Oq#t%Oq#u%Oq#v%Oq#x%Oq#z%Oq#{%Oq$O%Oq(a%Oq(r%Oq!]%Oq!^%Oq~P''VOg%e!Z!]%e!Z#`%e!Z$O%e!Z~P!1WO!Y;hO~P#/sOr;iO!g#vO(r'pO~O[;kO!Y;hO~P#/sO!]'pq!^'pq~P#BwO!]#h!Z!^#h!Z~P#BwO#k%e!ZP%e!ZR%e!Z[%e!Za%e!Zj%e!Zr%e!Z!S%e!Z!]%e!Z!l%e!Z!p%e!Z#R%e!Z#n%e!Z#o%e!Z#p%e!Z#q%e!Z#r%e!Z#s%e!Z#t%e!Z#u%e!Z#v%e!Z#x%e!Z#z%e!Z#{%e!Z'z%e!Z(a%e!Z(r%e!Z!k%e!Z!Y%e!Z'w%e!Z#`%e!Zv%e!Z!_%e!Z%i%e!Z!g%e!Z~P#/sOr;tO!g#vO(r'pO~O!Y;uO~P#/sOr;|O!g#vO(r'pO~O!Y;}O~P#/sOP%e!ZR%e!Z[%e!Zj%e!Zr%e!Z!S%e!Z!l%e!Z!p%e!Z#R%e!Z#n%e!Z#o%e!Z#p%e!Z#q%e!Z#r%e!Z#s%e!Z#t%e!Z#u%e!Z#v%e!Z#x%e!Z#z%e!Z#{%e!Z$O%e!Z(a%e!Z(r%e!Z!]%e!Z!^%e!Z~P''VOrROe!iOpkOrPO(T)]O(VTO(YUO(aVO(o[O~O!]WO!l$xO#jgPPP!>oI[PPPPPPPPP!BOP!C]PPI[!DnPI[PI[I[I[I[I[PI[!FQP!I[P!LbP!Lf!Lp!Lt!LtP!IXP!Lx!LxP#!OP#!SI[PI[#!Y#%_CjA^PA^PA^A^P#&lA^A^#)OA^#+vA^#.SA^A^#.r#1W#1W#1]#1f#1W#1qPP#1WPA^#2ZA^#6YA^A^6mPPP#:_PPP#:x#:xP#:xP#;`#:xPP#;fP#;]P#;]#;y#;]#P#>V#>]#>k#>q#>{#?R#?]#?c#?s#?y#@k#@}#AT#AZ#Ai#BO#Cs#DR#DY#Et#FS#Gt#HS#HY#H`#Hf#Hp#Hv#H|#IW#Ij#IpPPPPPPPPPPP#IvPPPPPPP#Jk#Mx$ b$ i$ qPPP$']P$'f$*_$0x$0{$1O$1}$2Q$2X$2aP$2g$2jP$3W$3[$4S$5b$5g$5}PP$6S$6Y$6^$6a$6e$6i$7e$7|$8e$8i$8l$8o$8y$8|$9Q$9UR!|RoqOXst!Z#d%m&r&t&u&w,s,x2[2_Y!vQ'`-e1o5{Q%tvQ%|yQ&T|Q&j!VS'W!e-]Q'f!iS'l!r!yU*k$|*Z*oQ+o%}S+|&V&WQ,d&dQ-c'_Q-m'gQ-u'mQ0[*qQ1b,OQ1y,eR<{SU+P%]S!S!nQ!r!v!y!z$|'W'_'`'l'm'n*k*o*q*r-]-c-e-u0[0_1o5{5}%[$ti#v$b$c$d$x${%O%Q%^%_%c)y*R*T*V*Y*a*g*w*x+f+i,S,V.f/P/d/m/x/y/{0`0b0i0j0o1f1i1q3c4^4_4j4o5Q5[5_6S7W7v8Q8V8[8q9b9p9y:P:`:r;Q;[;d;kP>X>Y>]>^Q&X|Q'U!eS'[%i-`Q+t&PQ,P&WQ,f&gQ0n+SQ1Y+uQ1_+{Q2Q,jQ2R,kQ5f1TQ5o1aQ6[1zQ6_1|Q6`2PQ8`5gQ8c5lQ8|6bQ:X8dQ:f8yQ;V:YR<}*ZrnOXst!V!Z#d%m&i&r&t&u&w,s,x2[2_R,h&k&z^OPXYstuvwz!Z!`!g!j!o#S#d#o#u#x#{$O$P$Q$R$S$T$U$V$W$X$Z$_$a$e$n%m%t&R&k&n&o&r&t&u&w&{'T'b'r(V(](d(x(z)O)s)}*i+X+]+g,p,s,x-U-X-i-q.P.V.g.t.{/V/n0]0l0r1S1r2S2T2V2X2[2_2a2p3Q3W3d3l4T4z5w6T6e6f6i6s6|7[8t9T9_:Z:mR>S[#]WZ#W#Z'X(T!b%jm#h#i#l$x%e%h(^(h(i(j*Y*^*b+Z+[+^,o-V.T.Z.[.]._/m/p2d3[3]4a6r7TQ%wxQ%{yW&Q|&V&W,OQ&_!TQ'c!hQ'e!iQ(q#sS+n%|%}Q+r&PQ,_&bQ,c&dS-l'f'gQ.i(rQ1R+oQ1X+uQ1Z+vQ1^+zQ1t,`S1x,d,eQ2|-mQ5e1TQ5i1WQ5n1`Q6Z1yQ8_5gQ8b5kQ8f5pQ:T8^R;T:U!U$zi$d%O%Q%^%_%c*R*T*a*w*x/P/x0`0b0i0j0o4_5Q8V9p>P>X>Y!^%yy!i!u%{%|%}'V'e'f'g'k'u*j+n+o-Y-l-m-t0R0U1R2u2|3T4r4s4v7}9{Q+h%wQ,T&[Q,W&]Q,b&dQ.h(qQ1s,_U1w,c,d,eQ3e.iQ6U1tS6Y1x1yQ8x6Z#f>T#v$b$c$x${)y*V*Y*g+f+i,S,V.f/d/m/y/{1f1i1q3c4^4j4o5[5_6S7W7v8Q8[8q9b9y:P:`:r;Q;[;d;k]>^o>UPS&[!Q&iQ&]!RQ&^!SU*}%[%d=sR,R&Y%]%Si#v$b$c$d$x${%O%Q%^%_%c)y*R*T*V*Y*a*g*w*x+f+i,S,V.f/P/d/m/x/y/{0`0b0i0j0o1f1i1q3c4^4_4j4o5Q5[5_6S7W7v8Q8V8[8q9b9p9y:P:`:r;Q;[;d;kP>X>Y>]>^T)z$u){V+P%]S$i$^c#Y#e%q%s%u(S(Y(t(y)R)S)T)U)V)W)X)Y)Z)[)^)`)b)g)q+d+x-Z-x-}.S.U.s.v.z.|.}/O/b0p2k2n3O3V3k3p3q3r3s3t3u3v3w3x3y3z3{3|4P4Q4X5X5c6u6{7Q7a7b7k7l8k9X9]9g9m9n:o;W;`SQ'Y!eR2q-]!W!nQ!e!r!v!y!z$|'W'_'`'l'm'n*Z*k*o*q*r-]-c-e-u0[0_1o5{5}R1l,ZnqOXst!Z#d%m&r&t&u&w,s,x2[2_Q&y!^Q'v!xS(s#u<^Q+l%zQ,]&_Q,^&aQ-j'dQ-w'oS.r(x=PS0q+X=ZQ1P+mQ1n,[Q2c,zQ2e,{Q2m-WQ2z-kQ2}-oS5Y0r=eQ5a1QS5d1S=fQ6t2oQ6x2{Q6}3SQ8]5bQ9Y6vQ9Z6yQ9^7OR:l9V$d$]c#Y#e%s%u(S(Y(t(y)R)S)T)U)V)W)X)Y)Z)[)^)`)b)g)q+d+x-Z-x-}.S.U.s.v.z.}/O/b0p2k2n3O3V3k3p3q3r3s3t3u3v3w3x3y3z3{3|4P4Q4X5X5c6u6{7Q7a7b7k7l8k9X9]9g9m9n:o;W;`SS#q]SU$fd)_,mS(p#p'iU*v%R(w4OU0m+O.n7gQ5^0xQ7V3`Q9d7YR:s9em!tQ!r!v!y!z'`'l'm'n-e-u1o5{5}Q't!uS(f#g2US-s'k'wQ/s*]Q0R*jQ3U-vQ4f/tQ4r0TQ4s0UQ4x0^Q7r4`S7}4t4vS8R4y4{Q9r7sQ9v7yQ9{8OQ:Q8TS:{9w9xS;g:|;PS;s;h;iS;{;t;uSSR=o>R%^bOPWXYZstuvw!Z!`!g!o#S#W#Z#d#o#u#x#{$O$P$Q$R$S$T$U$V$W$X$_$a$e%m%t&R&k&n&o&r&t&u&w&{'T'b'r(T(V(](d(x(z)O)}*i+X+]+g,p,s,x-i-q.P.V.g.t.{/n0]0l0r1S1r2S2T2V2X2[2_2a3Q3W3d3l4z6T6e6f6i6|7[8t9T9_Q%fj!^%xy!i!u%{%|%}'V'e'f'g'k'u*j+n+o-Y-l-m-t0R0U1R2u2|3T4r4s4v7}9{S&Oz!jQ+k%yQ,a&dW1v,b,c,d,eU6X1w1x1yS8w6Y6ZQ:d8x!r=j$Z$n'X)s-U-X/V2p4T5w6s:Z:mSQ=t>QR=u>R%QeOPXYstuvw!Z!`!g!o#S#d#o#u#x#{$O$P$Q$R$S$T$U$V$W$X$_$a$e%m%t&R&k&n&r&t&u&w&{'T'b'r(V(](d(x(z)O)}*i+X+]+g,p,s,x-i-q.P.V.g.t.{/n0]0l0r1S1r2S2T2V2X2[2_2a3Q3W3d3l4z6T6e6f6i6|7[8t9T9_Y#bWZ#W#Z(T!b%jm#h#i#l$x%e%h(^(h(i(j*Y*^*b+Z+[+^,o-V.T.Z.[.]._/m/p2d3[3]4a6r7TQ,n&o!p=k$Z$n)s-U-X/V2p4T5w6s:Z:mSR=n'XU']!e%i*ZR2s-`%SdOPWXYZstuvw!Z!`!g!o#S#W#Z#d#o#u#x#{$O$P$Q$R$S$T$U$V$W$X$_$a$e%m%t&R&k&n&r&t&u&w&{'T'b'r(T(V(](d(x(z)O)}*i+X+],p,s,x-i-q.P.V.t.{/n0]0l0r1S1r2S2T2V2X2[2_2a3Q3W3l4z6T6e6f6i6|8t9T9_!r)_$Z$n'X)s-U-X/V2p4T5w6s:Z:mSQ,m&oQ0x+gQ3`.gQ7Y3dR9e7[!b$Tc#Y%q(S(Y(t(y)Z)[)`)g+x-x-}.S.U.s.v/b0p3O3V3k3{5X5c6{7Q7a9]:oS)^)q-Z.|2k2n3p4P4X6u7b7k7l8k9X9g9m9n;W;`=vQ>X>ZR>Y>['QkOPWXYZstuvw!Z!`!g!o#S#W#Z#d#o#u#x#{$O$P$Q$R$S$T$U$V$W$X$Z$_$a$e$n%m%t&R&k&n&o&r&t&u&w&{'T'X'b'r(T(V(](d(x(z)O)s)}*i+X+]+g,p,s,x-U-X-i-q.P.V.g.t.{/V/n0]0l0r1S1r2S2T2V2X2[2_2a2p3Q3W3d3l4T4z5w6T6e6f6i6s6|7[8t9T9_:Z:mSS$oh$pR4U/U'XgOPWXYZhstuvw!Z!`!g!o#S#W#Z#d#o#u#x#{$O$P$Q$R$S$T$U$V$W$X$Z$_$a$e$n$p%m%t&R&k&n&o&r&t&u&w&{'T'X'b'r(T(V(](d(x(z)O)s)}*i+X+]+g,p,s,x-U-X-i-q.P.V.g.t.{/U/V/n0]0l0r1S1r2S2T2V2X2[2_2a2p3Q3W3d3l4T4z5w6T6e6f6i6s6|7[8t9T9_:Z:mST$kf$qQ$ifS)j$l)nR)v$qT$jf$qT)l$l)n'XhOPWXYZhstuvw!Z!`!g!o#S#W#Z#d#o#u#x#{$O$P$Q$R$S$T$U$V$W$X$Z$_$a$e$n$p%m%t&R&k&n&o&r&t&u&w&{'T'X'b'r(T(V(](d(x(z)O)s)}*i+X+]+g,p,s,x-U-X-i-q.P.V.g.t.{/U/V/n0]0l0r1S1r2S2T2V2X2[2_2a2p3Q3W3d3l4T4z5w6T6e6f6i6s6|7[8t9T9_:Z:mST$oh$pQ$rhR)u$p%^jOPWXYZstuvw!Z!`!g!o#S#W#Z#d#o#u#x#{$O$P$Q$R$S$T$U$V$W$X$_$a$e%m%t&R&k&n&o&r&t&u&w&{'T'b'r(T(V(](d(x(z)O)}*i+X+]+g,p,s,x-i-q.P.V.g.t.{/n0]0l0r1S1r2S2T2V2X2[2_2a3Q3W3d3l4z6T6e6f6i6|7[8t9T9_!s>Q$Z$n'X)s-U-X/V2p4T5w6s:Z:mS#glOPXZst!Z!`!o#S#d#o#{$n%m&k&n&o&r&t&u&w&{'T'b)O)s*i+]+g,p,s,x-i.g/V/n0]0l1r2S2T2V2X2[2_2a3d4T4z6T6e6f6i7[8t9T!U%Ri$d%O%Q%^%_%c*R*T*a*w*x/P/x0`0b0i0j0o4_5Q8V9p>P>X>Y#f(w#v$b$c$x${)y*V*Y*g+f+i,S,V.f/d/m/y/{1f1i1q3c4^4j4o5[5_6S7W7v8Q8[8q9b9y:P:`:r;Q;[;d;k]>^Q+T%aQ/c*Oo4OP>X>YQ*c$zU*l$|*Z*oQ+U%bQ0W*m#f=q#v$b$c$x${)y*V*Y*g+f+i,S,V.f/d/m/y/{1f1i1q3c4^4j4o5[5_6S7W7v8Q8[8q9b9y:P:`:r;Q;[;d;k]>^n=rTQ=x>UQ=y>VR=z>W!U%Ri$d%O%Q%^%_%c*R*T*a*w*x/P/x0`0b0i0j0o4_5Q8V9p>P>X>Y#f(w#v$b$c$x${)y*V*Y*g+f+i,S,V.f/d/m/y/{1f1i1q3c4^4j4o5[5_6S7W7v8Q8[8q9b9y:P:`:r;Q;[;d;k]>^o4OP>X>Y>]>^Q,U&]Q1h,WQ5s1gR8h5tV*n$|*Z*oU*n$|*Z*oT5z1o5{S0P*i/nQ4w0]T8S4z:]Q+j%xQ0V*lQ1O+kQ1u,aQ6W1vQ8v6XQ:c8wR;^:d!U%Oi$d%O%Q%^%_%c*R*T*a*w*x/P/x0`0b0i0j0o4_5Q8V9p>P>X>Yx*R$v)e*S*u+V/v0d0e4R4g5R5S5W7p8U:R:x=p=}>OS0`*t0a#f]>^nZ>[`=T3}7c7f7j9h:t:w;yS=_.l3iT=`7e9k!U%Qi$d%O%Q%^%_%c*R*T*a*w*x/P/x0`0b0i0j0o4_5Q8V9p>P>X>Y|*T$v)e*U*t+V/g/v0d0e4R4g4|5R5S5W7p8U:R:x=p=}>OS0b*u0c#f]>^nZ>[d=V3}7d7e7j9h9i:t:u:w;yS=a.m3jT=b7f9lrnOXst!V!Z#d%m&i&r&t&u&w,s,x2[2_Q&f!UR,p&ornOXst!V!Z#d%m&i&r&t&u&w,s,x2[2_R&f!UQ,Y&^R1d,RsnOXst!V!Z#d%m&i&r&t&u&w,s,x2[2_Q1p,_S6R1s1tU8p6P6Q6US:_8r8sS;Y:^:aQ;m;ZR;w;nQ&m!VR,i&iR6_1|R:f8yW&Q|&V&W,OR1Z+vQ&r!WR,s&sR,y&xT2],x2_R,}&yQ,|&yR2f,}Q'y!{R-y'ySsOtQ#dXT%ps#dQ#OTR'{#OQ#RUR'}#RQ){$uR/`){Q#UVR(Q#UQ#XWU(W#X(X.QQ(X#YR.Q(YQ-^'YR2r-^Q.u(yS3m.u3nR3n.vQ-e'`R2v-eY!rQ'`-e1o5{R'j!rQ/Q)eR4S/QU#_W%h*YU(_#_(`.RQ(`#`R.R(ZQ-a']R2t-at`OXst!V!Z#d%m&i&k&r&t&u&w,s,x2[2_S#hZ%eU#r`#h.[R.[(jQ(k#jQ.X(gW.a(k.X3X7RQ3X.YR7R3YQ)n$lR/W)nQ$phR)t$pQ$`cU)a$`-|O>Z>[Q/z*eU4k/z4m7xQ4m/|R7x4lS*o$|*ZR0Y*ox*S$v)e*t*u+V/v0d0e4R4g5R5S5W7p8U:R:x=p=}>O!d.j(u)c*[*e.l.m.q/_/k/|0v1e3h4[4h4l5r7]7`7w7z8X8Z9t9|:S:};R;e;j;v>Z>[U/h*S.j7ca7c3}7e7f7j9h:t:w;yQ0a*tQ3i.lU4}0a3i9kR9k7e|*U$v)e*t*u+V/g/v0d0e4R4g4|5R5S5W7p8U:R:x=p=}>O!h.k(u)c*[*e.l.m.q/_/k/|0v1e3f3h4[4h4l5r7]7^7`7w7z8X8Z9t9|:S:};R;e;j;v>Z>[U/j*U.k7de7d3}7e7f7j9h9i:t:u:w;yQ0c*uQ3j.mU5P0c3j9lR9l7fQ*z%UR0g*zQ5]0vR8Y5]Q+_%kR0u+_Q5v1jS8j5v:[R:[8kQ,[&_R1m,[Q5{1oR8m5{Q1{,fS6]1{8zR8z6_Q1U+rW5h1U5j8a:VQ5j1XQ8a5iR:V8bQ+w&QR1[+wQ2_,xR6m2_YrOXst#dQ&v!ZQ+a%mQ,r&rQ,t&tQ,u&uQ,w&wQ2Y,sS2],x2_R6l2[Q%opQ&z!_Q&}!aQ'P!bQ'R!cQ'q!uQ+`%lQ+l%zQ,Q&XQ,h&mQ-P&|W-p'k's't'wQ-w'oQ0X*nQ1P+mQ1c,PS2O,i,lQ2g-OQ2h-RQ2i-SQ2}-oW3P-r-s-v-xQ5a1QQ5m1_Q5q1eQ6V1uQ6a2QQ6k2ZU6z3O3R3UQ6}3SQ8]5bQ8e5oQ8g5rQ8l5zQ8u6WQ8{6`S9[6{7PQ9^7OQ:W8cQ:b8vQ:g8|Q:n9]Q;U:XQ;]:cQ;a:oQ;l;VR;o;^Q%zyQ'd!iQ'o!uU+m%{%|%}Q-W'VU-k'e'f'gS-o'k'uQ0Q*jS1Q+n+oQ2o-YS2{-l-mQ3S-tS4p0R0UQ5b1RQ6v2uQ6y2|Q7O3TU7{4r4s4vQ9z7}R;O9{S$wi>PR*{%VU%Ui%V>PR0f*yQ$viS(u#v+iS)c$b$cQ)e$dQ*[$xS*e${*YQ*t%OQ*u%QQ+Q%^Q+R%_Q+V%cQ.lPQ=}>XQ>O>YQ>Z>]R>[>^Q+O%]Q.nSR#[WR'Z!el!tQ!r!v!y!z'`'l'm'n-e-u1o5{5}S'V!e-]U*j$|*Z*oS-Y'W'_S0U*k*qQ0^*rQ2u-cQ4v0[R4{0_R({#xQ!fQT-d'`-e]!qQ!r'`-e1o5{Q#p]R'i < TypeParamList in out const TypeDefinition extends ThisType this LiteralType ArithOp Number BooleanLiteral TemplateType InterpolationEnd Interpolation InterpolationStart NullType null VoidType void TypeofType typeof MemberExpression . PropertyName [ TemplateString Escape Interpolation super RegExp ] ArrayExpression Spread , } { ObjectExpression Property async get set PropertyDefinition Block : NewTarget new NewExpression ) ( ArgList UnaryExpression delete LogicOp BitOp YieldExpression yield AwaitExpression await ParenthesizedExpression ClassExpression class ClassBody MethodDeclaration Decorator @ MemberExpression PrivatePropertyName CallExpression TypeArgList CompareOp < declare Privacy static abstract override PrivatePropertyDefinition PropertyDeclaration readonly accessor Optional TypeAnnotation Equals StaticBlock FunctionExpression ArrowFunction ParamList ParamList ArrayPattern ObjectPattern PatternProperty Privacy readonly Arrow MemberExpression BinaryExpression ArithOp ArithOp ArithOp ArithOp BitOp CompareOp instanceof satisfies CompareOp BitOp BitOp BitOp LogicOp LogicOp ConditionalExpression LogicOp LogicOp AssignmentExpression UpdateOp PostfixExpression CallExpression InstantiationExpression TaggedTemplateExpression DynamicImport import ImportMeta JSXElement JSXSelfCloseEndTag JSXSelfClosingTag JSXIdentifier JSXBuiltin JSXIdentifier JSXNamespacedName JSXMemberExpression JSXSpreadAttribute JSXAttribute JSXAttributeValue JSXEscape JSXEndTag JSXOpenTag JSXFragmentTag JSXText JSXEscape JSXStartCloseTag JSXCloseTag PrefixCast < ArrowFunction TypeParamList SequenceExpression InstantiationExpression KeyofType keyof UniqueType unique ImportType InferredType infer TypeName ParenthesizedType FunctionSignature ParamList NewSignature IndexedType TupleType Label ArrayType ReadonlyType ObjectType MethodType PropertyType IndexSignature PropertyDefinition CallSignature TypePredicate asserts is NewSignature new UnionType LogicOp IntersectionType LogicOp ConditionalType ParameterizedType ClassDeclaration abstract implements type VariableDeclaration let var using TypeAliasDeclaration InterfaceDeclaration interface EnumDeclaration enum EnumBody NamespaceDeclaration namespace module AmbientDeclaration declare GlobalDeclaration global ClassDeclaration ClassBody AmbientFunctionDeclaration ExportGroup VariableName VariableName ImportDeclaration defer ImportGroup ForStatement for ForSpec ForInSpec ForOfSpec of WhileStatement while WithStatement with DoStatement do IfStatement if else SwitchStatement switch SwitchBody CaseLabel case DefaultLabel TryStatement try CatchClause catch FinallyClause finally ReturnStatement return ThrowStatement throw BreakStatement break ContinueStatement continue DebuggerStatement debugger LabeledStatement ExpressionStatement SingleExpression SingleClassItem`,maxTerm:380,context:k,nodeProps:[[`isolate`,-8,5,6,14,37,39,51,53,55,``],[`group`,-26,9,17,19,68,207,211,215,216,218,221,224,234,237,243,245,247,249,252,258,264,266,268,270,272,274,275,`Statement`,-34,13,14,32,35,36,42,51,54,55,57,62,70,72,76,80,82,84,85,110,111,120,121,136,139,141,142,143,144,145,147,148,167,169,171,`Expression`,-23,31,33,37,41,43,45,173,175,177,178,180,181,182,184,185,186,188,189,190,201,203,205,206,`Type`,-3,88,103,109,`ClassItem`],[`openedBy`,23,`<`,38,`InterpolationStart`,56,`[`,60,`{`,73,`(`,160,`JSXStartCloseTag`],[`closedBy`,-2,24,168,`>`,40,`InterpolationEnd`,50,`]`,61,`}`,74,`)`,165,`JSXEndTag`]],propSources:[xe],skippedNodes:[0,5,6,278],repeatNodeCount:37,tokenData:"$Fq07[R!bOX%ZXY+gYZ-yZ[+g[]%Z]^.c^p%Zpq+gqr/mrs3cst:_tuEruvJSvwLkwx! Yxy!'iyz!(sz{!)}{|!,q|}!.O}!O!,q!O!P!/Y!P!Q!9j!Q!R#:O!R![#<_![!]#I_!]!^#Jk!^!_#Ku!_!`$![!`!a$$v!a!b$*T!b!c$,r!c!}Er!}#O$-|#O#P$/W#P#Q$4o#Q#R$5y#R#SEr#S#T$7W#T#o$8b#o#p$x#r#s$@U#s$f%Z$f$g+g$g#BYEr#BY#BZ$A`#BZ$ISEr$IS$I_$A`$I_$I|Er$I|$I}$Dk$I}$JO$Dk$JO$JTEr$JT$JU$A`$JU$KVEr$KV$KW$A`$KW&FUEr&FU&FV$A`&FV;'SEr;'S;=`I|<%l?HTEr?HT?HU$A`?HUOEr(n%d_$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z&j&hT$i&jO!^&c!_#o&c#p;'S&c;'S;=`&w<%lO&c&j&zP;=`<%l&c'|'U]$i&j(Z!bOY&}YZ&cZw&}wx&cx!^&}!^!_'}!_#O&}#O#P&c#P#o&}#o#p'}#p;'S&};'S;=`(l<%lO&}!b(SU(Z!bOY'}Zw'}x#O'}#P;'S'};'S;=`(f<%lO'}!b(iP;=`<%l'}'|(oP;=`<%l&}'[(y]$i&j(WpOY(rYZ&cZr(rrs&cs!^(r!^!_)r!_#O(r#O#P&c#P#o(r#o#p)r#p;'S(r;'S;=`*a<%lO(rp)wU(WpOY)rZr)rs#O)r#P;'S)r;'S;=`*Z<%lO)rp*^P;=`<%l)r'[*dP;=`<%l(r#S*nX(Wp(Z!bOY*gZr*grs'}sw*gwx)rx#O*g#P;'S*g;'S;=`+Z<%lO*g#S+^P;=`<%l*g(n+dP;=`<%l%Z07[+rq$i&j(Wp(Z!b'|0/lOX%ZXY+gYZ&cZ[+g[p%Zpq+gqr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p$f%Z$f$g+g$g#BY%Z#BY#BZ+g#BZ$IS%Z$IS$I_+g$I_$JT%Z$JT$JU+g$JU$KV%Z$KV$KW+g$KW&FU%Z&FU&FV+g&FV;'S%Z;'S;=`+a<%l?HT%Z?HT?HU+g?HUO%Z07[.ST(X#S$i&j'}0/lO!^&c!_#o&c#p;'S&c;'S;=`&w<%lO&c07[.n_$i&j(Wp(Z!b'}0/lOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z)3p/x`$i&j!p),Q(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`0z!`#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z(KW1V`#v(Ch$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`2X!`#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z(KW2d_#v(Ch$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'At3l_(V':f$i&j(Z!bOY4kYZ5qZr4krs7nsw4kwx5qx!^4k!^!_8p!_#O4k#O#P5q#P#o4k#o#p8p#p;'S4k;'S;=`:X<%lO4k(^4r_$i&j(Z!bOY4kYZ5qZr4krs7nsw4kwx5qx!^4k!^!_8p!_#O4k#O#P5q#P#o4k#o#p8p#p;'S4k;'S;=`:X<%lO4k&z5vX$i&jOr5qrs6cs!^5q!^!_6y!_#o5q#o#p6y#p;'S5q;'S;=`7h<%lO5q&z6jT$d`$i&jO!^&c!_#o&c#p;'S&c;'S;=`&w<%lO&c`6|TOr6yrs7]s;'S6y;'S;=`7b<%lO6y`7bO$d``7eP;=`<%l6y&z7kP;=`<%l5q(^7w]$d`$i&j(Z!bOY&}YZ&cZw&}wx&cx!^&}!^!_'}!_#O&}#O#P&c#P#o&}#o#p'}#p;'S&};'S;=`(l<%lO&}!r8uZ(Z!bOY8pYZ6yZr8prs9hsw8pwx6yx#O8p#O#P6y#P;'S8p;'S;=`:R<%lO8p!r9oU$d`(Z!bOY'}Zw'}x#O'}#P;'S'};'S;=`(f<%lO'}!r:UP;=`<%l8p(^:[P;=`<%l4k%9[:hh$i&j(Wp(Z!bOY%ZYZ&cZq%Zqr`#P#o`x!^=^!^!_?q!_#O=^#O#P>`#P#o=^#o#p?q#p;'S=^;'S;=`@h<%lO=^&n>gXWS$i&jOY>`YZ&cZ!^>`!^!_?S!_#o>`#o#p?S#p;'S>`;'S;=`?k<%lO>`S?XSWSOY?SZ;'S?S;'S;=`?e<%lO?SS?hP;=`<%l?S&n?nP;=`<%l>`!f?xWWS(Z!bOY?qZw?qwx?Sx#O?q#O#P?S#P;'S?q;'S;=`@b<%lO?q!f@eP;=`<%l?q(Q@kP;=`<%l=^'`@w]WS$i&j(WpOY@nYZ&cZr@nrs>`s!^@n!^!_Ap!_#O@n#O#P>`#P#o@n#o#pAp#p;'S@n;'S;=`Bg<%lO@ntAwWWS(WpOYApZrAprs?Ss#OAp#O#P?S#P;'SAp;'S;=`Ba<%lOAptBdP;=`<%lAp'`BjP;=`<%l@n#WBvYWS(Wp(Z!bOYBmZrBmrs?qswBmwxApx#OBm#O#P?S#P;'SBm;'S;=`Cf<%lOBm#WCiP;=`<%lBm(rCoP;=`<%l^!Q^$i&j!X7`OY!=yYZ&cZ!P!=y!P!Q!>|!Q!^!=y!^!_!@c!_!}!=y!}#O!CW#O#P!Dy#P#o!=y#o#p!@c#p;'S!=y;'S;=`!Ek<%lO!=y|#X#Z&c#Z#[!>|#[#]&c#]#^!>|#^#a&c#a#b!>|#b#g&c#g#h!>|#h#i&c#i#j!>|#j#k!>|#k#m&c#m#n!>|#n#o&c#p;'S&c;'S;=`&w<%lO&c7`!@hX!X7`OY!@cZ!P!@c!P!Q!AT!Q!}!@c!}#O!Ar#O#P!Bq#P;'S!@c;'S;=`!CQ<%lO!@c7`!AYW!X7`#W#X!AT#Z#[!AT#]#^!AT#a#b!AT#g#h!AT#i#j!AT#j#k!AT#m#n!AT7`!AuVOY!ArZ#O!Ar#O#P!B[#P#Q!@c#Q;'S!Ar;'S;=`!Bk<%lO!Ar7`!B_SOY!ArZ;'S!Ar;'S;=`!Bk<%lO!Ar7`!BnP;=`<%l!Ar7`!BtSOY!@cZ;'S!@c;'S;=`!CQ<%lO!@c7`!CTP;=`<%l!@c^!Ezl$i&j(Z!b!X7`OY&}YZ&cZw&}wx&cx!^&}!^!_'}!_#O&}#O#P&c#P#W&}#W#X!Eq#X#Z&}#Z#[!Eq#[#]&}#]#^!Eq#^#a&}#a#b!Eq#b#g&}#g#h!Eq#h#i&}#i#j!Eq#j#k!Eq#k#m&}#m#n!Eq#n#o&}#o#p'}#p;'S&};'S;=`(l<%lO&}8r!GyZ(Z!b!X7`OY!GrZw!Grwx!@cx!P!Gr!P!Q!Hl!Q!}!Gr!}#O!JU#O#P!Bq#P;'S!Gr;'S;=`!J|<%lO!Gr8r!Hse(Z!b!X7`OY'}Zw'}x#O'}#P#W'}#W#X!Hl#X#Z'}#Z#[!Hl#[#]'}#]#^!Hl#^#a'}#a#b!Hl#b#g'}#g#h!Hl#h#i'}#i#j!Hl#j#k!Hl#k#m'}#m#n!Hl#n;'S'};'S;=`(f<%lO'}8r!JZX(Z!bOY!JUZw!JUwx!Arx#O!JU#O#P!B[#P#Q!Gr#Q;'S!JU;'S;=`!Jv<%lO!JU8r!JyP;=`<%l!JU8r!KPP;=`<%l!Gr>^!KZ^$i&j(Z!bOY!KSYZ&cZw!KSwx!CWx!^!KS!^!_!JU!_#O!KS#O#P!DR#P#Q!^!LYP;=`<%l!KS>^!L`P;=`<%l!_#c#d#Bq#d#l%Z#l#m#Es#m#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#_#c#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#>j_$i&j(Wp(Z!bs'9tOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#?rd$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q!R#AQ!R!S#AQ!S!^%Z!^!_*g!_#O%Z#O#P&c#P#R%Z#R#S#AQ#S#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#A]f$i&j(Wp(Z!bs'9tOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q!R#AQ!R!S#AQ!S!^%Z!^!_*g!_#O%Z#O#P&c#P#R%Z#R#S#AQ#S#b%Z#b#c#>_#c#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#Bzc$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q!Y#DV!Y!^%Z!^!_*g!_#O%Z#O#P&c#P#R%Z#R#S#DV#S#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#Dbe$i&j(Wp(Z!bs'9tOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q!Y#DV!Y!^%Z!^!_*g!_#O%Z#O#P&c#P#R%Z#R#S#DV#S#b%Z#b#c#>_#c#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#E|g$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q![#Ge![!^%Z!^!_*g!_!c%Z!c!i#Ge!i#O%Z#O#P&c#P#R%Z#R#S#Ge#S#T%Z#T#Z#Ge#Z#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#Gpi$i&j(Wp(Z!bs'9tOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q![#Ge![!^%Z!^!_*g!_!c%Z!c!i#Ge!i#O%Z#O#P&c#P#R%Z#R#S#Ge#S#T%Z#T#Z#Ge#Z#b%Z#b#c#>_#c#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z*)x#Il_!g$b$i&j$O)Lv(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z)[#Jv_al$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z04f#LS^h#)`#R-v$?V_!^(CdvBr$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z?O$@a_!q7`$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z07[$Aq|$i&j(Wp(Z!b'|0/l$]#t(T,2j(e$I[OX%ZXY+gYZ&cZ[+g[p%Zpq+gqr%Zrs&}st%ZtuEruw%Zwx(rx}%Z}!OGv!O!Q%Z!Q![Er![!^%Z!^!_*g!_!c%Z!c!}Er!}#O%Z#O#P&c#P#R%Z#R#SEr#S#T%Z#T#oEr#o#p*g#p$f%Z$f$g+g$g#BYEr#BY#BZ$A`#BZ$ISEr$IS$I_$A`$I_$JTEr$JT$JU$A`$JU$KVEr$KV$KW$A`$KW&FUEr&FU&FV$A`&FV;'SEr;'S;=`I|<%l?HTEr?HT?HU$A`?HUOEr07[$D|k$i&j(Wp(Z!b'}0/l$]#t(T,2j(e$I[OY%ZYZ&cZr%Zrs&}st%ZtuEruw%Zwx(rx}%Z}!OGv!O!Q%Z!Q![Er![!^%Z!^!_*g!_!c%Z!c!}Er!}#O%Z#O#P&c#P#R%Z#R#SEr#S#T%Z#T#oEr#o#p*g#p$g%Z$g;'SEr;'S;=`I|<%lOEr",tokenizers:[j,M,N,be,2,3,4,5,6,7,8,9,10,11,12,13,14,A,new p("$S~RRtu[#O#Pg#S#T#|~_P#o#pb~gOx~~jVO#i!P#i#j!U#j#l!P#l#m!q#m;'S!P;'S;=`#v<%lO!P~!UO!U~~!XS!Q![!e!c!i!e#T#Z!e#o#p#Z~!hR!Q![!q!c!i!q#T#Z!q~!tR!Q![!}!c!i!}#T#Z!}~#QR!Q![!P!c!i!P#T#Z!P~#^R!Q![#g!c!i#g#T#Z#g~#jS!Q![#g!c!i#g#T#Z#g#q#r!P~#yP;=`<%l!P~$RO(c~~",141,340),new p(`j~RQYZXz{^~^O(Q~~aP!P!Qd~iO(R~~`,25,323)],topRules:{Script:[0,7],SingleExpression:[1,276],SingleClassItem:[2,277]},dialects:{jsx:0,ts:15175},dynamicPrecedences:{80:1,82:1,94:1,169:1,199:1},specialized:[{term:327,get:e=>Se[e]||-1},{term:343,get:e=>Ce[e]||-1},{term:95,get:e=>we[e]||-1}],tokenPrec:15201}),Ee=e({autoCloseTags:()=>$,javascript:()=>Z,javascriptLanguage:()=>W,jsxLanguage:()=>q,localCompletionSource:()=>U,snippets:()=>F,tsxLanguage:()=>J,typescriptLanguage:()=>K,typescriptSnippets:()=>I}),F=[h("function ${name}(${params}) {\n ${}\n}",{label:`function`,detail:`definition`,type:`keyword`}),h("for (let ${index} = 0; ${index} < ${bound}; ${index}++) {\n ${}\n}",{label:`for`,detail:`loop`,type:`keyword`}),h("for (let ${name} of ${collection}) {\n ${}\n}",{label:`for`,detail:`of loop`,type:`keyword`}),h(`do { +import{$ as e,C as t,D as n,I as r,M as i,_ as a,b as o,c as s,d as c,f as ee,i as te,l as ne,m as re,s as l,t as ie,u,v as d,x as f}from"./index-DPVcGf1z.js";import{i as p,n as m,r as ae,t as oe}from"./dist-T_roqrwM.js";import{i as h,n as g,r as se}from"./dist-bWbpkuNe.js";var ce=316,le=317,_=1,ue=2,de=3,fe=4,pe=318,me=320,he=321,ge=5,_e=6,ve=0,v=[9,10,11,12,13,32,133,160,5760,8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,8232,8233,8239,8287,12288],y=125,ye=59,b=47,x=42,S=43,C=45,w=60,T=44,E=63,D=46,O=91,k=new oe({start:!1,shift(e,t){return t==ge||t==_e||t==me?e:t==he},strict:!1}),A=new m((e,t)=>{let{next:n}=e;(n==y||n==-1||t.context)&&e.acceptToken(pe)},{contextual:!0,fallback:!0}),j=new m((e,t)=>{let{next:n}=e,r;v.indexOf(n)>-1||n==b&&((r=e.peek(1))==b||r==x)||n!=y&&n!=ye&&n!=-1&&!t.context&&e.acceptToken(ce)},{contextual:!0}),M=new m((e,t)=>{e.next==O&&!t.context&&e.acceptToken(le)},{contextual:!0}),N=new m((e,t)=>{let{next:n}=e;if(n==S||n==C){if(e.advance(),n==e.next){e.advance();let n=!t.context&&t.canShift(_);e.acceptToken(n?_:ue)}}else n==E&&e.peek(1)==D&&(e.advance(),e.advance(),(e.next<48||e.next>57)&&e.acceptToken(de))},{contextual:!0});function P(e,t){return e>=65&&e<=90||e>=97&&e<=122||e==95||e>=192||!t&&e>=48&&e<=57}var be=new m((e,t)=>{if(e.next!=w||!t.dialectEnabled(ve)||(e.advance(),e.next==b))return;let n=0;for(;v.indexOf(e.next)>-1;)e.advance(),n++;if(P(e.next,!0)){for(e.advance(),n++;P(e.next,!1);)e.advance(),n++;for(;v.indexOf(e.next)>-1;)e.advance(),n++;if(e.next==T)return;for(let t=0;;t++){if(t==7){if(!P(e.next,!0))return;break}if(e.next!=`extends`.charCodeAt(t))break;e.advance(),n++}}e.acceptToken(fe,-n)}),xe=o({"get set async static":f.modifier,"for while do if else switch try catch finally return throw break continue default case defer":f.controlKeyword,"in of await yield void typeof delete instanceof as satisfies":f.operatorKeyword,"let var const using function class extends":f.definitionKeyword,"import export from":f.moduleKeyword,"with debugger new":f.keyword,TemplateString:f.special(f.string),super:f.atom,BooleanLiteral:f.bool,this:f.self,null:f.null,Star:f.modifier,VariableName:f.variableName,"CallExpression/VariableName TaggedTemplateExpression/VariableName":f.function(f.variableName),VariableDefinition:f.definition(f.variableName),Label:f.labelName,PropertyName:f.propertyName,PrivatePropertyName:f.special(f.propertyName),"CallExpression/MemberExpression/PropertyName":f.function(f.propertyName),"FunctionDeclaration/VariableDefinition":f.function(f.definition(f.variableName)),"ClassDeclaration/VariableDefinition":f.definition(f.className),"NewExpression/VariableName":f.className,PropertyDefinition:f.definition(f.propertyName),PrivatePropertyDefinition:f.definition(f.special(f.propertyName)),UpdateOp:f.updateOperator,"LineComment Hashbang":f.lineComment,BlockComment:f.blockComment,Number:f.number,String:f.string,Escape:f.escape,ArithOp:f.arithmeticOperator,LogicOp:f.logicOperator,BitOp:f.bitwiseOperator,CompareOp:f.compareOperator,RegExp:f.regexp,Equals:f.definitionOperator,Arrow:f.function(f.punctuation),": Spread":f.punctuation,"( )":f.paren,"[ ]":f.squareBracket,"{ }":f.brace,"InterpolationStart InterpolationEnd":f.special(f.brace),".":f.derefOperator,", ;":f.separator,"@":f.meta,TypeName:f.typeName,TypeDefinition:f.definition(f.typeName),"type enum interface implements namespace module declare":f.definitionKeyword,"abstract global Privacy readonly override":f.modifier,"is keyof unique infer asserts":f.operatorKeyword,JSXAttributeValue:f.attributeValue,JSXText:f.content,"JSXStartTag JSXStartCloseTag JSXSelfCloseEndTag JSXEndTag":f.angleBracket,"JSXIdentifier JSXNameSpacedName":f.tagName,"JSXAttribute/JSXIdentifier JSXAttribute/JSXNameSpacedName":f.attributeName,"JSXBuiltin/JSXIdentifier":f.standard(f.tagName)}),Se={__proto__:null,export:20,as:25,from:33,default:36,async:41,function:42,in:52,out:55,const:56,extends:60,this:64,true:72,false:72,null:84,void:88,typeof:92,super:108,new:142,delete:154,yield:163,await:167,class:172,public:235,private:235,protected:235,readonly:237,instanceof:256,satisfies:259,import:292,keyof:349,unique:353,infer:359,asserts:395,is:397,abstract:417,implements:419,type:421,let:424,var:426,using:429,interface:435,enum:439,namespace:445,module:447,declare:451,global:455,defer:471,for:476,of:485,while:488,with:492,do:496,if:500,else:502,switch:506,case:512,try:518,catch:522,finally:526,return:530,throw:534,break:538,continue:542,debugger:546},Ce={__proto__:null,async:129,get:131,set:133,declare:195,public:197,private:197,protected:197,static:199,abstract:201,override:203,readonly:209,accessor:211,new:401},we={__proto__:null,"<":193},Te=ae.deserialize({version:14,states:"$F|Q%TQlOOO%[QlOOO'_QpOOP(lO`OOO*zQ!0MxO'#CiO+RO#tO'#CjO+aO&jO'#CjO+oO#@ItO'#DaO.QQlO'#DgO.bQlO'#DrO%[QlO'#DzO0fQlO'#ESOOQ!0Lf'#E['#E[O1PQ`O'#EXOOQO'#Ep'#EpOOQO'#Il'#IlO1XQ`O'#GsO1dQ`O'#EoO1iQ`O'#EoO3hQ!0MxO'#JrO6[Q!0MxO'#JsO6uQ`O'#F]O6zQ,UO'#FtOOQ!0Lf'#Ff'#FfO7VO7dO'#FfO9XQMhO'#F|O9`Q`O'#F{OOQ!0Lf'#Js'#JsOOQ!0Lb'#Jr'#JrO9eQ`O'#GwOOQ['#K_'#K_O9pQ`O'#IYO9uQ!0LrO'#IZOOQ['#J`'#J`OOQ['#I_'#I_Q`QlOOQ`QlOOO9}Q!L^O'#DvO:UQlO'#EOO:]QlO'#EQO9kQ`O'#GsO:dQMhO'#CoO:rQ`O'#EnO:}Q`O'#EyO;hQMhO'#FeO;xQ`O'#GsOOQO'#K`'#K`O;}Q`O'#K`O<]Q`O'#G{O<]Q`O'#G|O<]Q`O'#HOO9kQ`O'#HRO=SQ`O'#HUO>kQ`O'#CeO>{Q`O'#HcO?TQ`O'#HiO?TQ`O'#HkO`QlO'#HmO?TQ`O'#HoO?TQ`O'#HrO?YQ`O'#HxO?_Q!0LsO'#IOO%[QlO'#IQO?jQ!0LsO'#ISO?uQ!0LsO'#IUO9uQ!0LrO'#IWO@QQ!0MxO'#CiOASQpO'#DlQOQ`OOO%[QlO'#EQOAjQ`O'#ETO:dQMhO'#EnOAuQ`O'#EnOBQQ!bO'#FeOOQ['#Cg'#CgOOQ!0Lb'#Dq'#DqOOQ!0Lb'#Jv'#JvO%[QlO'#JvOOQO'#Jy'#JyOOQO'#Ih'#IhOCQQpO'#EgOOQ!0Lb'#Ef'#EfOOQ!0Lb'#J}'#J}OC|Q!0MSO'#EgODWQpO'#EWOOQO'#Jx'#JxODlQpO'#JyOEyQpO'#EWODWQpO'#EgPFWO&2DjO'#CbPOOO)CD})CD}OOOO'#I`'#I`OFcO#tO,59UOOQ!0Lh,59U,59UOOOO'#Ia'#IaOFqO&jO,59UOGPQ!L^O'#DcOOOO'#Ic'#IcOGWO#@ItO,59{OOQ!0Lf,59{,59{OGfQlO'#IdOGyQ`O'#JtOIxQ!fO'#JtO+}QlO'#JtOJPQ`O,5:ROJgQ`O'#EpOJtQ`O'#KTOKPQ`O'#KSOKPQ`O'#KSOKXQ`O,5;^OK^Q`O'#KROOQ!0Ln,5:^,5:^OKeQlO,5:^OMcQ!0MxO,5:fONSQ`O,5:nONmQ!0LrO'#KQONtQ`O'#KPO9eQ`O'#KPO! YQ`O'#KPO! bQ`O,5;]O! gQ`O'#KPO!#lQ!fO'#JsOOQ!0Lh'#Ci'#CiO%[QlO'#ESO!$[Q!fO,5:sOOQS'#Jz'#JzOOQO-EtOOQ['#Jh'#JhOOQ[,5>u,5>uOOQ[-E<]-E<]O!TO`QlO,5>VO!LOQ`O,5>XO`QlO,5>ZO!LTQ`O,5>^O!LYQlO,5>dOOQ[,5>j,5>jO%[QlO,5>jO9uQ!0LrO,5>lOOQ[,5>n,5>nO#!dQ`O,5>nOOQ[,5>p,5>pO#!dQ`O,5>pOOQ[,5>r,5>rO##QQpO'#D_O%[QlO'#JvO##sQpO'#JvO##}QpO'#DmO#$`QpO'#DmO#&qQlO'#DmO#&xQ`O'#JuO#'QQ`O,5:WO#'VQ`O'#EtO#'eQ`O'#KUO#'mQ`O,5;_O#'rQpO'#DmO#(PQpO'#EVOOQ!0Lf,5:o,5:oO%[QlO,5:oO#(WQ`O,5:oO?YQ`O,5;YO!CUQpO,5;YO!C^QMhO,5;YO:dQMhO,5;YO#(`Q`O,5@bO#(eQ07dO,5:sOOQO-EPO$6^Q`O,5>POOQ[1G3i1G3iO`QlO1G3iOOQ[1G3o1G3oOOQ[1G3q1G3qO?TQ`O1G3sO$6cQlO1G3uO$:gQlO'#HtOOQ[1G3x1G3xO$:tQ`O'#HzO?YQ`O'#H|OOQ[1G4O1G4OO$:|QlO1G4OO9uQ!0LrO1G4UOOQ[1G4W1G4WOOQ!0Lb'#G_'#G_O9uQ!0LrO1G4YO9uQ!0LrO1G4[O$?TQ`O,5@bO!)[QlO,5;`O9eQ`O,5;`O?YQ`O,5:XO!)[QlO,5:XO!CUQpO,5:XO$?YQ?MtO,5:XOOQO,5;`,5;`O$?dQpO'#IeO$?zQ`O,5@aOOQ!0Lf1G/r1G/rO$@SQpO'#IkO$@^Q`O,5@pOOQ!0Lb1G0y1G0yO#$`QpO,5:XOOQO'#Ig'#IgO$@fQpO,5:qOOQ!0Ln,5:q,5:qO#(ZQ`O1G0ZOOQ!0Lf1G0Z1G0ZO%[QlO1G0ZOOQ!0Lf1G0t1G0tO?YQ`O1G0tO!CUQpO1G0tO!C^QMhO1G0tOOQ!0Lb1G5|1G5|O!ByQ!0LrO1G0^OOQO1G0m1G0mO%[QlO1G0mO$@mQ!0LrO1G0mO$@xQ!0LrO1G0mO!CUQpO1G0^ODWQpO1G0^O$AWQ!0LrO1G0mOOQO1G0^1G0^O$AlQ!0MxO1G0mPOOO-E<[-E<[POOO1G.h1G.hOOOO1G/i1G/iO$AvQ!bO,5QQpO,5@}OOQ!0Lb1G3c1G3cOOQ[7+$V7+$VO@zQ`O7+$VO9uQ!0LrO7+$VO%>]Q`O7+$VO%[QlO1G6lO%[QlO1G6mO%>bQ!0LrO1G6lO%>lQlO1G3kO%>sQ`O1G3kO%>xQlO1G3kOOQ[7+)T7+)TO9uQ!0LrO7+)_O`QlO7+)aOOQ['#Kh'#KhOOQ['#JS'#JSO%?PQlO,5>`OOQ[,5>`,5>`O%[QlO'#HuO%?^Q`O'#HwOOQ[,5>f,5>fO9eQ`O,5>fOOQ[,5>h,5>hOOQ[7+)j7+)jOOQ[7+)p7+)pOOQ[7+)t7+)tOOQ[7+)v7+)vO%?cQpO1G5|O%?}Q?MtO1G0zO%@XQ`O1G0zOOQO1G/s1G/sO%@dQ?MtO1G/sO?YQ`O1G/sO!)[QlO'#DmOOQO,5?P,5?POOQO-ERQ`O7+,WO&>WQ`O7+,XO%[QlO7+,WO%[QlO7+,XOOQ[7+)V7+)VO&>]Q`O7+)VO&>bQlO7+)VO&>iQ`O7+)VOOQ[<nQ`O,5>aOOQ[,5>c,5>cO&>sQ`O1G4QO9eQ`O7+&fO!)[QlO7+&fOOQO7+%_7+%_O&>xQ?MtO1G6ZO?YQ`O7+%_OOQ!0Lf<yQ?MvO,5?aO'@|Q?MvO,5?cO'CPQ?MvO7+'|O'DuQMjOG27TOOQO<VO!l$xO#jROe!iOpkOrPO(T)]O(VTO(YUO(aVO(o[O~O!]$_Oa$qa'z$qa'w$qa!k$qa!Y$qa!_$qa%i$qa!g$qa~Ol)dO~P!&zOh%VOp%WOr%XOs$tOt$tOz%YO|%ZO!O%]O!S${O!_$|O!i%bO!l$xO#j%cO$W%`O$t%^O$v%_O$y%aO(T(vO(VTO(YUO(a$uO(y$}O(z%PO~Og(pP~P!,TO!Q)iO!g)hO!_$^X$Z$^X$]$^X$_$^X$f$^X~O!g)hO!_({X$Z({X$]({X$_({X$f({X~O!Q)iO~P!.^O!Q)iO!_({X$Z({X$]({X$_({X$f({X~O!_)kO$Z)oO$])jO$_)jO$f)pO~O![)sO~P!)[O$]$hO$_$gO$f)wO~On$zX!Q$zX#S$zX'y$zX(y$zX(z$zX~OgmXg$zXnmX!]mX#`mX~P!0SOx)yO(b)zO(c)|O~On*VO!Q*OO'y*PO(y$}O(z%PO~Og)}O~P!1WOg*WO~Oh%VOr%XOs$tOt$tOz%YO|%ZO!OVO!l$xO#jVO!l$xO#jROe!iOpkOrPO(VTO(YUO(aVO(o[O~O(T=QO~P#$qO!]-]O!^(iX~O!^-_O~O!g-VO#`-UO!]#hX!^#hX~O!]-`O!^(xX~O!^-bO~O!c-cO!d-cO(U!lO~P#$`O!^-fO~P'_On-iO!_'`O~O!Y-nO~Os!{a!b!{a!c!{a!d!{a#T!{a#U!{a#V!{a#W!{a#X!{a#[!{a#]!{a(U!{a(V!{a(Y!{a(e!{a(o!{a~P!#vO!p-sO#`-qO~PChO!c-uO!d-uO(U!lO~PDWOa%nO#`-qO'z%nO~Oa%nO!g#vO#`-qO'z%nO~Oa%nO!g#vO!p-sO#`-qO'z%nO(r'pO~O(P'xO(Q'xO(R-zO~Ov-{O~O!Y'Wa!]'Wa~P!:tO![.PO!Y'WX!]'WX~P%[O!](VO!Y(ha~O!Y(ha~PHRO!](^O!Y(va~O!S%hO![.TO!_%iO(T%gO!Y'^X!]'^X~O#`.VO!](ta!k(taa(ta'z(ta~O!g#vO~P#,wO!](jO!k(sa~O!S%hO!_%iO#j.ZO(T%gO~Op.`O!S%hO![.]O!_%iO!|]O#i._O#j.]O(T%gO!]'aX!k'aX~OR.dO!l#xO~Oh%VOn.gO!_'`O%i.fO~Oa#ci!]#ci'z#ci'w#ci!Y#ci!k#civ#ci!_#ci%i#ci!g#ci~P!:tOn>]O!Q*OO'y*PO(y$}O(z%PO~O#k#_aa#_a#`#_a'z#_a!]#_a!k#_a!_#_a!Y#_a~P#/sO#k(`XP(`XR(`X[(`Xa(`Xj(`Xr(`X!S(`X!l(`X!p(`X#R(`X#n(`X#o(`X#p(`X#q(`X#r(`X#s(`X#t(`X#u(`X#v(`X#x(`X#z(`X#{(`X'z(`X(a(`X(r(`X!k(`X!Y(`X'w(`Xv(`X!_(`X%i(`X!g(`X~P!6kO!].tO!k(kX~P!:tO!k.wO~O!Y.yO~OP$[OR#zO!Q#yO!S#{O!l#xO!p$[O(aVO[#mia#mij#mir#mi!]#mi#R#mi#o#mi#p#mi#q#mi#r#mi#s#mi#t#mi#u#mi#v#mi#x#mi#z#mi#{#mi'z#mi(r#mi(y#mi(z#mi'w#mi!Y#mi!k#miv#mi!_#mi%i#mi!g#mi~O#n#mi~P#3cO#n$OO~P#3cOP$[OR#zOr$aO!Q#yO!S#{O!l#xO!p$[O#n$OO#o$PO#p$PO#q$PO(aVO[#mia#mij#mi!]#mi#R#mi#s#mi#t#mi#u#mi#v#mi#x#mi#z#mi#{#mi'z#mi(r#mi(y#mi(z#mi'w#mi!Y#mi!k#miv#mi!_#mi%i#mi!g#mi~O#r#mi~P#6QO#r$QO~P#6QOP$[OR#zO[$cOj$ROr$aO!Q#yO!S#{O!l#xO!p$[O#R$RO#n$OO#o$PO#p$PO#q$PO#r$QO#s$RO#t$RO#u$bO(aVOa#mi!]#mi#x#mi#z#mi#{#mi'z#mi(r#mi(y#mi(z#mi'w#mi!Y#mi!k#miv#mi!_#mi%i#mi!g#mi~O#v#mi~P#8oOP$[OR#zO[$cOj$ROr$aO!Q#yO!S#{O!l#xO!p$[O#R$RO#n$OO#o$PO#p$PO#q$PO#r$QO#s$RO#t$RO#u$bO#v$SO(aVO(z#}Oa#mi!]#mi#z#mi#{#mi'z#mi(r#mi(y#mi'w#mi!Y#mi!k#miv#mi!_#mi%i#mi!g#mi~O#x$UO~P#;VO#x#mi~P#;VO#v$SO~P#8oOP$[OR#zO[$cOj$ROr$aO!Q#yO!S#{O!l#xO!p$[O#R$RO#n$OO#o$PO#p$PO#q$PO#r$QO#s$RO#t$RO#u$bO#v$SO#x$UO(aVO(y#|O(z#}Oa#mi!]#mi#{#mi'z#mi(r#mi'w#mi!Y#mi!k#miv#mi!_#mi%i#mi!g#mi~O#z#mi~P#={O#z$WO~P#={OP]XR]X[]Xj]Xr]X!Q]X!S]X!l]X!p]X#R]X#S]X#`]X#kfX#n]X#o]X#p]X#q]X#r]X#s]X#t]X#u]X#v]X#x]X#z]X#{]X$Q]X(a]X(r]X(y]X(z]X!]]X!^]X~O$O]X~P#@jOP$[OR#zO[]O!Q*OO'y*PO(y$}O(z%POP#miR#mi!S#mi!l#mi!p#mi#n#mi#o#mi#p#mi#q#mi(a#mi~P#EyO!]/POg(pX~P!1WOg/RO~Oa$Pi!]$Pi'z$Pi'w$Pi!Y$Pi!k$Piv$Pi!_$Pi%i$Pi!g$Pi~P!:tO$]/SO$_/SO~O$]/TO$_/TO~O!g)hO#`/UO!_$cX$Z$cX$]$cX$_$cX$f$cX~O![/VO~O!_)kO$Z/XO$])jO$_)jO$f/YO~O!]VO!l$xO#j^O!Q*OO'y*PO(y$}O(z%POP#miR#mi!S#mi!l#mi!p#mi#n#mi#o#mi#p#mi#q#mi(a#mi~P&,QO#S$dOP(`XR(`X[(`Xj(`Xn(`Xr(`X!Q(`X!S(`X!l(`X!p(`X#R(`X#n(`X#o(`X#p(`X#q(`X#r(`X#s(`X#t(`X#u(`X#v(`X#x(`X#z(`X#{(`X$O(`X'y(`X(a(`X(r(`X(y(`X(z(`X!](`X!^(`X~O$O$Pi!]$Pi!^$Pi~P#BwO$O!ri!^!ri~P$+oOg']a!]']a~P!1WO!^7nO~O!]'da!^'da~P#BwO!Y7oO~P#/sO!g#vO(r'pO!]'ea!k'ea~O!]/pO!k)Oi~O!]/pO!g#vO!k)Oi~Og$|q!]$|q#`$|q$O$|q~P!1WO!Y'ga!]'ga~P#/sO!g7vO~O!]/yO!Y)Pi~P#/sO!]/yO!Y)Pi~O!Y7yO~Oh%VOr8OO!l%eO(r'pO~Oj8QO!g#vO~Or8TO!g#vO(r'pO~O!Q*OO'y*PO(z%POn'ja(y'ja!]'ja#`'ja~Og'ja$O'ja~P&5RO!Q*OO'y*POn'la(y'la(z'la!]'la#`'la~Og'la$O'la~P&5tOg(_q!](_q~P!1WO#`8VOg(_q!](_q~P!1WO!Y8WO~Og%Oq!]%Oq#`%Oq$O%Oq~P!1WOa$oy!]$oy'z$oy'w$oy!Y$oy!k$oyv$oy!_$oy%i$oy!g$oy~P!:tO!g6rO~O!]5[O!_)Qa~O!_'`OP$TaR$Ta[$Taj$Tar$Ta!Q$Ta!S$Ta!]$Ta!l$Ta!p$Ta#R$Ta#n$Ta#o$Ta#p$Ta#q$Ta#r$Ta#s$Ta#t$Ta#u$Ta#v$Ta#x$Ta#z$Ta#{$Ta(a$Ta(r$Ta(y$Ta(z$Ta~O%i7WO~P&8fO%^8[Oa%[i!_%[i'z%[i!]%[i~Oa#cy!]#cy'z#cy'w#cy!Y#cy!k#cyv#cy!_#cy%i#cy!g#cy~P!:tO[8^O~Ob8`O(T+qO(VTO(YUO~O!]1TO!^)Xi~O`8dO~O(e(|O!]'pX!^'pX~O!]5uO!^)Ua~O!^8nO~P%;eO(o!sO~P$&YO#[8oO~O!_1oO~O!_1oO%i8qO~On8tO!_1oO%i8qO~O[8yO!]'sa!^'sa~O!]1zO!^)Vi~O!k8}O~O!k9OO~O!k9RO~O!k9RO~P%[Oa9TO~O!g9UO~O!k9VO~O!](wi!^(wi~P#BwOa%nO#`9_O'z%nO~O!](ty!k(tya(ty'z(ty~P!:tO!](jO!k(sy~O%i9bO~P&8fO!_'`O%i9bO~O#k$|qP$|qR$|q[$|qa$|qj$|qr$|q!S$|q!]$|q!l$|q!p$|q#R$|q#n$|q#o$|q#p$|q#q$|q#r$|q#s$|q#t$|q#u$|q#v$|q#x$|q#z$|q#{$|q'z$|q(a$|q(r$|q!k$|q!Y$|q'w$|q#`$|qv$|q!_$|q%i$|q!g$|q~P#/sO#k'jaP'jaR'ja['jaa'jaj'jar'ja!S'ja!l'ja!p'ja#R'ja#n'ja#o'ja#p'ja#q'ja#r'ja#s'ja#t'ja#u'ja#v'ja#x'ja#z'ja#{'ja'z'ja(a'ja(r'ja!k'ja!Y'ja'w'jav'ja!_'ja%i'ja!g'ja~P&5RO#k'laP'laR'la['laa'laj'lar'la!S'la!l'la!p'la#R'la#n'la#o'la#p'la#q'la#r'la#s'la#t'la#u'la#v'la#x'la#z'la#{'la'z'la(a'la(r'la!k'la!Y'la'w'lav'la!_'la%i'la!g'la~P&5tO#k%OqP%OqR%Oq[%Oqa%Oqj%Oqr%Oq!S%Oq!]%Oq!l%Oq!p%Oq#R%Oq#n%Oq#o%Oq#p%Oq#q%Oq#r%Oq#s%Oq#t%Oq#u%Oq#v%Oq#x%Oq#z%Oq#{%Oq'z%Oq(a%Oq(r%Oq!k%Oq!Y%Oq'w%Oq#`%Oqv%Oq!_%Oq%i%Oq!g%Oq~P#/sO!]'Yi!k'Yi~P!:tO$O#cq!]#cq!^#cq~P#BwO(y$}OP%aaR%aa[%aaj%aar%aa!S%aa!l%aa!p%aa#R%aa#n%aa#o%aa#p%aa#q%aa#r%aa#s%aa#t%aa#u%aa#v%aa#x%aa#z%aa#{%aa$O%aa(a%aa(r%aa!]%aa!^%aa~On%aa!Q%aa'y%aa(z%aa~P&IyO(z%POP%caR%ca[%caj%car%ca!S%ca!l%ca!p%ca#R%ca#n%ca#o%ca#p%ca#q%ca#r%ca#s%ca#t%ca#u%ca#v%ca#x%ca#z%ca#{%ca$O%ca(a%ca(r%ca!]%ca!^%ca~On%ca!Q%ca'y%ca(y%ca~P&LQOn>^O!Q*OO'y*PO(z%PO~P&IyOn>^O!Q*OO'y*PO(y$}O~P&LQOR0kO!Q0kO!S0lO#S$dOP}a[}aj}an}ar}a!l}a!p}a#R}a#n}a#o}a#p}a#q}a#r}a#s}a#t}a#u}a#v}a#x}a#z}a#{}a$O}a'y}a(a}a(r}a(y}a(z}a!]}a!^}a~O!Q*OO'y*POP$saR$sa[$saj$san$sar$sa!S$sa!l$sa!p$sa#R$sa#n$sa#o$sa#p$sa#q$sa#r$sa#s$sa#t$sa#u$sa#v$sa#x$sa#z$sa#{$sa$O$sa(a$sa(r$sa(y$sa(z$sa!]$sa!^$sa~O!Q*OO'y*POP$uaR$ua[$uaj$uan$uar$ua!S$ua!l$ua!p$ua#R$ua#n$ua#o$ua#p$ua#q$ua#r$ua#s$ua#t$ua#u$ua#v$ua#x$ua#z$ua#{$ua$O$ua(a$ua(r$ua(y$ua(z$ua!]$ua!^$ua~On>^O!Q*OO'y*PO(y$}O(z%PO~OP%TaR%Ta[%Taj%Tar%Ta!S%Ta!l%Ta!p%Ta#R%Ta#n%Ta#o%Ta#p%Ta#q%Ta#r%Ta#s%Ta#t%Ta#u%Ta#v%Ta#x%Ta#z%Ta#{%Ta$O%Ta(a%Ta(r%Ta!]%Ta!^%Ta~P''VO$O$mq!]$mq!^$mq~P#BwO$O$oq!]$oq!^$oq~P#BwO!^9oO~O$O9pO~P!1WO!g#vO!]'ei!k'ei~O!g#vO(r'pO!]'ei!k'ei~O!]/pO!k)Oq~O!Y'gi!]'gi~P#/sO!]/yO!Y)Pq~Or9wO!g#vO(r'pO~O[9yO!Y9xO~P#/sO!Y9xO~Oj:PO!g#vO~Og(_y!](_y~P!1WO!]'na!_'na~P#/sOa%[q!_%[q'z%[q!]%[q~P#/sO[:UO~O!]1TO!^)Xq~O`:YO~O#`:ZO!]'pa!^'pa~O!]5uO!^)Ui~P#BwO!S:]O~O!_1oO%i:`O~O(VTO(YUO(e:eO~O!]1zO!^)Vq~O!k:hO~O!k:iO~O!k:jO~O!k:jO~P%[O#`:mO!]#hy!^#hy~O!]#hy!^#hy~P#BwO%i:rO~P&8fO!_'`O%i:rO~O$O#|y!]#|y!^#|y~P#BwOP$|iR$|i[$|ij$|ir$|i!S$|i!l$|i!p$|i#R$|i#n$|i#o$|i#p$|i#q$|i#r$|i#s$|i#t$|i#u$|i#v$|i#x$|i#z$|i#{$|i$O$|i(a$|i(r$|i!]$|i!^$|i~P''VO!Q*OO'y*PO(z%POP'iaR'ia['iaj'ian'iar'ia!S'ia!l'ia!p'ia#R'ia#n'ia#o'ia#p'ia#q'ia#r'ia#s'ia#t'ia#u'ia#v'ia#x'ia#z'ia#{'ia$O'ia(a'ia(r'ia(y'ia!]'ia!^'ia~O!Q*OO'y*POP'kaR'ka['kaj'kan'kar'ka!S'ka!l'ka!p'ka#R'ka#n'ka#o'ka#p'ka#q'ka#r'ka#s'ka#t'ka#u'ka#v'ka#x'ka#z'ka#{'ka$O'ka(a'ka(r'ka(y'ka(z'ka!]'ka!^'ka~O(y$}OP%aiR%ai[%aij%ain%air%ai!Q%ai!S%ai!l%ai!p%ai#R%ai#n%ai#o%ai#p%ai#q%ai#r%ai#s%ai#t%ai#u%ai#v%ai#x%ai#z%ai#{%ai$O%ai'y%ai(a%ai(r%ai(z%ai!]%ai!^%ai~O(z%POP%ciR%ci[%cij%cin%cir%ci!Q%ci!S%ci!l%ci!p%ci#R%ci#n%ci#o%ci#p%ci#q%ci#r%ci#s%ci#t%ci#u%ci#v%ci#x%ci#z%ci#{%ci$O%ci'y%ci(a%ci(r%ci(y%ci!]%ci!^%ci~O$O$oy!]$oy!^$oy~P#BwO$O#cy!]#cy!^#cy~P#BwO!g#vO!]'eq!k'eq~O!]/pO!k)Oy~O!Y'gq!]'gq~P#/sOr:|O!g#vO(r'pO~O[;QO!Y;PO~P#/sO!Y;PO~Og(_!R!](_!R~P!1WOa%[y!_%[y'z%[y!]%[y~P#/sO!]1TO!^)Xy~O!]5uO!^)Uq~O(T;XO~O!_1oO%i;[O~O!k;_O~O%i;dO~P&8fOP$|qR$|q[$|qj$|qr$|q!S$|q!l$|q!p$|q#R$|q#n$|q#o$|q#p$|q#q$|q#r$|q#s$|q#t$|q#u$|q#v$|q#x$|q#z$|q#{$|q$O$|q(a$|q(r$|q!]$|q!^$|q~P''VO!Q*OO'y*PO(z%POP'jaR'ja['jaj'jan'jar'ja!S'ja!l'ja!p'ja#R'ja#n'ja#o'ja#p'ja#q'ja#r'ja#s'ja#t'ja#u'ja#v'ja#x'ja#z'ja#{'ja$O'ja(a'ja(r'ja(y'ja!]'ja!^'ja~O!Q*OO'y*POP'laR'la['laj'lan'lar'la!S'la!l'la!p'la#R'la#n'la#o'la#p'la#q'la#r'la#s'la#t'la#u'la#v'la#x'la#z'la#{'la$O'la(a'la(r'la(y'la(z'la!]'la!^'la~OP%OqR%Oq[%Oqj%Oqr%Oq!S%Oq!l%Oq!p%Oq#R%Oq#n%Oq#o%Oq#p%Oq#q%Oq#r%Oq#s%Oq#t%Oq#u%Oq#v%Oq#x%Oq#z%Oq#{%Oq$O%Oq(a%Oq(r%Oq!]%Oq!^%Oq~P''VOg%e!Z!]%e!Z#`%e!Z$O%e!Z~P!1WO!Y;hO~P#/sOr;iO!g#vO(r'pO~O[;kO!Y;hO~P#/sO!]'pq!^'pq~P#BwO!]#h!Z!^#h!Z~P#BwO#k%e!ZP%e!ZR%e!Z[%e!Za%e!Zj%e!Zr%e!Z!S%e!Z!]%e!Z!l%e!Z!p%e!Z#R%e!Z#n%e!Z#o%e!Z#p%e!Z#q%e!Z#r%e!Z#s%e!Z#t%e!Z#u%e!Z#v%e!Z#x%e!Z#z%e!Z#{%e!Z'z%e!Z(a%e!Z(r%e!Z!k%e!Z!Y%e!Z'w%e!Z#`%e!Zv%e!Z!_%e!Z%i%e!Z!g%e!Z~P#/sOr;tO!g#vO(r'pO~O!Y;uO~P#/sOr;|O!g#vO(r'pO~O!Y;}O~P#/sOP%e!ZR%e!Z[%e!Zj%e!Zr%e!Z!S%e!Z!l%e!Z!p%e!Z#R%e!Z#n%e!Z#o%e!Z#p%e!Z#q%e!Z#r%e!Z#s%e!Z#t%e!Z#u%e!Z#v%e!Z#x%e!Z#z%e!Z#{%e!Z$O%e!Z(a%e!Z(r%e!Z!]%e!Z!^%e!Z~P''VOrROe!iOpkOrPO(T)]O(VTO(YUO(aVO(o[O~O!]WO!l$xO#jgPPP!>oI[PPPPPPPPP!BOP!C]PPI[!DnPI[PI[I[I[I[I[PI[!FQP!I[P!LbP!Lf!Lp!Lt!LtP!IXP!Lx!LxP#!OP#!SI[PI[#!Y#%_CjA^PA^PA^A^P#&lA^A^#)OA^#+vA^#.SA^A^#.r#1W#1W#1]#1f#1W#1qPP#1WPA^#2ZA^#6YA^A^6mPPP#:_PPP#:x#:xP#:xP#;`#:xPP#;fP#;]P#;]#;y#;]#P#>V#>]#>k#>q#>{#?R#?]#?c#?s#?y#@k#@}#AT#AZ#Ai#BO#Cs#DR#DY#Et#FS#Gt#HS#HY#H`#Hf#Hp#Hv#H|#IW#Ij#IpPPPPPPPPPPP#IvPPPPPPP#Jk#Mx$ b$ i$ qPPP$']P$'f$*_$0x$0{$1O$1}$2Q$2X$2aP$2g$2jP$3W$3[$4S$5b$5g$5}PP$6S$6Y$6^$6a$6e$6i$7e$7|$8e$8i$8l$8o$8y$8|$9Q$9UR!|RoqOXst!Z#d%m&r&t&u&w,s,x2[2_Y!vQ'`-e1o5{Q%tvQ%|yQ&T|Q&j!VS'W!e-]Q'f!iS'l!r!yU*k$|*Z*oQ+o%}S+|&V&WQ,d&dQ-c'_Q-m'gQ-u'mQ0[*qQ1b,OQ1y,eR<{SU+P%]S!S!nQ!r!v!y!z$|'W'_'`'l'm'n*k*o*q*r-]-c-e-u0[0_1o5{5}%[$ti#v$b$c$d$x${%O%Q%^%_%c)y*R*T*V*Y*a*g*w*x+f+i,S,V.f/P/d/m/x/y/{0`0b0i0j0o1f1i1q3c4^4_4j4o5Q5[5_6S7W7v8Q8V8[8q9b9p9y:P:`:r;Q;[;d;kP>X>Y>]>^Q&X|Q'U!eS'[%i-`Q+t&PQ,P&WQ,f&gQ0n+SQ1Y+uQ1_+{Q2Q,jQ2R,kQ5f1TQ5o1aQ6[1zQ6_1|Q6`2PQ8`5gQ8c5lQ8|6bQ:X8dQ:f8yQ;V:YR<}*ZrnOXst!V!Z#d%m&i&r&t&u&w,s,x2[2_R,h&k&z^OPXYstuvwz!Z!`!g!j!o#S#d#o#u#x#{$O$P$Q$R$S$T$U$V$W$X$Z$_$a$e$n%m%t&R&k&n&o&r&t&u&w&{'T'b'r(V(](d(x(z)O)s)}*i+X+]+g,p,s,x-U-X-i-q.P.V.g.t.{/V/n0]0l0r1S1r2S2T2V2X2[2_2a2p3Q3W3d3l4T4z5w6T6e6f6i6s6|7[8t9T9_:Z:mR>S[#]WZ#W#Z'X(T!b%jm#h#i#l$x%e%h(^(h(i(j*Y*^*b+Z+[+^,o-V.T.Z.[.]._/m/p2d3[3]4a6r7TQ%wxQ%{yW&Q|&V&W,OQ&_!TQ'c!hQ'e!iQ(q#sS+n%|%}Q+r&PQ,_&bQ,c&dS-l'f'gQ.i(rQ1R+oQ1X+uQ1Z+vQ1^+zQ1t,`S1x,d,eQ2|-mQ5e1TQ5i1WQ5n1`Q6Z1yQ8_5gQ8b5kQ8f5pQ:T8^R;T:U!U$zi$d%O%Q%^%_%c*R*T*a*w*x/P/x0`0b0i0j0o4_5Q8V9p>P>X>Y!^%yy!i!u%{%|%}'V'e'f'g'k'u*j+n+o-Y-l-m-t0R0U1R2u2|3T4r4s4v7}9{Q+h%wQ,T&[Q,W&]Q,b&dQ.h(qQ1s,_U1w,c,d,eQ3e.iQ6U1tS6Y1x1yQ8x6Z#f>T#v$b$c$x${)y*V*Y*g+f+i,S,V.f/d/m/y/{1f1i1q3c4^4j4o5[5_6S7W7v8Q8[8q9b9y:P:`:r;Q;[;d;k]>^o>UPS&[!Q&iQ&]!RQ&^!SU*}%[%d=sR,R&Y%]%Si#v$b$c$d$x${%O%Q%^%_%c)y*R*T*V*Y*a*g*w*x+f+i,S,V.f/P/d/m/x/y/{0`0b0i0j0o1f1i1q3c4^4_4j4o5Q5[5_6S7W7v8Q8V8[8q9b9p9y:P:`:r;Q;[;d;kP>X>Y>]>^T)z$u){V+P%]S$i$^c#Y#e%q%s%u(S(Y(t(y)R)S)T)U)V)W)X)Y)Z)[)^)`)b)g)q+d+x-Z-x-}.S.U.s.v.z.|.}/O/b0p2k2n3O3V3k3p3q3r3s3t3u3v3w3x3y3z3{3|4P4Q4X5X5c6u6{7Q7a7b7k7l8k9X9]9g9m9n:o;W;`SQ'Y!eR2q-]!W!nQ!e!r!v!y!z$|'W'_'`'l'm'n*Z*k*o*q*r-]-c-e-u0[0_1o5{5}R1l,ZnqOXst!Z#d%m&r&t&u&w,s,x2[2_Q&y!^Q'v!xS(s#u<^Q+l%zQ,]&_Q,^&aQ-j'dQ-w'oS.r(x=PS0q+X=ZQ1P+mQ1n,[Q2c,zQ2e,{Q2m-WQ2z-kQ2}-oS5Y0r=eQ5a1QS5d1S=fQ6t2oQ6x2{Q6}3SQ8]5bQ9Y6vQ9Z6yQ9^7OR:l9V$d$]c#Y#e%s%u(S(Y(t(y)R)S)T)U)V)W)X)Y)Z)[)^)`)b)g)q+d+x-Z-x-}.S.U.s.v.z.}/O/b0p2k2n3O3V3k3p3q3r3s3t3u3v3w3x3y3z3{3|4P4Q4X5X5c6u6{7Q7a7b7k7l8k9X9]9g9m9n:o;W;`SS#q]SU$fd)_,mS(p#p'iU*v%R(w4OU0m+O.n7gQ5^0xQ7V3`Q9d7YR:s9em!tQ!r!v!y!z'`'l'm'n-e-u1o5{5}Q't!uS(f#g2US-s'k'wQ/s*]Q0R*jQ3U-vQ4f/tQ4r0TQ4s0UQ4x0^Q7r4`S7}4t4vS8R4y4{Q9r7sQ9v7yQ9{8OQ:Q8TS:{9w9xS;g:|;PS;s;h;iS;{;t;uSSR=o>R%^bOPWXYZstuvw!Z!`!g!o#S#W#Z#d#o#u#x#{$O$P$Q$R$S$T$U$V$W$X$_$a$e%m%t&R&k&n&o&r&t&u&w&{'T'b'r(T(V(](d(x(z)O)}*i+X+]+g,p,s,x-i-q.P.V.g.t.{/n0]0l0r1S1r2S2T2V2X2[2_2a3Q3W3d3l4z6T6e6f6i6|7[8t9T9_Q%fj!^%xy!i!u%{%|%}'V'e'f'g'k'u*j+n+o-Y-l-m-t0R0U1R2u2|3T4r4s4v7}9{S&Oz!jQ+k%yQ,a&dW1v,b,c,d,eU6X1w1x1yS8w6Y6ZQ:d8x!r=j$Z$n'X)s-U-X/V2p4T5w6s:Z:mSQ=t>QR=u>R%QeOPXYstuvw!Z!`!g!o#S#d#o#u#x#{$O$P$Q$R$S$T$U$V$W$X$_$a$e%m%t&R&k&n&r&t&u&w&{'T'b'r(V(](d(x(z)O)}*i+X+]+g,p,s,x-i-q.P.V.g.t.{/n0]0l0r1S1r2S2T2V2X2[2_2a3Q3W3d3l4z6T6e6f6i6|7[8t9T9_Y#bWZ#W#Z(T!b%jm#h#i#l$x%e%h(^(h(i(j*Y*^*b+Z+[+^,o-V.T.Z.[.]._/m/p2d3[3]4a6r7TQ,n&o!p=k$Z$n)s-U-X/V2p4T5w6s:Z:mSR=n'XU']!e%i*ZR2s-`%SdOPWXYZstuvw!Z!`!g!o#S#W#Z#d#o#u#x#{$O$P$Q$R$S$T$U$V$W$X$_$a$e%m%t&R&k&n&r&t&u&w&{'T'b'r(T(V(](d(x(z)O)}*i+X+],p,s,x-i-q.P.V.t.{/n0]0l0r1S1r2S2T2V2X2[2_2a3Q3W3l4z6T6e6f6i6|8t9T9_!r)_$Z$n'X)s-U-X/V2p4T5w6s:Z:mSQ,m&oQ0x+gQ3`.gQ7Y3dR9e7[!b$Tc#Y%q(S(Y(t(y)Z)[)`)g+x-x-}.S.U.s.v/b0p3O3V3k3{5X5c6{7Q7a9]:oS)^)q-Z.|2k2n3p4P4X6u7b7k7l8k9X9g9m9n;W;`=vQ>X>ZR>Y>['QkOPWXYZstuvw!Z!`!g!o#S#W#Z#d#o#u#x#{$O$P$Q$R$S$T$U$V$W$X$Z$_$a$e$n%m%t&R&k&n&o&r&t&u&w&{'T'X'b'r(T(V(](d(x(z)O)s)}*i+X+]+g,p,s,x-U-X-i-q.P.V.g.t.{/V/n0]0l0r1S1r2S2T2V2X2[2_2a2p3Q3W3d3l4T4z5w6T6e6f6i6s6|7[8t9T9_:Z:mSS$oh$pR4U/U'XgOPWXYZhstuvw!Z!`!g!o#S#W#Z#d#o#u#x#{$O$P$Q$R$S$T$U$V$W$X$Z$_$a$e$n$p%m%t&R&k&n&o&r&t&u&w&{'T'X'b'r(T(V(](d(x(z)O)s)}*i+X+]+g,p,s,x-U-X-i-q.P.V.g.t.{/U/V/n0]0l0r1S1r2S2T2V2X2[2_2a2p3Q3W3d3l4T4z5w6T6e6f6i6s6|7[8t9T9_:Z:mST$kf$qQ$ifS)j$l)nR)v$qT$jf$qT)l$l)n'XhOPWXYZhstuvw!Z!`!g!o#S#W#Z#d#o#u#x#{$O$P$Q$R$S$T$U$V$W$X$Z$_$a$e$n$p%m%t&R&k&n&o&r&t&u&w&{'T'X'b'r(T(V(](d(x(z)O)s)}*i+X+]+g,p,s,x-U-X-i-q.P.V.g.t.{/U/V/n0]0l0r1S1r2S2T2V2X2[2_2a2p3Q3W3d3l4T4z5w6T6e6f6i6s6|7[8t9T9_:Z:mST$oh$pQ$rhR)u$p%^jOPWXYZstuvw!Z!`!g!o#S#W#Z#d#o#u#x#{$O$P$Q$R$S$T$U$V$W$X$_$a$e%m%t&R&k&n&o&r&t&u&w&{'T'b'r(T(V(](d(x(z)O)}*i+X+]+g,p,s,x-i-q.P.V.g.t.{/n0]0l0r1S1r2S2T2V2X2[2_2a3Q3W3d3l4z6T6e6f6i6|7[8t9T9_!s>Q$Z$n'X)s-U-X/V2p4T5w6s:Z:mS#glOPXZst!Z!`!o#S#d#o#{$n%m&k&n&o&r&t&u&w&{'T'b)O)s*i+]+g,p,s,x-i.g/V/n0]0l1r2S2T2V2X2[2_2a3d4T4z6T6e6f6i7[8t9T!U%Ri$d%O%Q%^%_%c*R*T*a*w*x/P/x0`0b0i0j0o4_5Q8V9p>P>X>Y#f(w#v$b$c$x${)y*V*Y*g+f+i,S,V.f/d/m/y/{1f1i1q3c4^4j4o5[5_6S7W7v8Q8[8q9b9y:P:`:r;Q;[;d;k]>^Q+T%aQ/c*Oo4OP>X>YQ*c$zU*l$|*Z*oQ+U%bQ0W*m#f=q#v$b$c$x${)y*V*Y*g+f+i,S,V.f/d/m/y/{1f1i1q3c4^4j4o5[5_6S7W7v8Q8[8q9b9y:P:`:r;Q;[;d;k]>^n=rTQ=x>UQ=y>VR=z>W!U%Ri$d%O%Q%^%_%c*R*T*a*w*x/P/x0`0b0i0j0o4_5Q8V9p>P>X>Y#f(w#v$b$c$x${)y*V*Y*g+f+i,S,V.f/d/m/y/{1f1i1q3c4^4j4o5[5_6S7W7v8Q8[8q9b9y:P:`:r;Q;[;d;k]>^o4OP>X>Y>]>^Q,U&]Q1h,WQ5s1gR8h5tV*n$|*Z*oU*n$|*Z*oT5z1o5{S0P*i/nQ4w0]T8S4z:]Q+j%xQ0V*lQ1O+kQ1u,aQ6W1vQ8v6XQ:c8wR;^:d!U%Oi$d%O%Q%^%_%c*R*T*a*w*x/P/x0`0b0i0j0o4_5Q8V9p>P>X>Yx*R$v)e*S*u+V/v0d0e4R4g5R5S5W7p8U:R:x=p=}>OS0`*t0a#f]>^nZ>[`=T3}7c7f7j9h:t:w;yS=_.l3iT=`7e9k!U%Qi$d%O%Q%^%_%c*R*T*a*w*x/P/x0`0b0i0j0o4_5Q8V9p>P>X>Y|*T$v)e*U*t+V/g/v0d0e4R4g4|5R5S5W7p8U:R:x=p=}>OS0b*u0c#f]>^nZ>[d=V3}7d7e7j9h9i:t:u:w;yS=a.m3jT=b7f9lrnOXst!V!Z#d%m&i&r&t&u&w,s,x2[2_Q&f!UR,p&ornOXst!V!Z#d%m&i&r&t&u&w,s,x2[2_R&f!UQ,Y&^R1d,RsnOXst!V!Z#d%m&i&r&t&u&w,s,x2[2_Q1p,_S6R1s1tU8p6P6Q6US:_8r8sS;Y:^:aQ;m;ZR;w;nQ&m!VR,i&iR6_1|R:f8yW&Q|&V&W,OR1Z+vQ&r!WR,s&sR,y&xT2],x2_R,}&yQ,|&yR2f,}Q'y!{R-y'ySsOtQ#dXT%ps#dQ#OTR'{#OQ#RUR'}#RQ){$uR/`){Q#UVR(Q#UQ#XWU(W#X(X.QQ(X#YR.Q(YQ-^'YR2r-^Q.u(yS3m.u3nR3n.vQ-e'`R2v-eY!rQ'`-e1o5{R'j!rQ/Q)eR4S/QU#_W%h*YU(_#_(`.RQ(`#`R.R(ZQ-a']R2t-at`OXst!V!Z#d%m&i&k&r&t&u&w,s,x2[2_S#hZ%eU#r`#h.[R.[(jQ(k#jQ.X(gW.a(k.X3X7RQ3X.YR7R3YQ)n$lR/W)nQ$phR)t$pQ$`cU)a$`-|O>Z>[Q/z*eU4k/z4m7xQ4m/|R7x4lS*o$|*ZR0Y*ox*S$v)e*t*u+V/v0d0e4R4g5R5S5W7p8U:R:x=p=}>O!d.j(u)c*[*e.l.m.q/_/k/|0v1e3h4[4h4l5r7]7`7w7z8X8Z9t9|:S:};R;e;j;v>Z>[U/h*S.j7ca7c3}7e7f7j9h:t:w;yQ0a*tQ3i.lU4}0a3i9kR9k7e|*U$v)e*t*u+V/g/v0d0e4R4g4|5R5S5W7p8U:R:x=p=}>O!h.k(u)c*[*e.l.m.q/_/k/|0v1e3f3h4[4h4l5r7]7^7`7w7z8X8Z9t9|:S:};R;e;j;v>Z>[U/j*U.k7de7d3}7e7f7j9h9i:t:u:w;yQ0c*uQ3j.mU5P0c3j9lR9l7fQ*z%UR0g*zQ5]0vR8Y5]Q+_%kR0u+_Q5v1jS8j5v:[R:[8kQ,[&_R1m,[Q5{1oR8m5{Q1{,fS6]1{8zR8z6_Q1U+rW5h1U5j8a:VQ5j1XQ8a5iR:V8bQ+w&QR1[+wQ2_,xR6m2_YrOXst#dQ&v!ZQ+a%mQ,r&rQ,t&tQ,u&uQ,w&wQ2Y,sS2],x2_R6l2[Q%opQ&z!_Q&}!aQ'P!bQ'R!cQ'q!uQ+`%lQ+l%zQ,Q&XQ,h&mQ-P&|W-p'k's't'wQ-w'oQ0X*nQ1P+mQ1c,PS2O,i,lQ2g-OQ2h-RQ2i-SQ2}-oW3P-r-s-v-xQ5a1QQ5m1_Q5q1eQ6V1uQ6a2QQ6k2ZU6z3O3R3UQ6}3SQ8]5bQ8e5oQ8g5rQ8l5zQ8u6WQ8{6`S9[6{7PQ9^7OQ:W8cQ:b8vQ:g8|Q:n9]Q;U:XQ;]:cQ;a:oQ;l;VR;o;^Q%zyQ'd!iQ'o!uU+m%{%|%}Q-W'VU-k'e'f'gS-o'k'uQ0Q*jS1Q+n+oQ2o-YS2{-l-mQ3S-tS4p0R0UQ5b1RQ6v2uQ6y2|Q7O3TU7{4r4s4vQ9z7}R;O9{S$wi>PR*{%VU%Ui%V>PR0f*yQ$viS(u#v+iS)c$b$cQ)e$dQ*[$xS*e${*YQ*t%OQ*u%QQ+Q%^Q+R%_Q+V%cQ.lPQ=}>XQ>O>YQ>Z>]R>[>^Q+O%]Q.nSR#[WR'Z!el!tQ!r!v!y!z'`'l'm'n-e-u1o5{5}S'V!e-]U*j$|*Z*oS-Y'W'_S0U*k*qQ0^*rQ2u-cQ4v0[R4{0_R({#xQ!fQT-d'`-e]!qQ!r'`-e1o5{Q#p]R'i < TypeParamList in out const TypeDefinition extends ThisType this LiteralType ArithOp Number BooleanLiteral TemplateType InterpolationEnd Interpolation InterpolationStart NullType null VoidType void TypeofType typeof MemberExpression . PropertyName [ TemplateString Escape Interpolation super RegExp ] ArrayExpression Spread , } { ObjectExpression Property async get set PropertyDefinition Block : NewTarget new NewExpression ) ( ArgList UnaryExpression delete LogicOp BitOp YieldExpression yield AwaitExpression await ParenthesizedExpression ClassExpression class ClassBody MethodDeclaration Decorator @ MemberExpression PrivatePropertyName CallExpression TypeArgList CompareOp < declare Privacy static abstract override PrivatePropertyDefinition PropertyDeclaration readonly accessor Optional TypeAnnotation Equals StaticBlock FunctionExpression ArrowFunction ParamList ParamList ArrayPattern ObjectPattern PatternProperty Privacy readonly Arrow MemberExpression BinaryExpression ArithOp ArithOp ArithOp ArithOp BitOp CompareOp instanceof satisfies CompareOp BitOp BitOp BitOp LogicOp LogicOp ConditionalExpression LogicOp LogicOp AssignmentExpression UpdateOp PostfixExpression CallExpression InstantiationExpression TaggedTemplateExpression DynamicImport import ImportMeta JSXElement JSXSelfCloseEndTag JSXSelfClosingTag JSXIdentifier JSXBuiltin JSXIdentifier JSXNamespacedName JSXMemberExpression JSXSpreadAttribute JSXAttribute JSXAttributeValue JSXEscape JSXEndTag JSXOpenTag JSXFragmentTag JSXText JSXEscape JSXStartCloseTag JSXCloseTag PrefixCast < ArrowFunction TypeParamList SequenceExpression InstantiationExpression KeyofType keyof UniqueType unique ImportType InferredType infer TypeName ParenthesizedType FunctionSignature ParamList NewSignature IndexedType TupleType Label ArrayType ReadonlyType ObjectType MethodType PropertyType IndexSignature PropertyDefinition CallSignature TypePredicate asserts is NewSignature new UnionType LogicOp IntersectionType LogicOp ConditionalType ParameterizedType ClassDeclaration abstract implements type VariableDeclaration let var using TypeAliasDeclaration InterfaceDeclaration interface EnumDeclaration enum EnumBody NamespaceDeclaration namespace module AmbientDeclaration declare GlobalDeclaration global ClassDeclaration ClassBody AmbientFunctionDeclaration ExportGroup VariableName VariableName ImportDeclaration defer ImportGroup ForStatement for ForSpec ForInSpec ForOfSpec of WhileStatement while WithStatement with DoStatement do IfStatement if else SwitchStatement switch SwitchBody CaseLabel case DefaultLabel TryStatement try CatchClause catch FinallyClause finally ReturnStatement return ThrowStatement throw BreakStatement break ContinueStatement continue DebuggerStatement debugger LabeledStatement ExpressionStatement SingleExpression SingleClassItem`,maxTerm:380,context:k,nodeProps:[[`isolate`,-8,5,6,14,37,39,51,53,55,``],[`group`,-26,9,17,19,68,207,211,215,216,218,221,224,234,237,243,245,247,249,252,258,264,266,268,270,272,274,275,`Statement`,-34,13,14,32,35,36,42,51,54,55,57,62,70,72,76,80,82,84,85,110,111,120,121,136,139,141,142,143,144,145,147,148,167,169,171,`Expression`,-23,31,33,37,41,43,45,173,175,177,178,180,181,182,184,185,186,188,189,190,201,203,205,206,`Type`,-3,88,103,109,`ClassItem`],[`openedBy`,23,`<`,38,`InterpolationStart`,56,`[`,60,`{`,73,`(`,160,`JSXStartCloseTag`],[`closedBy`,-2,24,168,`>`,40,`InterpolationEnd`,50,`]`,61,`}`,74,`)`,165,`JSXEndTag`]],propSources:[xe],skippedNodes:[0,5,6,278],repeatNodeCount:37,tokenData:"$Fq07[R!bOX%ZXY+gYZ-yZ[+g[]%Z]^.c^p%Zpq+gqr/mrs3cst:_tuEruvJSvwLkwx! Yxy!'iyz!(sz{!)}{|!,q|}!.O}!O!,q!O!P!/Y!P!Q!9j!Q!R#:O!R![#<_![!]#I_!]!^#Jk!^!_#Ku!_!`$![!`!a$$v!a!b$*T!b!c$,r!c!}Er!}#O$-|#O#P$/W#P#Q$4o#Q#R$5y#R#SEr#S#T$7W#T#o$8b#o#p$x#r#s$@U#s$f%Z$f$g+g$g#BYEr#BY#BZ$A`#BZ$ISEr$IS$I_$A`$I_$I|Er$I|$I}$Dk$I}$JO$Dk$JO$JTEr$JT$JU$A`$JU$KVEr$KV$KW$A`$KW&FUEr&FU&FV$A`&FV;'SEr;'S;=`I|<%l?HTEr?HT?HU$A`?HUOEr(n%d_$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z&j&hT$i&jO!^&c!_#o&c#p;'S&c;'S;=`&w<%lO&c&j&zP;=`<%l&c'|'U]$i&j(Z!bOY&}YZ&cZw&}wx&cx!^&}!^!_'}!_#O&}#O#P&c#P#o&}#o#p'}#p;'S&};'S;=`(l<%lO&}!b(SU(Z!bOY'}Zw'}x#O'}#P;'S'};'S;=`(f<%lO'}!b(iP;=`<%l'}'|(oP;=`<%l&}'[(y]$i&j(WpOY(rYZ&cZr(rrs&cs!^(r!^!_)r!_#O(r#O#P&c#P#o(r#o#p)r#p;'S(r;'S;=`*a<%lO(rp)wU(WpOY)rZr)rs#O)r#P;'S)r;'S;=`*Z<%lO)rp*^P;=`<%l)r'[*dP;=`<%l(r#S*nX(Wp(Z!bOY*gZr*grs'}sw*gwx)rx#O*g#P;'S*g;'S;=`+Z<%lO*g#S+^P;=`<%l*g(n+dP;=`<%l%Z07[+rq$i&j(Wp(Z!b'|0/lOX%ZXY+gYZ&cZ[+g[p%Zpq+gqr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p$f%Z$f$g+g$g#BY%Z#BY#BZ+g#BZ$IS%Z$IS$I_+g$I_$JT%Z$JT$JU+g$JU$KV%Z$KV$KW+g$KW&FU%Z&FU&FV+g&FV;'S%Z;'S;=`+a<%l?HT%Z?HT?HU+g?HUO%Z07[.ST(X#S$i&j'}0/lO!^&c!_#o&c#p;'S&c;'S;=`&w<%lO&c07[.n_$i&j(Wp(Z!b'}0/lOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z)3p/x`$i&j!p),Q(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`0z!`#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z(KW1V`#v(Ch$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`2X!`#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z(KW2d_#v(Ch$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'At3l_(V':f$i&j(Z!bOY4kYZ5qZr4krs7nsw4kwx5qx!^4k!^!_8p!_#O4k#O#P5q#P#o4k#o#p8p#p;'S4k;'S;=`:X<%lO4k(^4r_$i&j(Z!bOY4kYZ5qZr4krs7nsw4kwx5qx!^4k!^!_8p!_#O4k#O#P5q#P#o4k#o#p8p#p;'S4k;'S;=`:X<%lO4k&z5vX$i&jOr5qrs6cs!^5q!^!_6y!_#o5q#o#p6y#p;'S5q;'S;=`7h<%lO5q&z6jT$d`$i&jO!^&c!_#o&c#p;'S&c;'S;=`&w<%lO&c`6|TOr6yrs7]s;'S6y;'S;=`7b<%lO6y`7bO$d``7eP;=`<%l6y&z7kP;=`<%l5q(^7w]$d`$i&j(Z!bOY&}YZ&cZw&}wx&cx!^&}!^!_'}!_#O&}#O#P&c#P#o&}#o#p'}#p;'S&};'S;=`(l<%lO&}!r8uZ(Z!bOY8pYZ6yZr8prs9hsw8pwx6yx#O8p#O#P6y#P;'S8p;'S;=`:R<%lO8p!r9oU$d`(Z!bOY'}Zw'}x#O'}#P;'S'};'S;=`(f<%lO'}!r:UP;=`<%l8p(^:[P;=`<%l4k%9[:hh$i&j(Wp(Z!bOY%ZYZ&cZq%Zqr`#P#o`x!^=^!^!_?q!_#O=^#O#P>`#P#o=^#o#p?q#p;'S=^;'S;=`@h<%lO=^&n>gXWS$i&jOY>`YZ&cZ!^>`!^!_?S!_#o>`#o#p?S#p;'S>`;'S;=`?k<%lO>`S?XSWSOY?SZ;'S?S;'S;=`?e<%lO?SS?hP;=`<%l?S&n?nP;=`<%l>`!f?xWWS(Z!bOY?qZw?qwx?Sx#O?q#O#P?S#P;'S?q;'S;=`@b<%lO?q!f@eP;=`<%l?q(Q@kP;=`<%l=^'`@w]WS$i&j(WpOY@nYZ&cZr@nrs>`s!^@n!^!_Ap!_#O@n#O#P>`#P#o@n#o#pAp#p;'S@n;'S;=`Bg<%lO@ntAwWWS(WpOYApZrAprs?Ss#OAp#O#P?S#P;'SAp;'S;=`Ba<%lOAptBdP;=`<%lAp'`BjP;=`<%l@n#WBvYWS(Wp(Z!bOYBmZrBmrs?qswBmwxApx#OBm#O#P?S#P;'SBm;'S;=`Cf<%lOBm#WCiP;=`<%lBm(rCoP;=`<%l^!Q^$i&j!X7`OY!=yYZ&cZ!P!=y!P!Q!>|!Q!^!=y!^!_!@c!_!}!=y!}#O!CW#O#P!Dy#P#o!=y#o#p!@c#p;'S!=y;'S;=`!Ek<%lO!=y|#X#Z&c#Z#[!>|#[#]&c#]#^!>|#^#a&c#a#b!>|#b#g&c#g#h!>|#h#i&c#i#j!>|#j#k!>|#k#m&c#m#n!>|#n#o&c#p;'S&c;'S;=`&w<%lO&c7`!@hX!X7`OY!@cZ!P!@c!P!Q!AT!Q!}!@c!}#O!Ar#O#P!Bq#P;'S!@c;'S;=`!CQ<%lO!@c7`!AYW!X7`#W#X!AT#Z#[!AT#]#^!AT#a#b!AT#g#h!AT#i#j!AT#j#k!AT#m#n!AT7`!AuVOY!ArZ#O!Ar#O#P!B[#P#Q!@c#Q;'S!Ar;'S;=`!Bk<%lO!Ar7`!B_SOY!ArZ;'S!Ar;'S;=`!Bk<%lO!Ar7`!BnP;=`<%l!Ar7`!BtSOY!@cZ;'S!@c;'S;=`!CQ<%lO!@c7`!CTP;=`<%l!@c^!Ezl$i&j(Z!b!X7`OY&}YZ&cZw&}wx&cx!^&}!^!_'}!_#O&}#O#P&c#P#W&}#W#X!Eq#X#Z&}#Z#[!Eq#[#]&}#]#^!Eq#^#a&}#a#b!Eq#b#g&}#g#h!Eq#h#i&}#i#j!Eq#j#k!Eq#k#m&}#m#n!Eq#n#o&}#o#p'}#p;'S&};'S;=`(l<%lO&}8r!GyZ(Z!b!X7`OY!GrZw!Grwx!@cx!P!Gr!P!Q!Hl!Q!}!Gr!}#O!JU#O#P!Bq#P;'S!Gr;'S;=`!J|<%lO!Gr8r!Hse(Z!b!X7`OY'}Zw'}x#O'}#P#W'}#W#X!Hl#X#Z'}#Z#[!Hl#[#]'}#]#^!Hl#^#a'}#a#b!Hl#b#g'}#g#h!Hl#h#i'}#i#j!Hl#j#k!Hl#k#m'}#m#n!Hl#n;'S'};'S;=`(f<%lO'}8r!JZX(Z!bOY!JUZw!JUwx!Arx#O!JU#O#P!B[#P#Q!Gr#Q;'S!JU;'S;=`!Jv<%lO!JU8r!JyP;=`<%l!JU8r!KPP;=`<%l!Gr>^!KZ^$i&j(Z!bOY!KSYZ&cZw!KSwx!CWx!^!KS!^!_!JU!_#O!KS#O#P!DR#P#Q!^!LYP;=`<%l!KS>^!L`P;=`<%l!_#c#d#Bq#d#l%Z#l#m#Es#m#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#_#c#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#>j_$i&j(Wp(Z!bs'9tOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#?rd$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q!R#AQ!R!S#AQ!S!^%Z!^!_*g!_#O%Z#O#P&c#P#R%Z#R#S#AQ#S#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#A]f$i&j(Wp(Z!bs'9tOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q!R#AQ!R!S#AQ!S!^%Z!^!_*g!_#O%Z#O#P&c#P#R%Z#R#S#AQ#S#b%Z#b#c#>_#c#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#Bzc$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q!Y#DV!Y!^%Z!^!_*g!_#O%Z#O#P&c#P#R%Z#R#S#DV#S#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#Dbe$i&j(Wp(Z!bs'9tOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q!Y#DV!Y!^%Z!^!_*g!_#O%Z#O#P&c#P#R%Z#R#S#DV#S#b%Z#b#c#>_#c#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#E|g$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q![#Ge![!^%Z!^!_*g!_!c%Z!c!i#Ge!i#O%Z#O#P&c#P#R%Z#R#S#Ge#S#T%Z#T#Z#Ge#Z#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#Gpi$i&j(Wp(Z!bs'9tOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q![#Ge![!^%Z!^!_*g!_!c%Z!c!i#Ge!i#O%Z#O#P&c#P#R%Z#R#S#Ge#S#T%Z#T#Z#Ge#Z#b%Z#b#c#>_#c#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z*)x#Il_!g$b$i&j$O)Lv(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z)[#Jv_al$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z04f#LS^h#)`#R-v$?V_!^(CdvBr$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z?O$@a_!q7`$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z07[$Aq|$i&j(Wp(Z!b'|0/l$]#t(T,2j(e$I[OX%ZXY+gYZ&cZ[+g[p%Zpq+gqr%Zrs&}st%ZtuEruw%Zwx(rx}%Z}!OGv!O!Q%Z!Q![Er![!^%Z!^!_*g!_!c%Z!c!}Er!}#O%Z#O#P&c#P#R%Z#R#SEr#S#T%Z#T#oEr#o#p*g#p$f%Z$f$g+g$g#BYEr#BY#BZ$A`#BZ$ISEr$IS$I_$A`$I_$JTEr$JT$JU$A`$JU$KVEr$KV$KW$A`$KW&FUEr&FU&FV$A`&FV;'SEr;'S;=`I|<%l?HTEr?HT?HU$A`?HUOEr07[$D|k$i&j(Wp(Z!b'}0/l$]#t(T,2j(e$I[OY%ZYZ&cZr%Zrs&}st%ZtuEruw%Zwx(rx}%Z}!OGv!O!Q%Z!Q![Er![!^%Z!^!_*g!_!c%Z!c!}Er!}#O%Z#O#P&c#P#R%Z#R#SEr#S#T%Z#T#oEr#o#p*g#p$g%Z$g;'SEr;'S;=`I|<%lOEr",tokenizers:[j,M,N,be,2,3,4,5,6,7,8,9,10,11,12,13,14,A,new p("$S~RRtu[#O#Pg#S#T#|~_P#o#pb~gOx~~jVO#i!P#i#j!U#j#l!P#l#m!q#m;'S!P;'S;=`#v<%lO!P~!UO!U~~!XS!Q![!e!c!i!e#T#Z!e#o#p#Z~!hR!Q![!q!c!i!q#T#Z!q~!tR!Q![!}!c!i!}#T#Z!}~#QR!Q![!P!c!i!P#T#Z!P~#^R!Q![#g!c!i#g#T#Z#g~#jS!Q![#g!c!i#g#T#Z#g#q#r!P~#yP;=`<%l!P~$RO(c~~",141,340),new p(`j~RQYZXz{^~^O(Q~~aP!P!Qd~iO(R~~`,25,323)],topRules:{Script:[0,7],SingleExpression:[1,276],SingleClassItem:[2,277]},dialects:{jsx:0,ts:15175},dynamicPrecedences:{80:1,82:1,94:1,169:1,199:1},specialized:[{term:327,get:e=>Se[e]||-1},{term:343,get:e=>Ce[e]||-1},{term:95,get:e=>we[e]||-1}],tokenPrec:15201}),Ee=e({autoCloseTags:()=>$,javascript:()=>Z,javascriptLanguage:()=>W,jsxLanguage:()=>q,localCompletionSource:()=>U,snippets:()=>F,tsxLanguage:()=>J,typescriptLanguage:()=>K,typescriptSnippets:()=>I}),F=[h("function ${name}(${params}) {\n ${}\n}",{label:`function`,detail:`definition`,type:`keyword`}),h("for (let ${index} = 0; ${index} < ${bound}; ${index}++) {\n ${}\n}",{label:`for`,detail:`loop`,type:`keyword`}),h("for (let ${name} of ${collection}) {\n ${}\n}",{label:`for`,detail:`of loop`,type:`keyword`}),h(`do { \${} } while (\${})`,{label:`do`,detail:`loop`,type:`keyword`}),h(`while (\${}) { \${} diff --git a/mcp/src/agents_remember/package_data/dashboard/assets/dist-BJusfjLN.js b/mcp/src/agents_remember/package_data/dashboard/assets/dist-ChRcjm7W.js similarity index 99% rename from mcp/src/agents_remember/package_data/dashboard/assets/dist-BJusfjLN.js rename to mcp/src/agents_remember/package_data/dashboard/assets/dist-ChRcjm7W.js index b5056685..c78c80be 100644 --- a/mcp/src/agents_remember/package_data/dashboard/assets/dist-BJusfjLN.js +++ b/mcp/src/agents_remember/package_data/dashboard/assets/dist-ChRcjm7W.js @@ -1 +1 @@ -import{$ as e,C as t,D as n,b as r,d as i,f as a,i as o,m as s,s as c,t as ee,v as l,x as u}from"./index-CLJ0aycO.js";import{i as d,n as f,r as p}from"./dist-B2R-c1vX.js";var m=145,h=1,te=146,ne=147,g=2,_=148,v=3,re=4,y=[9,10,11,12,13,32,133,160,5760,8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,8232,8233,8239,8287,12288],b=58,x=40,S=95,ie=91,C=45,ae=46,oe=35,se=37,w=38,T=92,E=10,D=42;function O(e){return e>=65&&e<=90||e>=97&&e<=122||e>=161}function k(e){return e>=48&&e<=57}function A(e){return k(e)||e>=97&&e<=102||e>=65&&e<=70}var j=(e,t,n)=>(r,i)=>{for(let a=!1,o=0,s=0;;s++){let{next:c}=r;if(O(c)||c==C||c==S||a&&k(c))!a&&(c!=C||s>0)&&(a=!0),o===s&&c==C&&o++,r.advance();else if(c==T&&r.peek(1)!=E){if(r.advance(),A(r.next)){do r.advance();while(A(r.next));r.next==32&&r.advance()}else r.next>-1&&r.advance();a=!0}else{a&&r.acceptToken(o==2&&i.canShift(g)?t:c==x?n:e);break}}},M=new f(j(te,g,ne),{contextual:!0}),N=new f(j(_,v,re),{contextual:!0}),P=new f(e=>{if(y.includes(e.peek(-1))){let{next:t}=e;(O(t)||t==S||t==oe||t==ae||t==D||t==ie||t==b&&O(e.peek(1))||t==C||t==w)&&e.acceptToken(m)}}),F=new f(e=>{if(!y.includes(e.peek(-1))){let{next:t}=e;if(t==se&&(e.advance(),e.acceptToken(h)),O(t)){do e.advance();while(O(e.next)||k(e.next));e.acceptToken(h)}}}),I=r({"AtKeyword import charset namespace keyframes media supports font-feature-values":u.definitionKeyword,"from to selector scope MatchFlag":u.keyword,NamespaceName:u.namespace,KeyframeName:u.labelName,KeyframeRangeName:u.operatorKeyword,TagName:u.tagName,ClassName:u.className,PseudoClassName:u.constant(u.className),IdName:u.labelName,"FeatureName PropertyName":u.propertyName,AttributeName:u.attributeName,NumberLiteral:u.number,KeywordQuery:u.keyword,UnaryQueryOp:u.operatorKeyword,"CallTag ValueName FontName":u.atom,VariableName:u.variableName,Callee:u.operatorKeyword,Unit:u.unit,"UniversalSelector NestingSelector":u.definitionOperator,"MatchOp CompareOp":u.compareOperator,"ChildOp SiblingOp, LogicOp":u.logicOperator,BinOp:u.arithmeticOperator,Important:u.modifier,Comment:u.blockComment,ColorLiteral:u.color,"ParenthesizedContent StringLiteral":u.string,":":u.punctuation,"PseudoOp #":u.derefOperator,"; , |":u.separator,"( )":u.paren,"[ ]":u.squareBracket,"{ }":u.brace}),L={__proto__:null,lang:44,"nth-child":44,"nth-last-child":44,"nth-of-type":44,"nth-last-of-type":44,dir:44,"host-context":44,if:90,url:152,"url-prefix":152,domain:152,regexp:152},R={__proto__:null,or:104,and:104,not:112,only:112,layer:206},z={__proto__:null,selector:118,style:124,layer:202},ce={__proto__:null,"@import":198,"@media":210,"@charset":214,"@namespace":218,"@keyframes":224,"@supports":236,"@scope":240,"@font-feature-values":246},B={__proto__:null,to:243},V=p.deserialize({version:14,states:"MlQYQdOOO#}QdOOP$UO`OOO%OQaO'#CfOOQP'#Ce'#CeO%VQdO'#CgO%[Q`O'#CgO%aQaO'#FnO&XQdO'#CkO&xQaO'#CcO'SQdO'#CnO'_QdO'#EOO'dQdO'#EQO'oQdO'#EXO'oQdO'#E[OOQP'#Fn'#FnO)RQhO'#E}OOQS'#Fm'#FmOOQS'#FQ'#FQQYQdOOO)YQdO'#EbO*iQhO'#EhO)YQdO'#EjO*pQdO'#ElO*{QdO'#EoO)}QhO'#EuO+TQdO'#EwO+`QdO'#EzO+eQaO'#CfO+lQ`O'#E_O+qQ`O'#F{O+|QdO'#F{QOQ`OOP,WO&jO'#CaPOOO)CA])CA]OOQP'#Ci'#CiOOQP,59R,59RO%VQdO,59ROOQP'#Cm'#CmOOQP,59V,59VO&XQdO,59VO,cQdO,59YO'_QdO,5:jO'dQdO,5:lO'oQdO,5:sO'oQdO,5:uO'oQdO,5:vO'oQdO'#FXO,nQ`O,58}O,vQdO'#E^OOQS,58},58}OOQP'#Cq'#CqOOQO'#D|'#D|OOQP,59Y,59YO,}Q`O,59YO-SQ`O,59YOOQP'#EP'#EPOOQP,5:j,5:jO-XQpO'#ERO-dQdO'#ESO-iQ`O'#ESO-nQpO,5:lO.XQaO,5:sO.oQaO,5:vOOQW'#D^'#D^O/nQhO'#DgO0RQhO,5;iO)}QhO'#DeO0`Q`O'#DnO0eQhO'#DxOOQW'#Ft'#FtOOQS,5;i,5;iO0jQ`O'#DhO0oQ`O'#DkOOQS-E9O-E9OOOQ['#Cv'#CvO0tQdO'#CwO1[QdO'#C}O1rQdO'#DQO2YQ!pO'#DSO4fQ!jO,5:|OOQO'#DX'#DXO-SQ`O'#DWO4vQ!nO'#FqO6|Q`O'#DYO7RQ`O'#DyOOQ['#Fq'#FqO7WQhO'#GOO7fQ`O,5;SO7kQ!bO,5;UOOQS'#En'#EnO7sQ`O,5;WO7xQdO,5;WOOQO'#Eq'#EqO8QQ`O,5;ZO8VQhO,5;aO'oQdO'#DjOOQS,5;c,5;cO0jQ`O,5;cO8_QdO,5;cOOQS'#F`'#F`O8gQdO'#E|O7fQ`O,5;fO8oQdO,5:yO9PQdO'#FZO9^Q`O,5lQhO'#DoOOQW,5:Y,5:YOOQW,5:d,5:dOOQW,5:S,5:SO>vQhO,5:VO?bQ!fO'#FrOOQS'#Fr'#FrOOQS'#FS'#FSO@rQdO,59cOOQ[,59c,59cOAYQdO,59iOOQ[,59i,59iOApQdO,59lOOQ[,59l,59lOOQ[,59n,59nO)YQdO,59pOBWQhO'#EdOOQW'#Ed'#EdOBuQ`O1G0hO4oQhO1G0hOOQ[,59r,59rO)}QhO'#D[OOQ[,59t,59tOBzQ#tO,5:eOCVQhO'#F]OCdQ`O,5vQhO'#DmOI_QhO'#DqOIgQhO'#DsOIlQhO'#FwOOQO'#Fw'#FwOItQ!bO'#DwOOQO'#Fy'#FyOOQO'#Fv'#FvOIyQ`O1G/qOOQS-E9Q-E9QOOQ[1G.}1G.}OOQ[1G/T1G/TOOQ[1G/W1G/WOOQ[1G/[1G/[OJOQdO,5;OOOQS7+&S7+&SOJTQ`O7+&SOJYQhO'#D]OJbQ`O,59vO)}QhO,59vOOQ[1G0P1G0POJjQ`O1G0POJoQhO,5;wOOQO-E9Z-E9ZOOQS7+&^7+&^OJ}QbO'#DSOOQO'#Et'#EtOK]Q`O'#EsOOQO'#Es'#EsOKhQ`O'#F^OKpQdO,5;^OOQS,5;^,5;^OOQ[1G/p1G/pOOQS7+&i7+&iO7fQ`O7+&iOK{Q!fO'#FYO)YQdO'#FYOMSQdO7+&POOQO7+&P7+&POOQO,5:{,5:{OOQO1G1a1G1aOMgQ!bO<vQhO'#DrOOQO,5:],5:]O! hQhO,5:_OGUQhO,5:cOOQW7+%]7+%]OOQO'#Ef'#EfO! pQ`O1G0jOOQS<xAN>xO!#zQ`OAN>xO!$PQaO,5;rOOQO-E9U-E9UO!$ZQdO,5;qOOQO-E9T-E9TOOQW<vQhO'#DuOOQO1G/y1G/yO!%vQ!bO1G/}OJOQdO'#F[O!&OQ`O7+&UOOQW7+&U7+&UO!&WQ!bO1G/cOOQ[7+$|7+$|O!&cQhO7+$|P!&jQ`O'#FTOOQO,5;y,5;yOOQO-E9]-E9]OOQS1G1d1G1dOOQPG24dG24dO!&oQ`OAN>ZO)YQdO1G1[O!&tQ`O7+'jOOQO1G/x1G/xO!&|Q`O,5:aO!$eQhO7+%iOOQO,5;v,5;vOOQO-E9Y-E9YOOQW<Q!]!^>|!^!_?_!_!`@Z!`!a@n!a!b%Z!b!cAo!c!k%Z!k!lC|!l!u%Z!u!vC|!v!}%Z!}#OD_#O#P%Z#P#QDp#Q#R2X#R#]%Z#]#^ER#^#g%Z#g#hC|#h#o%Z#o#pIf#p#qIw#q#rJ`#r#sJq#s#y%Z#y#z&R#z$f%Z$f$g&R$g#BY%Z#BY#BZ&R#BZ$IS%Z$IS$I_&R$I_$I|%Z$I|$JO&R$JO$JT%Z$JT$JU&R$JU$KV%Z$KV$KW&R$KW&FU%Z&FU&FV&R&FV;'S%Z;'S;=`KY<%lO%Z`%^SOy%jz;'S%j;'S;=`%{<%lO%j`%oS!o`Oy%jz;'S%j;'S;=`%{<%lO%j`&OP;=`<%l%j~&Wh$[~OX%jX^'r^p%jpq'rqy%jz#y%j#y#z'r#z$f%j$f$g'r$g#BY%j#BY#BZ'r#BZ$IS%j$IS$I_'r$I_$I|%j$I|$JO'r$JO$JT%j$JT$JU'r$JU$KV%j$KV$KW'r$KW&FU%j&FU&FV'r&FV;'S%j;'S;=`%{<%lO%j~'yh$[~!o`OX%jX^'r^p%jpq'rqy%jz#y%j#y#z'r#z$f%j$f$g'r$g#BY%j#BY#BZ'r#BZ$IS%j$IS$I_'r$I_$I|%j$I|$JO'r$JO$JT%j$JT$JU'r$JU$KV%j$KV$KW'r$KW&FU%j&FU&FV'r&FV;'S%j;'S;=`%{<%lO%jj)jS$qYOy%jz;'S%j;'S;=`%{<%lO%j~)yWOY)vZr)vrs*cs#O)v#O#P*h#P;'S)v;'S;=`+d<%lO)v~*hOw~~*kRO;'S)v;'S;=`*t;=`O)v~*wXOY)vZr)vrs*cs#O)v#O#P*h#P;'S)v;'S;=`+d;=`<%l)v<%lO)v~+gP;=`<%l)vj+oYmYOy%jz!Q%j!Q![,_![!c%j!c!i,_!i#T%j#T#Z,_#Z;'S%j;'S;=`%{<%lO%jj,dY!o`Oy%jz!Q%j!Q![-S![!c%j!c!i-S!i#T%j#T#Z-S#Z;'S%j;'S;=`%{<%lO%jj-XY!o`Oy%jz!Q%j!Q![-w![!c%j!c!i-w!i#T%j#T#Z-w#Z;'S%j;'S;=`%{<%lO%jj.OYuY!o`Oy%jz!Q%j!Q![.n![!c%j!c!i.n!i#T%j#T#Z.n#Z;'S%j;'S;=`%{<%lO%jj.uYuY!o`Oy%jz!Q%j!Q![/e![!c%j!c!i/e!i#T%j#T#Z/e#Z;'S%j;'S;=`%{<%lO%jj/jY!o`Oy%jz!Q%j!Q![0Y![!c%j!c!i0Y!i#T%j#T#Z0Y#Z;'S%j;'S;=`%{<%lO%jj0aYuY!o`Oy%jz!Q%j!Q![1P![!c%j!c!i1P!i#T%j#T#Z1P#Z;'S%j;'S;=`%{<%lO%jj1UY!o`Oy%jz!Q%j!Q![1t![!c%j!c!i1t!i#T%j#T#Z1t#Z;'S%j;'S;=`%{<%lO%jj1{SuY!o`Oy%jz;'S%j;'S;=`%{<%lO%jd2[UOy%jz!_%j!_!`2n!`;'S%j;'S;=`%{<%lO%jd2uS!yS!o`Oy%jz;'S%j;'S;=`%{<%lO%jb3WS^QOy%jz;'S%j;'S;=`%{<%lO%j~3gWOY3dZw3dwx*cx#O3d#O#P4P#P;'S3d;'S;=`4{<%lO3d~4SRO;'S3d;'S;=`4];=`O3d~4`XOY3dZw3dwx*cx#O3d#O#P4P#P;'S3d;'S;=`4{;=`<%l3d<%lO3d~5OP;=`<%l3dj5WShYOy%jz;'S%j;'S;=`%{<%lO%j~5iOg~n5pUWQyWOy%jz!_%j!_!`2n!`;'S%j;'S;=`%{<%lO%jj6ZWyW#PQOy%jz!O%j!O!P6s!P!Q%j!Q![9x![;'S%j;'S;=`%{<%lO%jj6xU!o`Oy%jz!Q%j!Q![7[![;'S%j;'S;=`%{<%lO%jj7cY!o`$gYOy%jz!Q%j!Q![7[![!g%j!g!h8R!h#X%j#X#Y8R#Y;'S%j;'S;=`%{<%lO%jj8WY!o`Oy%jz{%j{|8v|}%j}!O8v!O!Q%j!Q![9_![;'S%j;'S;=`%{<%lO%jj8{U!o`Oy%jz!Q%j!Q![9_![;'S%j;'S;=`%{<%lO%jj9fU!o`$gYOy%jz!Q%j!Q![9_![;'S%j;'S;=`%{<%lO%jj:P[!o`$gYOy%jz!O%j!O!P7[!P!Q%j!Q![9x![!g%j!g!h8R!h#X%j#X#Y8R#Y;'S%j;'S;=`%{<%lO%jj:zS!dYOy%jz;'S%j;'S;=`%{<%lO%jj;]WyWOy%jz!O%j!O!P6s!P!Q%j!Q![9x![;'S%j;'S;=`%{<%lO%jj;zU`YOy%jz!Q%j!Q![7[![;'S%j;'S;=`%{<%lO%j~VUcYOy%jz![%j![!]>i!];'S%j;'S;=`%{<%lO%jj>pSdY!o`Oy%jz;'S%j;'S;=`%{<%lO%jj?RSnYOy%jz;'S%j;'S;=`%{<%lO%jh?dU!WWOy%jz!_%j!_!`?v!`;'S%j;'S;=`%{<%lO%jh?}S!WW!o`Oy%jz;'S%j;'S;=`%{<%lO%jl@bS!WW!ySOy%jz;'S%j;'S;=`%{<%lO%jj@uV!|Q!WWOy%jz!_%j!_!`?v!`!aA[!a;'S%j;'S;=`%{<%lO%jbAcS!|Q!o`Oy%jz;'S%j;'S;=`%{<%lO%jjArYOy%jz}%j}!OBb!O!c%j!c!}CP!}#T%j#T#oCP#o;'S%j;'S;=`%{<%lO%jjBgW!o`Oy%jz!c%j!c!}CP!}#T%j#T#oCP#o;'S%j;'S;=`%{<%lO%jjCW[lY!o`Oy%jz}%j}!OCP!O!Q%j!Q![CP![!c%j!c!}CP!}#T%j#T#oCP#o;'S%j;'S;=`%{<%lO%jhDRS!zWOy%jz;'S%j;'S;=`%{<%lO%jjDdSpYOy%jz;'S%j;'S;=`%{<%lO%jnDuSo^Oy%jz;'S%j;'S;=`%{<%lO%jjEWU!zWOy%jz#a%j#a#bEj#b;'S%j;'S;=`%{<%lO%jbEoU!o`Oy%jz#d%j#d#eFR#e;'S%j;'S;=`%{<%lO%jbFWU!o`Oy%jz#c%j#c#dFj#d;'S%j;'S;=`%{<%lO%jbFoU!o`Oy%jz#f%j#f#gGR#g;'S%j;'S;=`%{<%lO%jbGWU!o`Oy%jz#h%j#h#iGj#i;'S%j;'S;=`%{<%lO%jbGoU!o`Oy%jz#T%j#T#UHR#U;'S%j;'S;=`%{<%lO%jbHWU!o`Oy%jz#b%j#b#cHj#c;'S%j;'S;=`%{<%lO%jbHoU!o`Oy%jz#h%j#h#iIR#i;'S%j;'S;=`%{<%lO%jbIYS$pQ!o`Oy%jz;'S%j;'S;=`%{<%lO%jjIkSsYOy%jz;'S%j;'S;=`%{<%lO%jfI|U$cUOy%jz!_%j!_!`2n!`;'S%j;'S;=`%{<%lO%jjJeSrYOy%jz;'S%j;'S;=`%{<%lO%jfJvU#PQOy%jz!_%j!_!`2n!`;'S%j;'S;=`%{<%lO%j`K]P;=`<%l%Z",tokenizers:[P,F,M,N,1,2,3,4,new d("m~RRYZ[z{a~~g~aO$_~~dP!P!Qg~lO$`~~",28,152)],topRules:{StyleSheet:[0,6],Styles:[1,126]},dynamicPrecedences:{94:1},specialized:[{term:147,get:e=>L[e]||-1},{term:148,get:e=>R[e]||-1},{term:4,get:e=>z[e]||-1},{term:28,get:e=>ce[e]||-1},{term:146,get:e=>B[e]||-1}],tokenPrec:2405}),H=e({css:()=>$,cssCompletionSource:()=>Z,cssLanguage:()=>Q,defineCSSCompletionSource:()=>X}),U=null;function W(){if(!U&&typeof document==`object`&&document.body){let{style:e}=document.body,t=[],n=new Set;for(let r in e)r!=`cssText`&&r!=`cssFloat`&&typeof e[r]==`string`&&(/[A-Z]/.test(r)&&(r=r.replace(/[A-Z]/g,e=>`-`+e.toLowerCase())),n.has(r)||(t.push(r),n.add(r)));U=t.sort().map(e=>({type:`property`,label:e,apply:e+`: `}))}return U||[]}var G=`active.after.any-link.autofill.backdrop.before.checked.cue.default.defined.disabled.empty.enabled.file-selector-button.first.first-child.first-letter.first-line.first-of-type.focus.focus-visible.focus-within.fullscreen.has.host.host-context.hover.in-range.indeterminate.invalid.is.lang.last-child.last-of-type.left.link.marker.modal.not.nth-child.nth-last-child.nth-last-of-type.nth-of-type.only-child.only-of-type.optional.out-of-range.part.placeholder.placeholder-shown.read-only.read-write.required.right.root.scope.selection.slotted.target.target-text.valid.visited.where`.split(`.`).map(e=>({type:`class`,label:e})),K=`above.absolute.activeborder.additive.activecaption.after-white-space.ahead.alias.all.all-scroll.alphabetic.alternate.always.antialiased.appworkspace.asterisks.attr.auto.auto-flow.avoid.avoid-column.avoid-page.avoid-region.axis-pan.background.backwards.baseline.below.bidi-override.blink.block.block-axis.bold.bolder.border.border-box.both.bottom.break.break-all.break-word.bullets.button.button-bevel.buttonface.buttonhighlight.buttonshadow.buttontext.calc.capitalize.caps-lock-indicator.caption.captiontext.caret.cell.center.checkbox.circle.cjk-decimal.clear.clip.close-quote.col-resize.collapse.color.color-burn.color-dodge.column.column-reverse.compact.condensed.contain.content.contents.content-box.context-menu.continuous.copy.counter.counters.cover.crop.cross.crosshair.currentcolor.cursive.cyclic.darken.dashed.decimal.decimal-leading-zero.default.default-button.dense.destination-atop.destination-in.destination-out.destination-over.difference.disc.discard.disclosure-closed.disclosure-open.document.dot-dash.dot-dot-dash.dotted.double.down.e-resize.ease.ease-in.ease-in-out.ease-out.element.ellipse.ellipsis.embed.end.ethiopic-abegede-gez.ethiopic-halehame-aa-er.ethiopic-halehame-gez.ew-resize.exclusion.expanded.extends.extra-condensed.extra-expanded.fantasy.fast.fill.fill-box.fixed.flat.flex.flex-end.flex-start.footnotes.forwards.from.geometricPrecision.graytext.grid.groove.hand.hard-light.help.hidden.hide.higher.highlight.highlighttext.horizontal.hsl.hsla.hue.icon.ignore.inactiveborder.inactivecaption.inactivecaptiontext.infinite.infobackground.infotext.inherit.initial.inline.inline-axis.inline-block.inline-flex.inline-grid.inline-table.inset.inside.intrinsic.invert.italic.justify.keep-all.landscape.large.larger.left.level.lighter.lighten.line-through.linear.linear-gradient.lines.list-item.listbox.listitem.local.logical.loud.lower.lower-hexadecimal.lower-latin.lower-norwegian.lowercase.ltr.luminosity.manipulation.match.matrix.matrix3d.medium.menu.menutext.message-box.middle.min-intrinsic.mix.monospace.move.multiple.multiple_mask_images.multiply.n-resize.narrower.ne-resize.nesw-resize.no-close-quote.no-drop.no-open-quote.no-repeat.none.normal.not-allowed.nowrap.ns-resize.numbers.numeric.nw-resize.nwse-resize.oblique.opacity.open-quote.optimizeLegibility.optimizeSpeed.outset.outside.outside-shape.overlay.overline.padding.padding-box.painted.page.paused.perspective.pinch-zoom.plus-darker.plus-lighter.pointer.polygon.portrait.pre.pre-line.pre-wrap.preserve-3d.progress.push-button.radial-gradient.radio.read-only.read-write.read-write-plaintext-only.rectangle.region.relative.repeat.repeating-linear-gradient.repeating-radial-gradient.repeat-x.repeat-y.reset.reverse.rgb.rgba.ridge.right.rotate.rotate3d.rotateX.rotateY.rotateZ.round.row.row-resize.row-reverse.rtl.run-in.running.s-resize.sans-serif.saturation.scale.scale3d.scaleX.scaleY.scaleZ.screen.scroll.scrollbar.scroll-position.se-resize.self-start.self-end.semi-condensed.semi-expanded.separate.serif.show.single.skew.skewX.skewY.skip-white-space.slide.slider-horizontal.slider-vertical.sliderthumb-horizontal.sliderthumb-vertical.slow.small.small-caps.small-caption.smaller.soft-light.solid.source-atop.source-in.source-out.source-over.space.space-around.space-between.space-evenly.spell-out.square.start.static.status-bar.stretch.stroke.stroke-box.sub.subpixel-antialiased.svg_masks.super.sw-resize.symbolic.symbols.system-ui.table.table-caption.table-cell.table-column.table-column-group.table-footer-group.table-header-group.table-row.table-row-group.text.text-bottom.text-top.textarea.textfield.thick.thin.threeddarkshadow.threedface.threedhighlight.threedlightshadow.threedshadow.to.top.transform.translate.translate3d.translateX.translateY.translateZ.transparent.ultra-condensed.ultra-expanded.underline.unidirectional-pan.unset.up.upper-latin.uppercase.url.var.vertical.vertical-text.view-box.visible.visibleFill.visiblePainted.visibleStroke.visual.w-resize.wait.wave.wider.window.windowframe.windowtext.words.wrap.wrap-reverse.x-large.x-small.xor.xx-large.xx-small`.split(`.`).map(e=>({type:`keyword`,label:e})).concat(`aliceblue.antiquewhite.aqua.aquamarine.azure.beige.bisque.black.blanchedalmond.blue.blueviolet.brown.burlywood.cadetblue.chartreuse.chocolate.coral.cornflowerblue.cornsilk.crimson.cyan.darkblue.darkcyan.darkgoldenrod.darkgray.darkgreen.darkkhaki.darkmagenta.darkolivegreen.darkorange.darkorchid.darkred.darksalmon.darkseagreen.darkslateblue.darkslategray.darkturquoise.darkviolet.deeppink.deepskyblue.dimgray.dodgerblue.firebrick.floralwhite.forestgreen.fuchsia.gainsboro.ghostwhite.gold.goldenrod.gray.grey.green.greenyellow.honeydew.hotpink.indianred.indigo.ivory.khaki.lavender.lavenderblush.lawngreen.lemonchiffon.lightblue.lightcoral.lightcyan.lightgoldenrodyellow.lightgray.lightgreen.lightpink.lightsalmon.lightseagreen.lightskyblue.lightslategray.lightsteelblue.lightyellow.lime.limegreen.linen.magenta.maroon.mediumaquamarine.mediumblue.mediumorchid.mediumpurple.mediumseagreen.mediumslateblue.mediumspringgreen.mediumturquoise.mediumvioletred.midnightblue.mintcream.mistyrose.moccasin.navajowhite.navy.oldlace.olive.olivedrab.orange.orangered.orchid.palegoldenrod.palegreen.paleturquoise.palevioletred.papayawhip.peachpuff.peru.pink.plum.powderblue.purple.rebeccapurple.red.rosybrown.royalblue.saddlebrown.salmon.sandybrown.seagreen.seashell.sienna.silver.skyblue.slateblue.slategray.snow.springgreen.steelblue.tan.teal.thistle.tomato.turquoise.violet.wheat.white.whitesmoke.yellow.yellowgreen`.split(`.`).map(e=>({type:`constant`,label:e}))),le=`a.abbr.address.article.aside.b.bdi.bdo.blockquote.body.br.button.canvas.caption.cite.code.col.colgroup.dd.del.details.dfn.dialog.div.dl.dt.em.figcaption.figure.footer.form.header.hgroup.h1.h2.h3.h4.h5.h6.hr.html.i.iframe.img.input.ins.kbd.label.legend.li.main.meter.nav.ol.output.p.pre.ruby.section.select.small.source.span.strong.sub.summary.sup.table.tbody.td.template.textarea.tfoot.th.thead.tr.u.ul`.split(`.`).map(e=>({type:`type`,label:e})),ue=[`@charset`,`@color-profile`,`@container`,`@counter-style`,`@font-face`,`@font-feature-values`,`@font-palette-values`,`@import`,`@keyframes`,`@layer`,`@media`,`@namespace`,`@page`,`@position-try`,`@property`,`@scope`,`@starting-style`,`@supports`,`@view-transition`].map(e=>({type:`keyword`,label:e})),q=/^(\w[\w-]*|-\w[\w-]*|)$/,de=/^-(-[\w-]*)?$/;function fe(e,t){if((e.name==`(`||e.type.isError)&&(e=e.parent||e),e.name!=`ArgList`)return!1;let n=e.parent?.firstChild;return n?.name==`Callee`?t.sliceString(n.from,n.to)==`var`:!1}var J=new n,pe=[`Declaration`];function me(e){for(let t=e;;){if(t.type.isTop)return t;if(!(t=t.parent))return e}}function Y(e,n,r){if(n.to-n.from>4096){let i=J.get(n);if(i)return i;let a=[],o=new Set,s=n.cursor(t.IncludeAnonymous);if(s.firstChild())do for(let t of Y(e,s.node,r))o.has(t.label)||(o.add(t.label),a.push(t));while(s.nextSibling());return J.set(n,a),a}else{let t=[],i=new Set;return n.cursor().iterate(n=>{if(r(n)&&n.matchContext(pe)&&n.node.nextSibling?.name==`:`){let r=e.sliceString(n.from,n.to);i.has(r)||(i.add(r),t.push({label:r,type:`variable`}))}}),t}}var X=e=>t=>{let{state:n,pos:r}=t,i=l(n).resolveInner(r,-1),a=i.type.isError&&i.from==i.to-1&&n.doc.sliceString(i.from,i.to)==`-`;if(i.name==`PropertyName`||(a||i.name==`TagName`)&&/^(Block|Styles)$/.test(i.resolve(i.to).name))return{from:i.from,options:W(),validFor:q};if(i.name==`ValueName`)return{from:i.from,options:K,validFor:q};if(i.name==`PseudoClassName`)return{from:i.from,options:G,validFor:q};if(e(i)||(t.explicit||a)&&fe(i,n.doc))return{from:e(i)||a?i.from:r,options:Y(n.doc,me(i),e),validFor:de};if(i.name==`TagName`){for(let{parent:e}=i;e;e=e.parent)if(e.name==`Block`)return{from:i.from,options:W(),validFor:q};return{from:i.from,options:le,validFor:q}}if(i.name==`AtKeyword`)return{from:i.from,options:ue,validFor:q};if(!t.explicit)return null;let o=i.resolve(r),s=o.childBefore(r);return s&&s.name==`:`&&o.name==`PseudoClassSelector`?{from:r,options:G,validFor:q}:s&&s.name==`:`&&o.name==`Declaration`||o.name==`ArgList`?{from:r,options:K,validFor:q}:o.name==`Block`||o.name==`Styles`?{from:r,options:W(),validFor:q}:null},Z=X(e=>e.name==`VariableName`),Q=ee.define({name:`css`,parser:V.configure({props:[s.add({Declaration:c()}),a.add({"Block KeyframeList":i})]}),languageData:{commentTokens:{block:{open:`/*`,close:`*/`}},indentOnInput:/^\s*\}$/,wordChars:`-`}});function $(){return new o(Q,Q.data.of({autocomplete:Z}))}export{Q as n,H as r,$ as t}; \ No newline at end of file +import{$ as e,C as t,D as n,b as r,d as i,f as a,i as o,m as s,s as c,t as ee,v as l,x as u}from"./index-DPVcGf1z.js";import{i as d,n as f,r as p}from"./dist-T_roqrwM.js";var m=145,h=1,te=146,ne=147,g=2,_=148,v=3,re=4,y=[9,10,11,12,13,32,133,160,5760,8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,8232,8233,8239,8287,12288],b=58,x=40,S=95,ie=91,C=45,ae=46,oe=35,se=37,w=38,T=92,E=10,D=42;function O(e){return e>=65&&e<=90||e>=97&&e<=122||e>=161}function k(e){return e>=48&&e<=57}function A(e){return k(e)||e>=97&&e<=102||e>=65&&e<=70}var j=(e,t,n)=>(r,i)=>{for(let a=!1,o=0,s=0;;s++){let{next:c}=r;if(O(c)||c==C||c==S||a&&k(c))!a&&(c!=C||s>0)&&(a=!0),o===s&&c==C&&o++,r.advance();else if(c==T&&r.peek(1)!=E){if(r.advance(),A(r.next)){do r.advance();while(A(r.next));r.next==32&&r.advance()}else r.next>-1&&r.advance();a=!0}else{a&&r.acceptToken(o==2&&i.canShift(g)?t:c==x?n:e);break}}},M=new f(j(te,g,ne),{contextual:!0}),N=new f(j(_,v,re),{contextual:!0}),P=new f(e=>{if(y.includes(e.peek(-1))){let{next:t}=e;(O(t)||t==S||t==oe||t==ae||t==D||t==ie||t==b&&O(e.peek(1))||t==C||t==w)&&e.acceptToken(m)}}),F=new f(e=>{if(!y.includes(e.peek(-1))){let{next:t}=e;if(t==se&&(e.advance(),e.acceptToken(h)),O(t)){do e.advance();while(O(e.next)||k(e.next));e.acceptToken(h)}}}),I=r({"AtKeyword import charset namespace keyframes media supports font-feature-values":u.definitionKeyword,"from to selector scope MatchFlag":u.keyword,NamespaceName:u.namespace,KeyframeName:u.labelName,KeyframeRangeName:u.operatorKeyword,TagName:u.tagName,ClassName:u.className,PseudoClassName:u.constant(u.className),IdName:u.labelName,"FeatureName PropertyName":u.propertyName,AttributeName:u.attributeName,NumberLiteral:u.number,KeywordQuery:u.keyword,UnaryQueryOp:u.operatorKeyword,"CallTag ValueName FontName":u.atom,VariableName:u.variableName,Callee:u.operatorKeyword,Unit:u.unit,"UniversalSelector NestingSelector":u.definitionOperator,"MatchOp CompareOp":u.compareOperator,"ChildOp SiblingOp, LogicOp":u.logicOperator,BinOp:u.arithmeticOperator,Important:u.modifier,Comment:u.blockComment,ColorLiteral:u.color,"ParenthesizedContent StringLiteral":u.string,":":u.punctuation,"PseudoOp #":u.derefOperator,"; , |":u.separator,"( )":u.paren,"[ ]":u.squareBracket,"{ }":u.brace}),L={__proto__:null,lang:44,"nth-child":44,"nth-last-child":44,"nth-of-type":44,"nth-last-of-type":44,dir:44,"host-context":44,if:90,url:152,"url-prefix":152,domain:152,regexp:152},R={__proto__:null,or:104,and:104,not:112,only:112,layer:206},z={__proto__:null,selector:118,style:124,layer:202},ce={__proto__:null,"@import":198,"@media":210,"@charset":214,"@namespace":218,"@keyframes":224,"@supports":236,"@scope":240,"@font-feature-values":246},B={__proto__:null,to:243},V=p.deserialize({version:14,states:"MlQYQdOOO#}QdOOP$UO`OOO%OQaO'#CfOOQP'#Ce'#CeO%VQdO'#CgO%[Q`O'#CgO%aQaO'#FnO&XQdO'#CkO&xQaO'#CcO'SQdO'#CnO'_QdO'#EOO'dQdO'#EQO'oQdO'#EXO'oQdO'#E[OOQP'#Fn'#FnO)RQhO'#E}OOQS'#Fm'#FmOOQS'#FQ'#FQQYQdOOO)YQdO'#EbO*iQhO'#EhO)YQdO'#EjO*pQdO'#ElO*{QdO'#EoO)}QhO'#EuO+TQdO'#EwO+`QdO'#EzO+eQaO'#CfO+lQ`O'#E_O+qQ`O'#F{O+|QdO'#F{QOQ`OOP,WO&jO'#CaPOOO)CA])CA]OOQP'#Ci'#CiOOQP,59R,59RO%VQdO,59ROOQP'#Cm'#CmOOQP,59V,59VO&XQdO,59VO,cQdO,59YO'_QdO,5:jO'dQdO,5:lO'oQdO,5:sO'oQdO,5:uO'oQdO,5:vO'oQdO'#FXO,nQ`O,58}O,vQdO'#E^OOQS,58},58}OOQP'#Cq'#CqOOQO'#D|'#D|OOQP,59Y,59YO,}Q`O,59YO-SQ`O,59YOOQP'#EP'#EPOOQP,5:j,5:jO-XQpO'#ERO-dQdO'#ESO-iQ`O'#ESO-nQpO,5:lO.XQaO,5:sO.oQaO,5:vOOQW'#D^'#D^O/nQhO'#DgO0RQhO,5;iO)}QhO'#DeO0`Q`O'#DnO0eQhO'#DxOOQW'#Ft'#FtOOQS,5;i,5;iO0jQ`O'#DhO0oQ`O'#DkOOQS-E9O-E9OOOQ['#Cv'#CvO0tQdO'#CwO1[QdO'#C}O1rQdO'#DQO2YQ!pO'#DSO4fQ!jO,5:|OOQO'#DX'#DXO-SQ`O'#DWO4vQ!nO'#FqO6|Q`O'#DYO7RQ`O'#DyOOQ['#Fq'#FqO7WQhO'#GOO7fQ`O,5;SO7kQ!bO,5;UOOQS'#En'#EnO7sQ`O,5;WO7xQdO,5;WOOQO'#Eq'#EqO8QQ`O,5;ZO8VQhO,5;aO'oQdO'#DjOOQS,5;c,5;cO0jQ`O,5;cO8_QdO,5;cOOQS'#F`'#F`O8gQdO'#E|O7fQ`O,5;fO8oQdO,5:yO9PQdO'#FZO9^Q`O,5lQhO'#DoOOQW,5:Y,5:YOOQW,5:d,5:dOOQW,5:S,5:SO>vQhO,5:VO?bQ!fO'#FrOOQS'#Fr'#FrOOQS'#FS'#FSO@rQdO,59cOOQ[,59c,59cOAYQdO,59iOOQ[,59i,59iOApQdO,59lOOQ[,59l,59lOOQ[,59n,59nO)YQdO,59pOBWQhO'#EdOOQW'#Ed'#EdOBuQ`O1G0hO4oQhO1G0hOOQ[,59r,59rO)}QhO'#D[OOQ[,59t,59tOBzQ#tO,5:eOCVQhO'#F]OCdQ`O,5vQhO'#DmOI_QhO'#DqOIgQhO'#DsOIlQhO'#FwOOQO'#Fw'#FwOItQ!bO'#DwOOQO'#Fy'#FyOOQO'#Fv'#FvOIyQ`O1G/qOOQS-E9Q-E9QOOQ[1G.}1G.}OOQ[1G/T1G/TOOQ[1G/W1G/WOOQ[1G/[1G/[OJOQdO,5;OOOQS7+&S7+&SOJTQ`O7+&SOJYQhO'#D]OJbQ`O,59vO)}QhO,59vOOQ[1G0P1G0POJjQ`O1G0POJoQhO,5;wOOQO-E9Z-E9ZOOQS7+&^7+&^OJ}QbO'#DSOOQO'#Et'#EtOK]Q`O'#EsOOQO'#Es'#EsOKhQ`O'#F^OKpQdO,5;^OOQS,5;^,5;^OOQ[1G/p1G/pOOQS7+&i7+&iO7fQ`O7+&iOK{Q!fO'#FYO)YQdO'#FYOMSQdO7+&POOQO7+&P7+&POOQO,5:{,5:{OOQO1G1a1G1aOMgQ!bO<vQhO'#DrOOQO,5:],5:]O! hQhO,5:_OGUQhO,5:cOOQW7+%]7+%]OOQO'#Ef'#EfO! pQ`O1G0jOOQS<xAN>xO!#zQ`OAN>xO!$PQaO,5;rOOQO-E9U-E9UO!$ZQdO,5;qOOQO-E9T-E9TOOQW<vQhO'#DuOOQO1G/y1G/yO!%vQ!bO1G/}OJOQdO'#F[O!&OQ`O7+&UOOQW7+&U7+&UO!&WQ!bO1G/cOOQ[7+$|7+$|O!&cQhO7+$|P!&jQ`O'#FTOOQO,5;y,5;yOOQO-E9]-E9]OOQS1G1d1G1dOOQPG24dG24dO!&oQ`OAN>ZO)YQdO1G1[O!&tQ`O7+'jOOQO1G/x1G/xO!&|Q`O,5:aO!$eQhO7+%iOOQO,5;v,5;vOOQO-E9Y-E9YOOQW<Q!]!^>|!^!_?_!_!`@Z!`!a@n!a!b%Z!b!cAo!c!k%Z!k!lC|!l!u%Z!u!vC|!v!}%Z!}#OD_#O#P%Z#P#QDp#Q#R2X#R#]%Z#]#^ER#^#g%Z#g#hC|#h#o%Z#o#pIf#p#qIw#q#rJ`#r#sJq#s#y%Z#y#z&R#z$f%Z$f$g&R$g#BY%Z#BY#BZ&R#BZ$IS%Z$IS$I_&R$I_$I|%Z$I|$JO&R$JO$JT%Z$JT$JU&R$JU$KV%Z$KV$KW&R$KW&FU%Z&FU&FV&R&FV;'S%Z;'S;=`KY<%lO%Z`%^SOy%jz;'S%j;'S;=`%{<%lO%j`%oS!o`Oy%jz;'S%j;'S;=`%{<%lO%j`&OP;=`<%l%j~&Wh$[~OX%jX^'r^p%jpq'rqy%jz#y%j#y#z'r#z$f%j$f$g'r$g#BY%j#BY#BZ'r#BZ$IS%j$IS$I_'r$I_$I|%j$I|$JO'r$JO$JT%j$JT$JU'r$JU$KV%j$KV$KW'r$KW&FU%j&FU&FV'r&FV;'S%j;'S;=`%{<%lO%j~'yh$[~!o`OX%jX^'r^p%jpq'rqy%jz#y%j#y#z'r#z$f%j$f$g'r$g#BY%j#BY#BZ'r#BZ$IS%j$IS$I_'r$I_$I|%j$I|$JO'r$JO$JT%j$JT$JU'r$JU$KV%j$KV$KW'r$KW&FU%j&FU&FV'r&FV;'S%j;'S;=`%{<%lO%jj)jS$qYOy%jz;'S%j;'S;=`%{<%lO%j~)yWOY)vZr)vrs*cs#O)v#O#P*h#P;'S)v;'S;=`+d<%lO)v~*hOw~~*kRO;'S)v;'S;=`*t;=`O)v~*wXOY)vZr)vrs*cs#O)v#O#P*h#P;'S)v;'S;=`+d;=`<%l)v<%lO)v~+gP;=`<%l)vj+oYmYOy%jz!Q%j!Q![,_![!c%j!c!i,_!i#T%j#T#Z,_#Z;'S%j;'S;=`%{<%lO%jj,dY!o`Oy%jz!Q%j!Q![-S![!c%j!c!i-S!i#T%j#T#Z-S#Z;'S%j;'S;=`%{<%lO%jj-XY!o`Oy%jz!Q%j!Q![-w![!c%j!c!i-w!i#T%j#T#Z-w#Z;'S%j;'S;=`%{<%lO%jj.OYuY!o`Oy%jz!Q%j!Q![.n![!c%j!c!i.n!i#T%j#T#Z.n#Z;'S%j;'S;=`%{<%lO%jj.uYuY!o`Oy%jz!Q%j!Q![/e![!c%j!c!i/e!i#T%j#T#Z/e#Z;'S%j;'S;=`%{<%lO%jj/jY!o`Oy%jz!Q%j!Q![0Y![!c%j!c!i0Y!i#T%j#T#Z0Y#Z;'S%j;'S;=`%{<%lO%jj0aYuY!o`Oy%jz!Q%j!Q![1P![!c%j!c!i1P!i#T%j#T#Z1P#Z;'S%j;'S;=`%{<%lO%jj1UY!o`Oy%jz!Q%j!Q![1t![!c%j!c!i1t!i#T%j#T#Z1t#Z;'S%j;'S;=`%{<%lO%jj1{SuY!o`Oy%jz;'S%j;'S;=`%{<%lO%jd2[UOy%jz!_%j!_!`2n!`;'S%j;'S;=`%{<%lO%jd2uS!yS!o`Oy%jz;'S%j;'S;=`%{<%lO%jb3WS^QOy%jz;'S%j;'S;=`%{<%lO%j~3gWOY3dZw3dwx*cx#O3d#O#P4P#P;'S3d;'S;=`4{<%lO3d~4SRO;'S3d;'S;=`4];=`O3d~4`XOY3dZw3dwx*cx#O3d#O#P4P#P;'S3d;'S;=`4{;=`<%l3d<%lO3d~5OP;=`<%l3dj5WShYOy%jz;'S%j;'S;=`%{<%lO%j~5iOg~n5pUWQyWOy%jz!_%j!_!`2n!`;'S%j;'S;=`%{<%lO%jj6ZWyW#PQOy%jz!O%j!O!P6s!P!Q%j!Q![9x![;'S%j;'S;=`%{<%lO%jj6xU!o`Oy%jz!Q%j!Q![7[![;'S%j;'S;=`%{<%lO%jj7cY!o`$gYOy%jz!Q%j!Q![7[![!g%j!g!h8R!h#X%j#X#Y8R#Y;'S%j;'S;=`%{<%lO%jj8WY!o`Oy%jz{%j{|8v|}%j}!O8v!O!Q%j!Q![9_![;'S%j;'S;=`%{<%lO%jj8{U!o`Oy%jz!Q%j!Q![9_![;'S%j;'S;=`%{<%lO%jj9fU!o`$gYOy%jz!Q%j!Q![9_![;'S%j;'S;=`%{<%lO%jj:P[!o`$gYOy%jz!O%j!O!P7[!P!Q%j!Q![9x![!g%j!g!h8R!h#X%j#X#Y8R#Y;'S%j;'S;=`%{<%lO%jj:zS!dYOy%jz;'S%j;'S;=`%{<%lO%jj;]WyWOy%jz!O%j!O!P6s!P!Q%j!Q![9x![;'S%j;'S;=`%{<%lO%jj;zU`YOy%jz!Q%j!Q![7[![;'S%j;'S;=`%{<%lO%j~VUcYOy%jz![%j![!]>i!];'S%j;'S;=`%{<%lO%jj>pSdY!o`Oy%jz;'S%j;'S;=`%{<%lO%jj?RSnYOy%jz;'S%j;'S;=`%{<%lO%jh?dU!WWOy%jz!_%j!_!`?v!`;'S%j;'S;=`%{<%lO%jh?}S!WW!o`Oy%jz;'S%j;'S;=`%{<%lO%jl@bS!WW!ySOy%jz;'S%j;'S;=`%{<%lO%jj@uV!|Q!WWOy%jz!_%j!_!`?v!`!aA[!a;'S%j;'S;=`%{<%lO%jbAcS!|Q!o`Oy%jz;'S%j;'S;=`%{<%lO%jjArYOy%jz}%j}!OBb!O!c%j!c!}CP!}#T%j#T#oCP#o;'S%j;'S;=`%{<%lO%jjBgW!o`Oy%jz!c%j!c!}CP!}#T%j#T#oCP#o;'S%j;'S;=`%{<%lO%jjCW[lY!o`Oy%jz}%j}!OCP!O!Q%j!Q![CP![!c%j!c!}CP!}#T%j#T#oCP#o;'S%j;'S;=`%{<%lO%jhDRS!zWOy%jz;'S%j;'S;=`%{<%lO%jjDdSpYOy%jz;'S%j;'S;=`%{<%lO%jnDuSo^Oy%jz;'S%j;'S;=`%{<%lO%jjEWU!zWOy%jz#a%j#a#bEj#b;'S%j;'S;=`%{<%lO%jbEoU!o`Oy%jz#d%j#d#eFR#e;'S%j;'S;=`%{<%lO%jbFWU!o`Oy%jz#c%j#c#dFj#d;'S%j;'S;=`%{<%lO%jbFoU!o`Oy%jz#f%j#f#gGR#g;'S%j;'S;=`%{<%lO%jbGWU!o`Oy%jz#h%j#h#iGj#i;'S%j;'S;=`%{<%lO%jbGoU!o`Oy%jz#T%j#T#UHR#U;'S%j;'S;=`%{<%lO%jbHWU!o`Oy%jz#b%j#b#cHj#c;'S%j;'S;=`%{<%lO%jbHoU!o`Oy%jz#h%j#h#iIR#i;'S%j;'S;=`%{<%lO%jbIYS$pQ!o`Oy%jz;'S%j;'S;=`%{<%lO%jjIkSsYOy%jz;'S%j;'S;=`%{<%lO%jfI|U$cUOy%jz!_%j!_!`2n!`;'S%j;'S;=`%{<%lO%jjJeSrYOy%jz;'S%j;'S;=`%{<%lO%jfJvU#PQOy%jz!_%j!_!`2n!`;'S%j;'S;=`%{<%lO%j`K]P;=`<%l%Z",tokenizers:[P,F,M,N,1,2,3,4,new d("m~RRYZ[z{a~~g~aO$_~~dP!P!Qg~lO$`~~",28,152)],topRules:{StyleSheet:[0,6],Styles:[1,126]},dynamicPrecedences:{94:1},specialized:[{term:147,get:e=>L[e]||-1},{term:148,get:e=>R[e]||-1},{term:4,get:e=>z[e]||-1},{term:28,get:e=>ce[e]||-1},{term:146,get:e=>B[e]||-1}],tokenPrec:2405}),H=e({css:()=>$,cssCompletionSource:()=>Z,cssLanguage:()=>Q,defineCSSCompletionSource:()=>X}),U=null;function W(){if(!U&&typeof document==`object`&&document.body){let{style:e}=document.body,t=[],n=new Set;for(let r in e)r!=`cssText`&&r!=`cssFloat`&&typeof e[r]==`string`&&(/[A-Z]/.test(r)&&(r=r.replace(/[A-Z]/g,e=>`-`+e.toLowerCase())),n.has(r)||(t.push(r),n.add(r)));U=t.sort().map(e=>({type:`property`,label:e,apply:e+`: `}))}return U||[]}var G=`active.after.any-link.autofill.backdrop.before.checked.cue.default.defined.disabled.empty.enabled.file-selector-button.first.first-child.first-letter.first-line.first-of-type.focus.focus-visible.focus-within.fullscreen.has.host.host-context.hover.in-range.indeterminate.invalid.is.lang.last-child.last-of-type.left.link.marker.modal.not.nth-child.nth-last-child.nth-last-of-type.nth-of-type.only-child.only-of-type.optional.out-of-range.part.placeholder.placeholder-shown.read-only.read-write.required.right.root.scope.selection.slotted.target.target-text.valid.visited.where`.split(`.`).map(e=>({type:`class`,label:e})),K=`above.absolute.activeborder.additive.activecaption.after-white-space.ahead.alias.all.all-scroll.alphabetic.alternate.always.antialiased.appworkspace.asterisks.attr.auto.auto-flow.avoid.avoid-column.avoid-page.avoid-region.axis-pan.background.backwards.baseline.below.bidi-override.blink.block.block-axis.bold.bolder.border.border-box.both.bottom.break.break-all.break-word.bullets.button.button-bevel.buttonface.buttonhighlight.buttonshadow.buttontext.calc.capitalize.caps-lock-indicator.caption.captiontext.caret.cell.center.checkbox.circle.cjk-decimal.clear.clip.close-quote.col-resize.collapse.color.color-burn.color-dodge.column.column-reverse.compact.condensed.contain.content.contents.content-box.context-menu.continuous.copy.counter.counters.cover.crop.cross.crosshair.currentcolor.cursive.cyclic.darken.dashed.decimal.decimal-leading-zero.default.default-button.dense.destination-atop.destination-in.destination-out.destination-over.difference.disc.discard.disclosure-closed.disclosure-open.document.dot-dash.dot-dot-dash.dotted.double.down.e-resize.ease.ease-in.ease-in-out.ease-out.element.ellipse.ellipsis.embed.end.ethiopic-abegede-gez.ethiopic-halehame-aa-er.ethiopic-halehame-gez.ew-resize.exclusion.expanded.extends.extra-condensed.extra-expanded.fantasy.fast.fill.fill-box.fixed.flat.flex.flex-end.flex-start.footnotes.forwards.from.geometricPrecision.graytext.grid.groove.hand.hard-light.help.hidden.hide.higher.highlight.highlighttext.horizontal.hsl.hsla.hue.icon.ignore.inactiveborder.inactivecaption.inactivecaptiontext.infinite.infobackground.infotext.inherit.initial.inline.inline-axis.inline-block.inline-flex.inline-grid.inline-table.inset.inside.intrinsic.invert.italic.justify.keep-all.landscape.large.larger.left.level.lighter.lighten.line-through.linear.linear-gradient.lines.list-item.listbox.listitem.local.logical.loud.lower.lower-hexadecimal.lower-latin.lower-norwegian.lowercase.ltr.luminosity.manipulation.match.matrix.matrix3d.medium.menu.menutext.message-box.middle.min-intrinsic.mix.monospace.move.multiple.multiple_mask_images.multiply.n-resize.narrower.ne-resize.nesw-resize.no-close-quote.no-drop.no-open-quote.no-repeat.none.normal.not-allowed.nowrap.ns-resize.numbers.numeric.nw-resize.nwse-resize.oblique.opacity.open-quote.optimizeLegibility.optimizeSpeed.outset.outside.outside-shape.overlay.overline.padding.padding-box.painted.page.paused.perspective.pinch-zoom.plus-darker.plus-lighter.pointer.polygon.portrait.pre.pre-line.pre-wrap.preserve-3d.progress.push-button.radial-gradient.radio.read-only.read-write.read-write-plaintext-only.rectangle.region.relative.repeat.repeating-linear-gradient.repeating-radial-gradient.repeat-x.repeat-y.reset.reverse.rgb.rgba.ridge.right.rotate.rotate3d.rotateX.rotateY.rotateZ.round.row.row-resize.row-reverse.rtl.run-in.running.s-resize.sans-serif.saturation.scale.scale3d.scaleX.scaleY.scaleZ.screen.scroll.scrollbar.scroll-position.se-resize.self-start.self-end.semi-condensed.semi-expanded.separate.serif.show.single.skew.skewX.skewY.skip-white-space.slide.slider-horizontal.slider-vertical.sliderthumb-horizontal.sliderthumb-vertical.slow.small.small-caps.small-caption.smaller.soft-light.solid.source-atop.source-in.source-out.source-over.space.space-around.space-between.space-evenly.spell-out.square.start.static.status-bar.stretch.stroke.stroke-box.sub.subpixel-antialiased.svg_masks.super.sw-resize.symbolic.symbols.system-ui.table.table-caption.table-cell.table-column.table-column-group.table-footer-group.table-header-group.table-row.table-row-group.text.text-bottom.text-top.textarea.textfield.thick.thin.threeddarkshadow.threedface.threedhighlight.threedlightshadow.threedshadow.to.top.transform.translate.translate3d.translateX.translateY.translateZ.transparent.ultra-condensed.ultra-expanded.underline.unidirectional-pan.unset.up.upper-latin.uppercase.url.var.vertical.vertical-text.view-box.visible.visibleFill.visiblePainted.visibleStroke.visual.w-resize.wait.wave.wider.window.windowframe.windowtext.words.wrap.wrap-reverse.x-large.x-small.xor.xx-large.xx-small`.split(`.`).map(e=>({type:`keyword`,label:e})).concat(`aliceblue.antiquewhite.aqua.aquamarine.azure.beige.bisque.black.blanchedalmond.blue.blueviolet.brown.burlywood.cadetblue.chartreuse.chocolate.coral.cornflowerblue.cornsilk.crimson.cyan.darkblue.darkcyan.darkgoldenrod.darkgray.darkgreen.darkkhaki.darkmagenta.darkolivegreen.darkorange.darkorchid.darkred.darksalmon.darkseagreen.darkslateblue.darkslategray.darkturquoise.darkviolet.deeppink.deepskyblue.dimgray.dodgerblue.firebrick.floralwhite.forestgreen.fuchsia.gainsboro.ghostwhite.gold.goldenrod.gray.grey.green.greenyellow.honeydew.hotpink.indianred.indigo.ivory.khaki.lavender.lavenderblush.lawngreen.lemonchiffon.lightblue.lightcoral.lightcyan.lightgoldenrodyellow.lightgray.lightgreen.lightpink.lightsalmon.lightseagreen.lightskyblue.lightslategray.lightsteelblue.lightyellow.lime.limegreen.linen.magenta.maroon.mediumaquamarine.mediumblue.mediumorchid.mediumpurple.mediumseagreen.mediumslateblue.mediumspringgreen.mediumturquoise.mediumvioletred.midnightblue.mintcream.mistyrose.moccasin.navajowhite.navy.oldlace.olive.olivedrab.orange.orangered.orchid.palegoldenrod.palegreen.paleturquoise.palevioletred.papayawhip.peachpuff.peru.pink.plum.powderblue.purple.rebeccapurple.red.rosybrown.royalblue.saddlebrown.salmon.sandybrown.seagreen.seashell.sienna.silver.skyblue.slateblue.slategray.snow.springgreen.steelblue.tan.teal.thistle.tomato.turquoise.violet.wheat.white.whitesmoke.yellow.yellowgreen`.split(`.`).map(e=>({type:`constant`,label:e}))),le=`a.abbr.address.article.aside.b.bdi.bdo.blockquote.body.br.button.canvas.caption.cite.code.col.colgroup.dd.del.details.dfn.dialog.div.dl.dt.em.figcaption.figure.footer.form.header.hgroup.h1.h2.h3.h4.h5.h6.hr.html.i.iframe.img.input.ins.kbd.label.legend.li.main.meter.nav.ol.output.p.pre.ruby.section.select.small.source.span.strong.sub.summary.sup.table.tbody.td.template.textarea.tfoot.th.thead.tr.u.ul`.split(`.`).map(e=>({type:`type`,label:e})),ue=[`@charset`,`@color-profile`,`@container`,`@counter-style`,`@font-face`,`@font-feature-values`,`@font-palette-values`,`@import`,`@keyframes`,`@layer`,`@media`,`@namespace`,`@page`,`@position-try`,`@property`,`@scope`,`@starting-style`,`@supports`,`@view-transition`].map(e=>({type:`keyword`,label:e})),q=/^(\w[\w-]*|-\w[\w-]*|)$/,de=/^-(-[\w-]*)?$/;function fe(e,t){if((e.name==`(`||e.type.isError)&&(e=e.parent||e),e.name!=`ArgList`)return!1;let n=e.parent?.firstChild;return n?.name==`Callee`?t.sliceString(n.from,n.to)==`var`:!1}var J=new n,pe=[`Declaration`];function me(e){for(let t=e;;){if(t.type.isTop)return t;if(!(t=t.parent))return e}}function Y(e,n,r){if(n.to-n.from>4096){let i=J.get(n);if(i)return i;let a=[],o=new Set,s=n.cursor(t.IncludeAnonymous);if(s.firstChild())do for(let t of Y(e,s.node,r))o.has(t.label)||(o.add(t.label),a.push(t));while(s.nextSibling());return J.set(n,a),a}else{let t=[],i=new Set;return n.cursor().iterate(n=>{if(r(n)&&n.matchContext(pe)&&n.node.nextSibling?.name==`:`){let r=e.sliceString(n.from,n.to);i.has(r)||(i.add(r),t.push({label:r,type:`variable`}))}}),t}}var X=e=>t=>{let{state:n,pos:r}=t,i=l(n).resolveInner(r,-1),a=i.type.isError&&i.from==i.to-1&&n.doc.sliceString(i.from,i.to)==`-`;if(i.name==`PropertyName`||(a||i.name==`TagName`)&&/^(Block|Styles)$/.test(i.resolve(i.to).name))return{from:i.from,options:W(),validFor:q};if(i.name==`ValueName`)return{from:i.from,options:K,validFor:q};if(i.name==`PseudoClassName`)return{from:i.from,options:G,validFor:q};if(e(i)||(t.explicit||a)&&fe(i,n.doc))return{from:e(i)||a?i.from:r,options:Y(n.doc,me(i),e),validFor:de};if(i.name==`TagName`){for(let{parent:e}=i;e;e=e.parent)if(e.name==`Block`)return{from:i.from,options:W(),validFor:q};return{from:i.from,options:le,validFor:q}}if(i.name==`AtKeyword`)return{from:i.from,options:ue,validFor:q};if(!t.explicit)return null;let o=i.resolve(r),s=o.childBefore(r);return s&&s.name==`:`&&o.name==`PseudoClassSelector`?{from:r,options:G,validFor:q}:s&&s.name==`:`&&o.name==`Declaration`||o.name==`ArgList`?{from:r,options:K,validFor:q}:o.name==`Block`||o.name==`Styles`?{from:r,options:W(),validFor:q}:null},Z=X(e=>e.name==`VariableName`),Q=ee.define({name:`css`,parser:V.configure({props:[s.add({Declaration:c()}),a.add({"Block KeyframeList":i})]}),languageData:{commentTokens:{block:{open:`/*`,close:`*/`}},indentOnInput:/^\s*\}$/,wordChars:`-`}});function $(){return new o(Q,Q.data.of({autocomplete:Z}))}export{Q as n,H as r,$ as t}; \ No newline at end of file diff --git a/mcp/src/agents_remember/package_data/dashboard/assets/dist-DvbxHIw3.js b/mcp/src/agents_remember/package_data/dashboard/assets/dist-DGS86N5w.js similarity index 99% rename from mcp/src/agents_remember/package_data/dashboard/assets/dist-DvbxHIw3.js rename to mcp/src/agents_remember/package_data/dashboard/assets/dist-DGS86N5w.js index e73bbc70..1f371a73 100644 --- a/mcp/src/agents_remember/package_data/dashboard/assets/dist-DvbxHIw3.js +++ b/mcp/src/agents_remember/package_data/dashboard/assets/dist-DGS86N5w.js @@ -1,4 +1,4 @@ -import{A as e,B as t,E as n,I as r,K as i,L as a,M as o,O as s,P as c,T as l,a as u,b as d,c as f,f as p,g as m,h,i as g,k as _,m as ee,n as te,p as ne,r as re,v,w as y,x as b,y as ie}from"./index-CLJ0aycO.js";import{t as ae}from"./dist-BxWz473c.js";import{html as oe,htmlCompletionSource as se}from"./dist-jYLMKGSZ.js";var ce=class e{static create(t,n,r,i,a){return new e(t,n,r,i+(i<<8)+t+(n<<4)|0,a,[],[])}constructor(e,t,n,r,i,a,o){this.type=e,this.value=t,this.from=n,this.hash=r,this.end=i,this.children=a,this.positions=o,this.hashProp=[[y.contextHash,r]]}addChild(e,t){e.prop(y.contextHash)!=this.hash&&(e=new _(e.type,e.children,e.positions,e.length,this.hashProp)),this.children.push(e),this.positions.push(t)}toTree(e,t=this.end){let r=this.children.length-1;return r>=0&&(t=Math.max(t,this.positions[r]+this.children[r].length+this.from)),new _(e.types[this.type],this.children,this.positions,t-this.from).balance({makeTree:(e,t,r)=>new _(n.none,e,t,r,this.hashProp)})}},x;(function(e){e[e.Document=1]=`Document`,e[e.CodeBlock=2]=`CodeBlock`,e[e.FencedCode=3]=`FencedCode`,e[e.Blockquote=4]=`Blockquote`,e[e.HorizontalRule=5]=`HorizontalRule`,e[e.BulletList=6]=`BulletList`,e[e.OrderedList=7]=`OrderedList`,e[e.ListItem=8]=`ListItem`,e[e.ATXHeading1=9]=`ATXHeading1`,e[e.ATXHeading2=10]=`ATXHeading2`,e[e.ATXHeading3=11]=`ATXHeading3`,e[e.ATXHeading4=12]=`ATXHeading4`,e[e.ATXHeading5=13]=`ATXHeading5`,e[e.ATXHeading6=14]=`ATXHeading6`,e[e.SetextHeading1=15]=`SetextHeading1`,e[e.SetextHeading2=16]=`SetextHeading2`,e[e.HTMLBlock=17]=`HTMLBlock`,e[e.LinkReference=18]=`LinkReference`,e[e.Paragraph=19]=`Paragraph`,e[e.CommentBlock=20]=`CommentBlock`,e[e.ProcessingInstructionBlock=21]=`ProcessingInstructionBlock`,e[e.Escape=22]=`Escape`,e[e.Entity=23]=`Entity`,e[e.HardBreak=24]=`HardBreak`,e[e.Emphasis=25]=`Emphasis`,e[e.StrongEmphasis=26]=`StrongEmphasis`,e[e.Link=27]=`Link`,e[e.Image=28]=`Image`,e[e.InlineCode=29]=`InlineCode`,e[e.HTMLTag=30]=`HTMLTag`,e[e.Comment=31]=`Comment`,e[e.ProcessingInstruction=32]=`ProcessingInstruction`,e[e.Autolink=33]=`Autolink`,e[e.HeaderMark=34]=`HeaderMark`,e[e.QuoteMark=35]=`QuoteMark`,e[e.ListMark=36]=`ListMark`,e[e.LinkMark=37]=`LinkMark`,e[e.EmphasisMark=38]=`EmphasisMark`,e[e.CodeMark=39]=`CodeMark`,e[e.CodeText=40]=`CodeText`,e[e.CodeInfo=41]=`CodeInfo`,e[e.LinkTitle=42]=`LinkTitle`,e[e.LinkLabel=43]=`LinkLabel`,e[e.URL=44]=`URL`})(x||={});var le=class{constructor(e,t){this.start=e,this.content=t,this.marks=[],this.parsers=[]}},ue=class{constructor(){this.text=``,this.baseIndent=0,this.basePos=0,this.depth=0,this.markers=[],this.pos=0,this.indent=0,this.next=-1}forward(){this.basePos>this.pos&&this.forwardInner()}forwardInner(){let e=this.skipSpace(this.basePos);this.indent=this.countIndent(e,this.pos,this.indent),this.pos=e,this.next=e==this.text.length?-1:this.text.charCodeAt(e)}skipSpace(e){return C(this.text,e)}reset(e){for(this.text=e,this.baseIndent=this.basePos=this.pos=this.indent=0,this.forwardInner(),this.depth=1;this.markers.length;)this.markers.pop()}moveBase(e){this.basePos=e,this.baseIndent=this.countIndent(e,this.pos,this.indent)}moveBaseColumn(e){this.baseIndent=e,this.basePos=this.findColumn(e)}addMarker(e){this.markers.push(e)}countIndent(e,t=0,n=0){for(let r=t;r=t.stack[n.depth+1].value+n.baseIndent)return!0;if(n.indent>=n.baseIndent+4)return!1;let r=(e.type==x.OrderedList?E:T)(n,t,!1);return r>0&&(e.type!=x.BulletList||w(n,t,!1)<0)&&n.text.charCodeAt(n.pos+r-1)==e.value}var fe={[x.Blockquote](e,t,n){return n.next==62?(n.markers.push(L(x.QuoteMark,t.lineStart+n.pos,t.lineStart+n.pos+1)),n.moveBase(n.pos+(S(n.text.charCodeAt(n.pos+1))?2:1)),e.end=t.lineStart+n.text.length,!0):!1},[x.ListItem](e,t,n){return n.indent-1?!1:(n.moveBaseColumn(n.baseIndent+e.value),!0)},[x.OrderedList]:de,[x.BulletList]:de,[x.Document](){return!0}};function S(e){return e==32||e==9||e==10||e==13}function C(e,t=0){for(;tn&&S(e.charCodeAt(t-1));)t--;return t}function me(e){if(e.next!=96&&e.next!=126)return-1;let t=e.pos+1;for(;t-1&&e.depth==t.stack.length&&t.parser.leafBlockParsers.indexOf(Te.SetextHeading)>-1||r<3?-1:1}function ge(e,t){for(let n=e.stack.length-1;n>=0;n--)if(e.stack[n].type==t)return!0;return!1}function T(e,t,n){return(e.next==45||e.next==43||e.next==42)&&(e.pos==e.text.length-1||S(e.text.charCodeAt(e.pos+1)))&&(!n||ge(t,x.BulletList)||e.skipSpace(e.pos+2)=48&&i<=57;){if(r++,r==e.text.length)return-1;i=e.text.charCodeAt(r)}return r==e.pos||r>e.pos+9||i!=46&&i!=41||re.pos+1||e.next!=49)?-1:r+1-e.pos}function _e(e){if(e.next!=35)return-1;let t=e.pos+1;for(;t6?-1:n}function ve(e){if(e.next!=45&&e.next!=61||e.indent>=e.baseIndent+4)return-1;let t=e.pos+1;for(;t/,be=/\?>/,O=[[/^<(?:script|pre|style)(?:\s|>|$)/i,/<\/(?:script|pre|style)>/i],[/^\s*/,be=/\?>/,O=[[/^<(?:script|pre|style)(?:\s|>|$)/i,/<\/(?:script|pre|style)>/i],[/^\s*