@jstn-sdk/rcs 0.1.0 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +154 -101
- package/dist/agents/definitions.d.ts.map +1 -1
- package/dist/agents/definitions.js +0 -101
- package/dist/agents/definitions.js.map +1 -1
- package/dist/blueprint/runtime.d.ts +52 -0
- package/dist/blueprint/runtime.d.ts.map +1 -0
- package/dist/{ralplan → blueprint}/runtime.js +19 -19
- package/dist/blueprint/runtime.js.map +1 -0
- package/dist/catalog/reader.d.ts.map +1 -1
- package/dist/catalog/reader.js +8 -2
- package/dist/catalog/reader.js.map +1 -1
- package/dist/catalog/schema.js +1 -1
- package/dist/catalog/schema.js.map +1 -1
- package/dist/cli/forge.d.ts +17 -0
- package/dist/cli/{ralph.d.ts.map → forge.d.ts.map} +1 -1
- package/dist/cli/{ralph.js → forge.js} +82 -82
- package/dist/cli/{ralph.js.map → forge.js.map} +1 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +20 -15
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +2 -3
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/star-prompt.js +2 -2
- package/dist/cli/star-prompt.js.map +1 -1
- package/dist/cli/state.js +1 -1
- package/dist/cli/team.d.ts.map +1 -1
- package/dist/cli/team.js +3 -2
- package/dist/cli/team.js.map +1 -1
- package/dist/cli/tmux-hook.d.ts.map +1 -1
- package/dist/cli/tmux-hook.js +9 -1
- package/dist/cli/tmux-hook.js.map +1 -1
- package/dist/config/generator.d.ts +1 -1
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +1 -1
- package/dist/config/generator.js.map +1 -1
- package/dist/forge/contract.d.ts +17 -0
- package/dist/{ralph → forge}/contract.d.ts.map +1 -1
- package/dist/{ralph → forge}/contract.js +16 -16
- package/dist/{ralph → forge}/contract.js.map +1 -1
- package/dist/{ralph → forge}/persistence.d.ts +5 -5
- package/dist/{ralph → forge}/persistence.d.ts.map +1 -1
- package/dist/{ralph → forge}/persistence.js +7 -6
- package/dist/forge/persistence.js.map +1 -0
- package/dist/hooks/agents-overlay.d.ts +1 -1
- package/dist/hooks/agents-overlay.d.ts.map +1 -1
- package/dist/hooks/agents-overlay.js +37 -31
- package/dist/hooks/agents-overlay.js.map +1 -1
- package/dist/hooks/extensibility/dispatcher.d.ts.map +1 -1
- package/dist/hooks/extensibility/dispatcher.js +82 -14
- package/dist/hooks/extensibility/dispatcher.js.map +1 -1
- package/dist/hooks/keyword-detector.d.ts +8 -8
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +94 -64
- package/dist/hooks/keyword-detector.js.map +1 -1
- package/dist/hooks/keyword-registry.d.ts.map +1 -1
- package/dist/hooks/keyword-registry.js +9 -11
- package/dist/hooks/keyword-registry.js.map +1 -1
- package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
- package/dist/hooks/prompt-guidance-contract.js +10 -21
- package/dist/hooks/prompt-guidance-contract.js.map +1 -1
- package/dist/hooks/task-size-detector.js +2 -2
- package/dist/hooks/task-size-detector.js.map +1 -1
- package/dist/hooks/triage-state.d.ts +1 -1
- package/dist/hooks/triage-state.js +1 -1
- package/dist/hud/colors.d.ts +2 -2
- package/dist/hud/colors.js +2 -2
- package/dist/hud/render.js +21 -21
- package/dist/hud/render.js.map +1 -1
- package/dist/hud/state.d.ts +3 -3
- package/dist/hud/state.d.ts.map +1 -1
- package/dist/hud/state.js +18 -15
- package/dist/hud/state.js.map +1 -1
- package/dist/hud/types.d.ts +6 -6
- package/dist/hud/types.d.ts.map +1 -1
- package/dist/mcp/bootstrap.d.ts.map +1 -1
- package/dist/mcp/bootstrap.js +36 -2
- package/dist/mcp/bootstrap.js.map +1 -1
- package/dist/mcp/state-paths.d.ts +1 -0
- package/dist/mcp/state-paths.d.ts.map +1 -1
- package/dist/mcp/state-paths.js +4 -1
- package/dist/mcp/state-paths.js.map +1 -1
- package/dist/mcp/state-server.d.ts +4 -4
- package/dist/mcp/state-server.js +2 -2
- package/dist/mcp/state-server.js.map +1 -1
- package/dist/modes/base.d.ts +2 -2
- package/dist/modes/base.d.ts.map +1 -1
- package/dist/modes/base.js +29 -26
- package/dist/modes/base.js.map +1 -1
- package/dist/notifications/reply-listener.d.ts.map +1 -1
- package/dist/notifications/reply-listener.js +7 -1
- package/dist/notifications/reply-listener.js.map +1 -1
- package/dist/notifications/tmux.d.ts.map +1 -1
- package/dist/notifications/tmux.js +39 -6
- package/dist/notifications/tmux.js.map +1 -1
- package/dist/pipeline/index.d.ts +7 -6
- package/dist/pipeline/index.d.ts.map +1 -1
- package/dist/pipeline/index.js +5 -4
- package/dist/pipeline/index.js.map +1 -1
- package/dist/pipeline/orchestrator.d.ts +5 -5
- package/dist/pipeline/orchestrator.js +25 -25
- package/dist/pipeline/orchestrator.js.map +1 -1
- package/dist/pipeline/stages/blueprint.d.ts +25 -0
- package/dist/pipeline/stages/blueprint.d.ts.map +1 -0
- package/dist/pipeline/stages/{ralplan.js → blueprint.js} +16 -16
- package/dist/pipeline/stages/blueprint.js.map +1 -0
- package/dist/pipeline/stages/code-review.d.ts +2 -2
- package/dist/pipeline/stages/code-review.js +6 -6
- package/dist/pipeline/stages/code-review.js.map +1 -1
- package/dist/pipeline/stages/forge-verify.d.ts +50 -0
- package/dist/pipeline/stages/forge-verify.d.ts.map +1 -0
- package/dist/pipeline/stages/{ralph-verify.js → forge-verify.js} +21 -24
- package/dist/pipeline/stages/forge-verify.js.map +1 -0
- package/dist/pipeline/stages/team-exec.d.ts +1 -1
- package/dist/pipeline/stages/team-exec.js +19 -19
- package/dist/pipeline/stages/team-exec.js.map +1 -1
- package/dist/pipeline/types.d.ts +12 -12
- package/dist/pipeline/types.d.ts.map +1 -1
- package/dist/pipeline/types.js +1 -1
- package/dist/planning/artifacts.d.ts +3 -4
- package/dist/planning/artifacts.d.ts.map +1 -1
- package/dist/planning/artifacts.js +2 -3
- package/dist/planning/artifacts.js.map +1 -1
- package/dist/question/policy.js +1 -1
- package/dist/runtime/bridge.d.ts.map +1 -1
- package/dist/runtime/bridge.js +70 -13
- package/dist/runtime/bridge.js.map +1 -1
- package/dist/scripts/codex-native-hook.js +30 -30
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/eval/eval-cross-server-party-flow.d.ts +3 -0
- package/dist/scripts/eval/eval-cross-server-party-flow.d.ts.map +1 -0
- package/dist/scripts/eval/eval-cross-server-party-flow.js +12 -0
- package/dist/scripts/eval/eval-cross-server-party-flow.js.map +1 -0
- package/dist/scripts/eval/eval-gui-onboarding-clarity.d.ts +3 -0
- package/dist/scripts/eval/eval-gui-onboarding-clarity.d.ts.map +1 -0
- package/dist/scripts/eval/eval-gui-onboarding-clarity.js +17 -0
- package/dist/scripts/eval/eval-gui-onboarding-clarity.js.map +1 -0
- package/dist/scripts/eval/eval-liveops-reward-loop-balance.d.ts +3 -0
- package/dist/scripts/eval/eval-liveops-reward-loop-balance.d.ts.map +1 -0
- package/dist/scripts/eval/eval-liveops-reward-loop-balance.js +12 -0
- package/dist/scripts/eval/eval-liveops-reward-loop-balance.js.map +1 -0
- package/dist/scripts/eval/eval-profile-datastore-recovery.d.ts +3 -0
- package/dist/scripts/eval/eval-profile-datastore-recovery.d.ts.map +1 -0
- package/dist/scripts/eval/eval-profile-datastore-recovery.js +17 -0
- package/dist/scripts/eval/eval-profile-datastore-recovery.js.map +1 -0
- package/dist/scripts/eval/eval-remote-contract-hardening.d.ts +3 -0
- package/dist/scripts/eval/eval-remote-contract-hardening.d.ts.map +1 -0
- package/dist/scripts/eval/eval-remote-contract-hardening.js +17 -0
- package/dist/scripts/eval/eval-remote-contract-hardening.js.map +1 -0
- package/dist/scripts/notify-fallback-watcher.js +140 -139
- package/dist/scripts/notify-fallback-watcher.js.map +1 -1
- package/dist/scripts/notify-hook/forge-session-resume.d.ts +23 -0
- package/dist/scripts/notify-hook/{ralph-session-resume.d.ts.map → forge-session-resume.d.ts.map} +1 -1
- package/dist/scripts/notify-hook/{ralph-session-resume.js → forge-session-resume.js} +37 -36
- package/dist/scripts/notify-hook/{ralph-session-resume.js.map → forge-session-resume.js.map} +1 -1
- package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-dispatch.js +34 -4
- package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
- package/dist/scripts/notify-hook/visual-verdict.js +3 -3
- package/dist/scripts/notify-hook.js +9 -9
- package/dist/scripts/run-test-files.js +1 -1
- package/dist/scripts/run-test-files.js.map +1 -1
- package/dist/scripts/surface-taxonomy.d.ts +23 -0
- package/dist/scripts/surface-taxonomy.d.ts.map +1 -0
- package/dist/scripts/surface-taxonomy.js +271 -0
- package/dist/scripts/surface-taxonomy.js.map +1 -0
- package/dist/scripts/sync-plugin-mirror.d.ts.map +1 -1
- package/dist/scripts/sync-plugin-mirror.js +5 -4
- package/dist/scripts/sync-plugin-mirror.js.map +1 -1
- package/dist/scripts/tmux-hook-engine.d.ts +1 -1
- package/dist/scripts/tmux-hook-engine.d.ts.map +1 -1
- package/dist/scripts/tmux-hook-engine.js +29 -20
- package/dist/scripts/tmux-hook-engine.js.map +1 -1
- package/dist/state/operations.d.ts +1 -1
- package/dist/state/operations.d.ts.map +1 -1
- package/dist/state/operations.js +18 -18
- package/dist/state/operations.js.map +1 -1
- package/dist/state/skill-active.d.ts +13 -1
- package/dist/state/skill-active.d.ts.map +1 -1
- package/dist/state/skill-active.js +38 -17
- package/dist/state/skill-active.js.map +1 -1
- package/dist/state/workflow-transition.d.ts +6 -5
- package/dist/state/workflow-transition.d.ts.map +1 -1
- package/dist/state/workflow-transition.js +27 -15
- package/dist/state/workflow-transition.js.map +1 -1
- package/dist/team/contracts.d.ts +1 -1
- package/dist/team/contracts.js +2 -2
- package/dist/team/followup-planner.d.ts +2 -2
- package/dist/team/followup-planner.d.ts.map +1 -1
- package/dist/team/followup-planner.js +16 -14
- package/dist/team/followup-planner.js.map +1 -1
- package/dist/team/idle-nudge.d.ts.map +1 -1
- package/dist/team/idle-nudge.js +3 -2
- package/dist/team/idle-nudge.js.map +1 -1
- package/dist/team/leader-activity.js +1 -1
- package/dist/team/model-contract.d.ts.map +1 -1
- package/dist/team/model-contract.js +4 -1
- package/dist/team/model-contract.js.map +1 -1
- package/dist/team/orchestrator.js +4 -4
- package/dist/team/orchestrator.js.map +1 -1
- package/dist/team/role-router.js +3 -3
- package/dist/team/role-router.js.map +1 -1
- package/dist/team/state/dispatch.d.ts.map +1 -1
- package/dist/team/state/dispatch.js +4 -1
- package/dist/team/state/dispatch.js.map +1 -1
- package/dist/team/tmux-session.d.ts +4 -0
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +42 -9
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/team/worktree.d.ts +1 -1
- package/dist/utils/platform-command.d.ts.map +1 -1
- package/dist/utils/platform-command.js +9 -0
- package/dist/utils/platform-command.js.map +1 -1
- package/dist/verification/verifier.d.ts +1 -1
- package/dist/verification/verifier.js +2 -2
- package/docs/STATE_MODEL.md +24 -24
- package/docs/agents.html +8 -16
- package/docs/archive/README.md +15 -0
- package/docs/{prompt-migration-changelog.md → archive/prompt-migration-changelog.md} +0 -11
- package/docs/{release-body-0.9.0.md → archive/release-body-0.9.0.md} +6 -24
- package/docs/{release-body-0.9.1.md → archive/release-body-0.9.1.md} +3 -3
- package/docs/codex-native-hooks.md +4 -4
- package/docs/contracts/forge-cancel-contract.md +20 -0
- package/docs/contracts/forge-state-contract.md +52 -0
- package/docs/contracts/multi-state-transition-contract.md +5 -5
- package/docs/contracts/multi-state-transition-review.md +3 -3
- package/docs/contracts/repo-aware-team-dag-decomposition.md +1 -1
- package/docs/contracts/rust-runtime-thin-adapter-contract.md +1 -1
- package/docs/contracts/team-startup-dispatch-latency.md +1 -1
- package/docs/getting-started.html +11 -1
- package/docs/guidance-schema.md +6 -3
- package/docs/index.html +55 -4
- package/docs/integrations.html +4 -3
- package/docs/issues/team-forge-followup-team.md +38 -0
- package/docs/openclaw-integration.md +2 -2
- package/docs/prompt-guidance-contract.md +11 -11
- package/docs/prs/{dev-deprecate-team-ralph.md → dev-deprecate-team-forge.md} +27 -27
- package/docs/prs/{dev-fix-ralph-live-pane-invariant.md → dev-fix-forge-live-pane-invariant.md} +7 -7
- package/docs/prs/{dev-team-ralph-workflow-positioning.md → dev-team-forge-workflow-positioning.md} +7 -7
- package/docs/qa/forge-persistence-gate.md +20 -0
- package/docs/qa/rust-runtime-thin-adapter-gate.md +31 -40
- package/docs/readme/README.de.md +14 -0
- package/docs/readme/README.el.md +14 -0
- package/docs/readme/README.es.md +14 -0
- package/docs/readme/README.fr.md +14 -0
- package/docs/readme/README.it.md +14 -0
- package/docs/readme/README.ja.md +14 -0
- package/docs/readme/README.ko.md +14 -0
- package/docs/readme/README.pl.md +14 -0
- package/docs/readme/README.pt.md +14 -0
- package/docs/readme/README.ru.md +14 -0
- package/docs/readme/README.tr.md +14 -0
- package/docs/readme/README.uk.md +14 -0
- package/docs/readme/README.vi.md +14 -0
- package/docs/readme/README.zh-TW.md +14 -0
- package/docs/readme/README.zh.md +14 -0
- package/docs/readme/rcs-cover.svg +75 -0
- package/docs/reference/canonical-vocabulary.md +106 -0
- package/docs/reference/forge-parity-matrix.md +26 -0
- package/docs/reference/forge-upstream-baseline.md +32 -0
- package/docs/reference/rcs-config-schema-routing.md +5 -5
- package/docs/reference/roblox-pre-action-protocol.md +4 -0
- package/docs/reference/roblox-taxonomy-migration-plan.md +46 -0
- package/docs/reference/roblox-workspace-standard.md +83 -0
- package/docs/reference/robloxstudio-mcp-compatibility.md +117 -0
- package/docs/reference/semantic-design-system.md +110 -0
- package/docs/reference/surface-map.md +131 -0
- package/docs/reference/team-allocation-rebalance-policy.md +1 -1
- package/docs/release-notes-v0.1.0.md +1 -1
- package/docs/release-notes-v0.1.1.md +49 -0
- package/docs/release-notes-v0.1.6.md +27 -0
- package/docs/reports/open-prs-dev-readiness-2026-04-09.md +2 -2
- package/docs/shared/agent-tiers.md +3 -3
- package/docs/skills.html +10 -12
- package/docs/troubleshooting.md +1 -1
- package/package.json +20 -13
- package/plugins/roblox-ai-os-creator-skills/.codex-plugin/plugin.json +1 -1
- package/plugins/roblox-ai-os-creator-skills/docs/reference/roblox-pre-action-protocol.md +4 -0
- package/plugins/roblox-ai-os-creator-skills/skills/ai-slop-cleaner/SKILL.md +14 -7
- package/plugins/roblox-ai-os-creator-skills/skills/analyze/SKILL.md +9 -2
- package/plugins/roblox-ai-os-creator-skills/skills/ask-claude/SKILL.md +7 -0
- package/plugins/roblox-ai-os-creator-skills/skills/ask-gemini/SKILL.md +7 -0
- package/plugins/roblox-ai-os-creator-skills/skills/autoforge/SKILL.md +7 -0
- package/plugins/roblox-ai-os-creator-skills/skills/autopilot/SKILL.md +48 -41
- package/plugins/roblox-ai-os-creator-skills/skills/autoresearch/SKILL.md +8 -1
- package/plugins/roblox-ai-os-creator-skills/skills/blueprint/SKILL.md +227 -9
- package/plugins/roblox-ai-os-creator-skills/skills/blueprint-loop/SKILL.md +7 -0
- package/plugins/roblox-ai-os-creator-skills/skills/blueprint-psych/SKILL.md +7 -0
- package/plugins/roblox-ai-os-creator-skills/skills/blueprint-retention/SKILL.md +7 -0
- package/plugins/roblox-ai-os-creator-skills/skills/blueprint-social/SKILL.md +7 -0
- package/plugins/roblox-ai-os-creator-skills/skills/brief/SKILL.md +7 -0
- package/plugins/roblox-ai-os-creator-skills/skills/brief-audience/SKILL.md +7 -0
- package/plugins/roblox-ai-os-creator-skills/skills/brief-motivation/SKILL.md +7 -0
- package/plugins/roblox-ai-os-creator-skills/skills/cancel/SKILL.md +59 -52
- package/plugins/roblox-ai-os-creator-skills/skills/code-review/SKILL.md +30 -24
- package/plugins/roblox-ai-os-creator-skills/skills/configure-notifications/SKILL.md +7 -0
- package/plugins/roblox-ai-os-creator-skills/skills/crew/SKILL.md +7 -0
- package/plugins/roblox-ai-os-creator-skills/skills/deep-interview/SKILL.md +25 -18
- package/plugins/roblox-ai-os-creator-skills/skills/doctor/SKILL.md +7 -0
- package/plugins/roblox-ai-os-creator-skills/skills/forge/SKILL.md +174 -11
- package/plugins/roblox-ai-os-creator-skills/skills/forge-community/SKILL.md +7 -0
- package/plugins/roblox-ai-os-creator-skills/skills/forge-daily-loop/SKILL.md +7 -0
- package/plugins/roblox-ai-os-creator-skills/skills/forge-event-loop/SKILL.md +7 -0
- package/plugins/roblox-ai-os-creator-skills/skills/forge-fomo/SKILL.md +7 -0
- package/plugins/roblox-ai-os-creator-skills/skills/forge-mastery/SKILL.md +7 -0
- package/plugins/roblox-ai-os-creator-skills/skills/forge-progression/SKILL.md +7 -0
- package/plugins/roblox-ai-os-creator-skills/skills/forge-reward-loop/SKILL.md +7 -0
- package/plugins/roblox-ai-os-creator-skills/skills/forge-status/SKILL.md +7 -0
- package/plugins/roblox-ai-os-creator-skills/skills/help/SKILL.md +8 -1
- package/plugins/roblox-ai-os-creator-skills/skills/hud/SKILL.md +16 -9
- package/plugins/roblox-ai-os-creator-skills/skills/note/SKILL.md +8 -1
- package/plugins/roblox-ai-os-creator-skills/skills/pipeline/SKILL.md +18 -11
- package/plugins/roblox-ai-os-creator-skills/skills/plan/SKILL.md +36 -29
- package/plugins/roblox-ai-os-creator-skills/skills/rcs-setup/SKILL.md +8 -1
- package/plugins/roblox-ai-os-creator-skills/skills/security-review/SKILL.md +120 -236
- package/plugins/roblox-ai-os-creator-skills/skills/skill/SKILL.md +20 -13
- package/plugins/roblox-ai-os-creator-skills/skills/team/SKILL.md +17 -11
- package/plugins/roblox-ai-os-creator-skills/skills/trace/SKILL.md +7 -0
- package/plugins/roblox-ai-os-creator-skills/skills/ultraqa/SKILL.md +10 -3
- package/plugins/roblox-ai-os-creator-skills/skills/ultrawork/SKILL.md +19 -12
- package/plugins/roblox-ai-os-creator-skills/skills/{visual-ralph → visual-forge}/SKILL.md +36 -27
- package/plugins/roblox-ai-os-creator-skills/skills/visual-verdict/SKILL.md +9 -2
- package/plugins/roblox-ai-os-creator-skills/skills/wiki/SKILL.md +10 -3
- package/plugins/roblox-ai-os-creator-skills/skills/worker/SKILL.md +16 -7
- package/plugins/roblox-ai-os-creator-skills/templates/roblox/pre-action-plan.md +1 -0
- package/prompts/analyst.md +7 -0
- package/prompts/architect.md +11 -4
- package/prompts/build-fixer.md +7 -0
- package/prompts/code-reviewer.md +9 -2
- package/prompts/code-simplifier.md +4 -0
- package/prompts/critic.md +13 -6
- package/prompts/debugger.md +8 -1
- package/prompts/dependency-expert.md +8 -1
- package/prompts/designer.md +20 -10
- package/prompts/executor.md +7 -0
- package/prompts/explore-harness.md +7 -0
- package/prompts/explore.md +7 -0
- package/prompts/git-master.md +8 -1
- package/prompts/planner.md +10 -3
- package/prompts/researcher.md +7 -0
- package/prompts/security-reviewer.md +76 -92
- package/prompts/sisyphus-lite.md +7 -0
- package/prompts/team-executor.md +7 -0
- package/prompts/team-orchestrator.md +9 -2
- package/prompts/test-engineer.md +11 -3
- package/prompts/verifier.md +7 -0
- package/prompts/vision.md +9 -2
- package/prompts/writer.md +11 -4
- package/skills/.agents/skills/roblox-animations/SKILL.md +220 -0
- package/skills/.agents/skills/roblox-datastores/SKILL.md +219 -0
- package/skills/.agents/skills/roblox-gui/SKILL.md +192 -0
- package/skills/.agents/skills/roblox-monetization/SKILL.md +208 -0
- package/skills/.agents/skills/roblox-performance/SKILL.md +230 -0
- package/skills/.agents/skills/roblox-remote-events/SKILL.md +199 -0
- package/skills/.agents/skills/roblox-security/SKILL.md +236 -0
- package/skills/ai-slop-cleaner/SKILL.md +14 -7
- package/skills/analyze/SKILL.md +9 -2
- package/skills/ask-claude/SKILL.md +7 -0
- package/skills/ask-gemini/SKILL.md +7 -0
- package/skills/autoforge/SKILL.md +7 -0
- package/skills/autopilot/SKILL.md +48 -41
- package/skills/autoresearch/SKILL.md +8 -1
- package/skills/blueprint/SKILL.md +227 -9
- package/skills/blueprint-loop/SKILL.md +7 -0
- package/skills/blueprint-psych/SKILL.md +7 -0
- package/skills/blueprint-retention/SKILL.md +7 -0
- package/skills/blueprint-social/SKILL.md +7 -0
- package/skills/brief/SKILL.md +7 -0
- package/skills/brief-audience/SKILL.md +7 -0
- package/skills/brief-motivation/SKILL.md +7 -0
- package/skills/build-fix/SKILL.md +9 -2
- package/skills/cancel/SKILL.md +59 -52
- package/skills/code-review/SKILL.md +30 -24
- package/skills/configure-notifications/SKILL.md +7 -0
- package/skills/crew/SKILL.md +7 -0
- package/skills/deep-interview/SKILL.md +25 -18
- package/skills/deepsearch/SKILL.md +7 -0
- package/skills/doctor/SKILL.md +7 -0
- package/skills/ecomode/SKILL.md +9 -2
- package/skills/forge/SKILL.md +174 -11
- package/skills/forge-community/SKILL.md +7 -0
- package/skills/forge-daily-loop/SKILL.md +7 -0
- package/skills/forge-event-loop/SKILL.md +7 -0
- package/skills/forge-fomo/SKILL.md +7 -0
- package/skills/{ralph-init → forge-init}/SKILL.md +20 -13
- package/skills/forge-mastery/SKILL.md +7 -0
- package/skills/forge-progression/SKILL.md +7 -0
- package/skills/forge-reward-loop/SKILL.md +7 -0
- package/skills/forge-status/SKILL.md +7 -0
- package/skills/git-master/SKILL.md +7 -0
- package/skills/help/SKILL.md +8 -1
- package/skills/hud/SKILL.md +16 -9
- package/skills/note/SKILL.md +8 -1
- package/skills/pipeline/SKILL.md +18 -11
- package/skills/plan/SKILL.md +36 -29
- package/skills/rcs-setup/SKILL.md +8 -1
- package/skills/review/SKILL.md +7 -0
- package/skills/security-review/SKILL.md +120 -236
- package/skills/skill/SKILL.md +20 -13
- package/skills/skills-lock.json +47 -0
- package/skills/swarm/SKILL.md +8 -1
- package/skills/tdd/SKILL.md +7 -0
- package/skills/team/SKILL.md +17 -11
- package/skills/trace/SKILL.md +7 -0
- package/skills/ultraqa/SKILL.md +10 -3
- package/skills/ultrawork/SKILL.md +19 -12
- package/skills/{visual-ralph → visual-forge}/SKILL.md +36 -27
- package/skills/visual-verdict/SKILL.md +9 -2
- package/skills/web-clone/SKILL.md +14 -7
- package/skills/wiki/SKILL.md +10 -3
- package/skills/worker/SKILL.md +16 -7
- package/src/scripts/__tests__/codex-native-hook.test.ts +386 -319
- package/src/scripts/__tests__/run-test-files.test.ts +6 -4
- package/src/scripts/__tests__/verify-native-agents.test.ts +16 -16
- package/src/scripts/codex-native-hook.ts +34 -34
- package/src/scripts/eval/eval-cross-server-party-flow.ts +14 -0
- package/src/scripts/eval/eval-gui-onboarding-clarity.ts +20 -0
- package/src/scripts/eval/eval-liveops-reward-loop-balance.ts +14 -0
- package/src/scripts/eval/eval-profile-datastore-recovery.ts +20 -0
- package/src/scripts/eval/eval-remote-contract-hardening.ts +20 -0
- package/src/scripts/notify-fallback-watcher.ts +147 -146
- package/src/scripts/notify-hook/__tests__/team-worker-posttooluse.test.ts +24 -10
- package/src/scripts/notify-hook/{ralph-session-resume.ts → forge-session-resume.ts} +45 -43
- package/src/scripts/notify-hook/team-dispatch.ts +31 -4
- package/src/scripts/notify-hook/visual-verdict.ts +3 -3
- package/src/scripts/notify-hook.ts +10 -10
- package/src/scripts/run-test-files.ts +1 -1
- package/src/scripts/surface-taxonomy.ts +316 -0
- package/src/scripts/sync-plugin-mirror.ts +5 -4
- package/src/scripts/tmux-hook-engine.ts +31 -19
- package/templates/AGENTS.md +24 -15
- package/templates/catalog-manifest.json +5 -88
- package/templates/roblox/pre-action-plan.md +1 -0
- package/templates/roblox/robloxstudio-mcp.codex.json +18 -0
- package/templates/roblox/robloxstudio-mcp.windows.json +22 -0
- package/dist/adapt/__tests__/foundation.test.d.ts +0 -2
- package/dist/adapt/__tests__/foundation.test.d.ts.map +0 -1
- package/dist/adapt/__tests__/foundation.test.js +0 -171
- package/dist/adapt/__tests__/foundation.test.js.map +0 -1
- package/dist/adapt/__tests__/hermes.test.d.ts +0 -2
- package/dist/adapt/__tests__/hermes.test.d.ts.map +0 -1
- package/dist/adapt/__tests__/hermes.test.js +0 -137
- package/dist/adapt/__tests__/hermes.test.js.map +0 -1
- package/dist/agents/__tests__/definitions.test.d.ts +0 -2
- package/dist/agents/__tests__/definitions.test.d.ts.map +0 -1
- package/dist/agents/__tests__/definitions.test.js +0 -62
- package/dist/agents/__tests__/definitions.test.js.map +0 -1
- package/dist/agents/__tests__/native-config.test.d.ts +0 -2
- package/dist/agents/__tests__/native-config.test.d.ts.map +0 -1
- package/dist/agents/__tests__/native-config.test.js +0 -278
- package/dist/agents/__tests__/native-config.test.js.map +0 -1
- package/dist/autoresearch/__tests__/contracts.test.d.ts +0 -2
- package/dist/autoresearch/__tests__/contracts.test.d.ts.map +0 -1
- package/dist/autoresearch/__tests__/contracts.test.js +0 -127
- package/dist/autoresearch/__tests__/contracts.test.js.map +0 -1
- package/dist/autoresearch/__tests__/runtime-parity-extra.test.d.ts +0 -2
- package/dist/autoresearch/__tests__/runtime-parity-extra.test.d.ts.map +0 -1
- package/dist/autoresearch/__tests__/runtime-parity-extra.test.js +0 -356
- package/dist/autoresearch/__tests__/runtime-parity-extra.test.js.map +0 -1
- package/dist/autoresearch/__tests__/runtime.test.d.ts +0 -2
- package/dist/autoresearch/__tests__/runtime.test.d.ts.map +0 -1
- package/dist/autoresearch/__tests__/runtime.test.js +0 -218
- package/dist/autoresearch/__tests__/runtime.test.js.map +0 -1
- package/dist/autoresearch/__tests__/skill-validation.test.d.ts +0 -2
- package/dist/autoresearch/__tests__/skill-validation.test.d.ts.map +0 -1
- package/dist/autoresearch/__tests__/skill-validation.test.js +0 -91
- package/dist/autoresearch/__tests__/skill-validation.test.js.map +0 -1
- package/dist/catalog/__tests__/generator.test.d.ts +0 -2
- package/dist/catalog/__tests__/generator.test.d.ts.map +0 -1
- package/dist/catalog/__tests__/generator.test.js +0 -49
- package/dist/catalog/__tests__/generator.test.js.map +0 -1
- package/dist/catalog/__tests__/plugin-bundle-ssot.test.d.ts +0 -2
- package/dist/catalog/__tests__/plugin-bundle-ssot.test.d.ts.map +0 -1
- package/dist/catalog/__tests__/plugin-bundle-ssot.test.js +0 -83
- package/dist/catalog/__tests__/plugin-bundle-ssot.test.js.map +0 -1
- package/dist/catalog/__tests__/schema.test.d.ts +0 -2
- package/dist/catalog/__tests__/schema.test.d.ts.map +0 -1
- package/dist/catalog/__tests__/schema.test.js +0 -91
- package/dist/catalog/__tests__/schema.test.js.map +0 -1
- package/dist/cli/__tests__/adapt-help.test.d.ts +0 -2
- package/dist/cli/__tests__/adapt-help.test.d.ts.map +0 -1
- package/dist/cli/__tests__/adapt-help.test.js +0 -37
- package/dist/cli/__tests__/adapt-help.test.js.map +0 -1
- package/dist/cli/__tests__/adapt.test.d.ts +0 -2
- package/dist/cli/__tests__/adapt.test.d.ts.map +0 -1
- package/dist/cli/__tests__/adapt.test.js +0 -62
- package/dist/cli/__tests__/adapt.test.js.map +0 -1
- package/dist/cli/__tests__/agents-init.test.d.ts +0 -2
- package/dist/cli/__tests__/agents-init.test.d.ts.map +0 -1
- package/dist/cli/__tests__/agents-init.test.js +0 -184
- package/dist/cli/__tests__/agents-init.test.js.map +0 -1
- package/dist/cli/__tests__/agents.test.d.ts +0 -2
- package/dist/cli/__tests__/agents.test.d.ts.map +0 -1
- package/dist/cli/__tests__/agents.test.js +0 -137
- package/dist/cli/__tests__/agents.test.js.map +0 -1
- package/dist/cli/__tests__/ask.test.d.ts +0 -2
- package/dist/cli/__tests__/ask.test.d.ts.map +0 -1
- package/dist/cli/__tests__/ask.test.js +0 -265
- package/dist/cli/__tests__/ask.test.js.map +0 -1
- package/dist/cli/__tests__/autoresearch-guided.test.d.ts +0 -2
- package/dist/cli/__tests__/autoresearch-guided.test.d.ts.map +0 -1
- package/dist/cli/__tests__/autoresearch-guided.test.js +0 -365
- package/dist/cli/__tests__/autoresearch-guided.test.js.map +0 -1
- package/dist/cli/__tests__/autoresearch.test.d.ts +0 -2
- package/dist/cli/__tests__/autoresearch.test.d.ts.map +0 -1
- package/dist/cli/__tests__/autoresearch.test.js +0 -203
- package/dist/cli/__tests__/autoresearch.test.js.map +0 -1
- package/dist/cli/__tests__/catalog-contract.test.d.ts +0 -2
- package/dist/cli/__tests__/catalog-contract.test.d.ts.map +0 -1
- package/dist/cli/__tests__/catalog-contract.test.js +0 -18
- package/dist/cli/__tests__/catalog-contract.test.js.map +0 -1
- package/dist/cli/__tests__/cleanup.test.d.ts +0 -2
- package/dist/cli/__tests__/cleanup.test.d.ts.map +0 -1
- package/dist/cli/__tests__/cleanup.test.js +0 -419
- package/dist/cli/__tests__/cleanup.test.js.map +0 -1
- package/dist/cli/__tests__/codex-plugin-layout.test.d.ts +0 -2
- package/dist/cli/__tests__/codex-plugin-layout.test.d.ts.map +0 -1
- package/dist/cli/__tests__/codex-plugin-layout.test.js +0 -210
- package/dist/cli/__tests__/codex-plugin-layout.test.js.map +0 -1
- package/dist/cli/__tests__/doctor-context-window-warning.test.d.ts +0 -2
- package/dist/cli/__tests__/doctor-context-window-warning.test.d.ts.map +0 -1
- package/dist/cli/__tests__/doctor-context-window-warning.test.js +0 -122
- package/dist/cli/__tests__/doctor-context-window-warning.test.js.map +0 -1
- package/dist/cli/__tests__/doctor-invalid-config.test.d.ts +0 -2
- package/dist/cli/__tests__/doctor-invalid-config.test.d.ts.map +0 -1
- package/dist/cli/__tests__/doctor-invalid-config.test.js +0 -52
- package/dist/cli/__tests__/doctor-invalid-config.test.js.map +0 -1
- package/dist/cli/__tests__/doctor-team.test.d.ts +0 -2
- package/dist/cli/__tests__/doctor-team.test.d.ts.map +0 -1
- package/dist/cli/__tests__/doctor-team.test.js +0 -299
- package/dist/cli/__tests__/doctor-team.test.js.map +0 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.d.ts +0 -2
- package/dist/cli/__tests__/doctor-warning-copy.test.d.ts.map +0 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js +0 -438
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +0 -1
- package/dist/cli/__tests__/error-handling-warnings.test.d.ts +0 -2
- package/dist/cli/__tests__/error-handling-warnings.test.d.ts.map +0 -1
- package/dist/cli/__tests__/error-handling-warnings.test.js +0 -52
- package/dist/cli/__tests__/error-handling-warnings.test.js.map +0 -1
- package/dist/cli/__tests__/exec.test.d.ts +0 -2
- package/dist/cli/__tests__/exec.test.d.ts.map +0 -1
- package/dist/cli/__tests__/exec.test.js +0 -213
- package/dist/cli/__tests__/exec.test.js.map +0 -1
- package/dist/cli/__tests__/explore-windows-diagnostics.test.d.ts +0 -2
- package/dist/cli/__tests__/explore-windows-diagnostics.test.d.ts.map +0 -1
- package/dist/cli/__tests__/explore-windows-diagnostics.test.js +0 -17
- package/dist/cli/__tests__/explore-windows-diagnostics.test.js.map +0 -1
- package/dist/cli/__tests__/explore.test.d.ts +0 -2
- package/dist/cli/__tests__/explore.test.d.ts.map +0 -1
- package/dist/cli/__tests__/explore.test.js +0 -1090
- package/dist/cli/__tests__/explore.test.js.map +0 -1
- package/dist/cli/__tests__/hooks.test.d.ts +0 -2
- package/dist/cli/__tests__/hooks.test.d.ts.map +0 -1
- package/dist/cli/__tests__/hooks.test.js +0 -55
- package/dist/cli/__tests__/hooks.test.js.map +0 -1
- package/dist/cli/__tests__/index.test.d.ts +0 -2
- package/dist/cli/__tests__/index.test.d.ts.map +0 -1
- package/dist/cli/__tests__/index.test.js +0 -2259
- package/dist/cli/__tests__/index.test.js.map +0 -1
- package/dist/cli/__tests__/launch-fallback.test.d.ts +0 -2
- package/dist/cli/__tests__/launch-fallback.test.d.ts.map +0 -1
- package/dist/cli/__tests__/launch-fallback.test.js +0 -661
- package/dist/cli/__tests__/launch-fallback.test.js.map +0 -1
- package/dist/cli/__tests__/lifecycle-notifications.test.d.ts +0 -2
- package/dist/cli/__tests__/lifecycle-notifications.test.d.ts.map +0 -1
- package/dist/cli/__tests__/lifecycle-notifications.test.js +0 -48
- package/dist/cli/__tests__/lifecycle-notifications.test.js.map +0 -1
- package/dist/cli/__tests__/list.test.d.ts +0 -2
- package/dist/cli/__tests__/list.test.d.ts.map +0 -1
- package/dist/cli/__tests__/list.test.js +0 -38
- package/dist/cli/__tests__/list.test.js.map +0 -1
- package/dist/cli/__tests__/mcp-parity.test.d.ts +0 -2
- package/dist/cli/__tests__/mcp-parity.test.d.ts.map +0 -1
- package/dist/cli/__tests__/mcp-parity.test.js +0 -228
- package/dist/cli/__tests__/mcp-parity.test.js.map +0 -1
- package/dist/cli/__tests__/mcp-serve.test.d.ts +0 -2
- package/dist/cli/__tests__/mcp-serve.test.d.ts.map +0 -1
- package/dist/cli/__tests__/mcp-serve.test.js +0 -64
- package/dist/cli/__tests__/mcp-serve.test.js.map +0 -1
- package/dist/cli/__tests__/native-assets.test.d.ts +0 -2
- package/dist/cli/__tests__/native-assets.test.d.ts.map +0 -1
- package/dist/cli/__tests__/native-assets.test.js +0 -308
- package/dist/cli/__tests__/native-assets.test.js.map +0 -1
- package/dist/cli/__tests__/native-hook-dispatch-contract.test.d.ts +0 -2
- package/dist/cli/__tests__/native-hook-dispatch-contract.test.d.ts.map +0 -1
- package/dist/cli/__tests__/native-hook-dispatch-contract.test.js +0 -11
- package/dist/cli/__tests__/native-hook-dispatch-contract.test.js.map +0 -1
- package/dist/cli/__tests__/nested-help-routing.test.d.ts +0 -2
- package/dist/cli/__tests__/nested-help-routing.test.d.ts.map +0 -1
- package/dist/cli/__tests__/nested-help-routing.test.js +0 -96
- package/dist/cli/__tests__/nested-help-routing.test.js.map +0 -1
- package/dist/cli/__tests__/package-bin-contract.test.d.ts +0 -2
- package/dist/cli/__tests__/package-bin-contract.test.d.ts.map +0 -1
- package/dist/cli/__tests__/package-bin-contract.test.js +0 -177
- package/dist/cli/__tests__/package-bin-contract.test.js.map +0 -1
- package/dist/cli/__tests__/packaged-explore-harness-lock.d.ts +0 -3
- package/dist/cli/__tests__/packaged-explore-harness-lock.d.ts.map +0 -1
- package/dist/cli/__tests__/packaged-explore-harness-lock.js +0 -67
- package/dist/cli/__tests__/packaged-explore-harness-lock.js.map +0 -1
- package/dist/cli/__tests__/packaged-script-resolution.test.d.ts +0 -2
- package/dist/cli/__tests__/packaged-script-resolution.test.d.ts.map +0 -1
- package/dist/cli/__tests__/packaged-script-resolution.test.js +0 -19
- package/dist/cli/__tests__/packaged-script-resolution.test.js.map +0 -1
- package/dist/cli/__tests__/prompt-skill-sanitization.test.d.ts +0 -2
- package/dist/cli/__tests__/prompt-skill-sanitization.test.d.ts.map +0 -1
- package/dist/cli/__tests__/prompt-skill-sanitization.test.js +0 -48
- package/dist/cli/__tests__/prompt-skill-sanitization.test.js.map +0 -1
- package/dist/cli/__tests__/question.test.d.ts +0 -2
- package/dist/cli/__tests__/question.test.d.ts.map +0 -1
- package/dist/cli/__tests__/question.test.js +0 -633
- package/dist/cli/__tests__/question.test.js.map +0 -1
- package/dist/cli/__tests__/ralph-deslop-contract.test.d.ts +0 -2
- package/dist/cli/__tests__/ralph-deslop-contract.test.d.ts.map +0 -1
- package/dist/cli/__tests__/ralph-deslop-contract.test.js +0 -28
- package/dist/cli/__tests__/ralph-deslop-contract.test.js.map +0 -1
- package/dist/cli/__tests__/ralph-prd-deep-interview.test.d.ts +0 -2
- package/dist/cli/__tests__/ralph-prd-deep-interview.test.d.ts.map +0 -1
- package/dist/cli/__tests__/ralph-prd-deep-interview.test.js +0 -24
- package/dist/cli/__tests__/ralph-prd-deep-interview.test.js.map +0 -1
- package/dist/cli/__tests__/ralph-prd-smoke.test.d.ts +0 -2
- package/dist/cli/__tests__/ralph-prd-smoke.test.d.ts.map +0 -1
- package/dist/cli/__tests__/ralph-prd-smoke.test.js +0 -167
- package/dist/cli/__tests__/ralph-prd-smoke.test.js.map +0 -1
- package/dist/cli/__tests__/ralph.test.d.ts +0 -2
- package/dist/cli/__tests__/ralph.test.d.ts.map +0 -1
- package/dist/cli/__tests__/ralph.test.js +0 -256
- package/dist/cli/__tests__/ralph.test.js.map +0 -1
- package/dist/cli/__tests__/resume.test.d.ts +0 -2
- package/dist/cli/__tests__/resume.test.d.ts.map +0 -1
- package/dist/cli/__tests__/resume.test.js +0 -84
- package/dist/cli/__tests__/resume.test.js.map +0 -1
- package/dist/cli/__tests__/session-scoped-runtime.test.d.ts +0 -2
- package/dist/cli/__tests__/session-scoped-runtime.test.d.ts.map +0 -1
- package/dist/cli/__tests__/session-scoped-runtime.test.js +0 -146
- package/dist/cli/__tests__/session-scoped-runtime.test.js.map +0 -1
- package/dist/cli/__tests__/session-search-help.test.d.ts +0 -2
- package/dist/cli/__tests__/session-search-help.test.d.ts.map +0 -1
- package/dist/cli/__tests__/session-search-help.test.js +0 -76
- package/dist/cli/__tests__/session-search-help.test.js.map +0 -1
- package/dist/cli/__tests__/session-search.test.d.ts +0 -2
- package/dist/cli/__tests__/session-search.test.d.ts.map +0 -1
- package/dist/cli/__tests__/session-search.test.js +0 -77
- package/dist/cli/__tests__/session-search.test.js.map +0 -1
- package/dist/cli/__tests__/setup-agents-overwrite.test.d.ts +0 -2
- package/dist/cli/__tests__/setup-agents-overwrite.test.d.ts.map +0 -1
- package/dist/cli/__tests__/setup-agents-overwrite.test.js +0 -457
- package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +0 -1
- package/dist/cli/__tests__/setup-gh-star.test.d.ts +0 -2
- package/dist/cli/__tests__/setup-gh-star.test.d.ts.map +0 -1
- package/dist/cli/__tests__/setup-gh-star.test.js +0 -67
- package/dist/cli/__tests__/setup-gh-star.test.js.map +0 -1
- package/dist/cli/__tests__/setup-hooks-shared-ownership.test.d.ts +0 -2
- package/dist/cli/__tests__/setup-hooks-shared-ownership.test.d.ts.map +0 -1
- package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js +0 -189
- package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js.map +0 -1
- package/dist/cli/__tests__/setup-install-mode.test.d.ts +0 -2
- package/dist/cli/__tests__/setup-install-mode.test.d.ts.map +0 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +0 -873
- package/dist/cli/__tests__/setup-install-mode.test.js.map +0 -1
- package/dist/cli/__tests__/setup-prompts-overwrite.test.d.ts +0 -2
- package/dist/cli/__tests__/setup-prompts-overwrite.test.d.ts.map +0 -1
- package/dist/cli/__tests__/setup-prompts-overwrite.test.js +0 -191
- package/dist/cli/__tests__/setup-prompts-overwrite.test.js.map +0 -1
- package/dist/cli/__tests__/setup-refresh.test.d.ts +0 -2
- package/dist/cli/__tests__/setup-refresh.test.d.ts.map +0 -1
- package/dist/cli/__tests__/setup-refresh.test.js +0 -591
- package/dist/cli/__tests__/setup-refresh.test.js.map +0 -1
- package/dist/cli/__tests__/setup-scope.test.d.ts +0 -2
- package/dist/cli/__tests__/setup-scope.test.d.ts.map +0 -1
- package/dist/cli/__tests__/setup-scope.test.js +0 -340
- package/dist/cli/__tests__/setup-scope.test.js.map +0 -1
- package/dist/cli/__tests__/setup-skill-validation.test.d.ts +0 -2
- package/dist/cli/__tests__/setup-skill-validation.test.d.ts.map +0 -1
- package/dist/cli/__tests__/setup-skill-validation.test.js +0 -44
- package/dist/cli/__tests__/setup-skill-validation.test.js.map +0 -1
- package/dist/cli/__tests__/setup-skills-overwrite.test.d.ts +0 -2
- package/dist/cli/__tests__/setup-skills-overwrite.test.d.ts.map +0 -1
- package/dist/cli/__tests__/setup-skills-overwrite.test.js +0 -295
- package/dist/cli/__tests__/setup-skills-overwrite.test.js.map +0 -1
- package/dist/cli/__tests__/sidecar.test.d.ts +0 -2
- package/dist/cli/__tests__/sidecar.test.d.ts.map +0 -1
- package/dist/cli/__tests__/sidecar.test.js +0 -24
- package/dist/cli/__tests__/sidecar.test.js.map +0 -1
- package/dist/cli/__tests__/sparkshell-cli.test.d.ts +0 -2
- package/dist/cli/__tests__/sparkshell-cli.test.d.ts.map +0 -1
- package/dist/cli/__tests__/sparkshell-cli.test.js +0 -400
- package/dist/cli/__tests__/sparkshell-cli.test.js.map +0 -1
- package/dist/cli/__tests__/sparkshell-packaging.test.d.ts +0 -2
- package/dist/cli/__tests__/sparkshell-packaging.test.d.ts.map +0 -1
- package/dist/cli/__tests__/sparkshell-packaging.test.js +0 -74
- package/dist/cli/__tests__/sparkshell-packaging.test.js.map +0 -1
- package/dist/cli/__tests__/star-prompt.test.d.ts +0 -2
- package/dist/cli/__tests__/star-prompt.test.d.ts.map +0 -1
- package/dist/cli/__tests__/star-prompt.test.js +0 -172
- package/dist/cli/__tests__/star-prompt.test.js.map +0 -1
- package/dist/cli/__tests__/state.test.d.ts +0 -2
- package/dist/cli/__tests__/state.test.d.ts.map +0 -1
- package/dist/cli/__tests__/state.test.js +0 -46
- package/dist/cli/__tests__/state.test.js.map +0 -1
- package/dist/cli/__tests__/team-decompose.test.d.ts +0 -2
- package/dist/cli/__tests__/team-decompose.test.d.ts.map +0 -1
- package/dist/cli/__tests__/team-decompose.test.js +0 -133
- package/dist/cli/__tests__/team-decompose.test.js.map +0 -1
- package/dist/cli/__tests__/team.test.d.ts +0 -2
- package/dist/cli/__tests__/team.test.d.ts.map +0 -1
- package/dist/cli/__tests__/team.test.js +0 -1820
- package/dist/cli/__tests__/team.test.js.map +0 -1
- package/dist/cli/__tests__/uninstall.test.d.ts +0 -2
- package/dist/cli/__tests__/uninstall.test.d.ts.map +0 -1
- package/dist/cli/__tests__/uninstall.test.js +0 -766
- package/dist/cli/__tests__/uninstall.test.js.map +0 -1
- package/dist/cli/__tests__/update.test.d.ts +0 -2
- package/dist/cli/__tests__/update.test.d.ts.map +0 -1
- package/dist/cli/__tests__/update.test.js +0 -589
- package/dist/cli/__tests__/update.test.js.map +0 -1
- package/dist/cli/__tests__/version-sync-contract.test.d.ts +0 -2
- package/dist/cli/__tests__/version-sync-contract.test.d.ts.map +0 -1
- package/dist/cli/__tests__/version-sync-contract.test.js +0 -41
- package/dist/cli/__tests__/version-sync-contract.test.js.map +0 -1
- package/dist/cli/__tests__/version.test.d.ts +0 -2
- package/dist/cli/__tests__/version.test.d.ts.map +0 -1
- package/dist/cli/__tests__/version.test.js +0 -21
- package/dist/cli/__tests__/version.test.js.map +0 -1
- package/dist/cli/__tests__/windows-popup-loop-contract.test.d.ts +0 -2
- package/dist/cli/__tests__/windows-popup-loop-contract.test.d.ts.map +0 -1
- package/dist/cli/__tests__/windows-popup-loop-contract.test.js +0 -31
- package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +0 -1
- package/dist/cli/ralph.d.ts +0 -17
- package/dist/compat/__tests__/doctor-contract.test.d.ts +0 -2
- package/dist/compat/__tests__/doctor-contract.test.d.ts.map +0 -1
- package/dist/compat/__tests__/doctor-contract.test.js +0 -108
- package/dist/compat/__tests__/doctor-contract.test.js.map +0 -1
- package/dist/compat/__tests__/rust-runtime-compat.test.d.ts +0 -2
- package/dist/compat/__tests__/rust-runtime-compat.test.d.ts.map +0 -1
- package/dist/compat/__tests__/rust-runtime-compat.test.js +0 -218
- package/dist/compat/__tests__/rust-runtime-compat.test.js.map +0 -1
- package/dist/config/__tests__/codex-hooks.test.d.ts +0 -2
- package/dist/config/__tests__/codex-hooks.test.d.ts.map +0 -1
- package/dist/config/__tests__/codex-hooks.test.js +0 -77
- package/dist/config/__tests__/codex-hooks.test.js.map +0 -1
- package/dist/config/__tests__/generator-idempotent.test.d.ts +0 -2
- package/dist/config/__tests__/generator-idempotent.test.d.ts.map +0 -1
- package/dist/config/__tests__/generator-idempotent.test.js +0 -882
- package/dist/config/__tests__/generator-idempotent.test.js.map +0 -1
- package/dist/config/__tests__/generator-notify.test.d.ts +0 -2
- package/dist/config/__tests__/generator-notify.test.d.ts.map +0 -1
- package/dist/config/__tests__/generator-notify.test.js +0 -343
- package/dist/config/__tests__/generator-notify.test.js.map +0 -1
- package/dist/config/__tests__/generator-status-line-presets.test.d.ts +0 -2
- package/dist/config/__tests__/generator-status-line-presets.test.d.ts.map +0 -1
- package/dist/config/__tests__/generator-status-line-presets.test.js +0 -203
- package/dist/config/__tests__/generator-status-line-presets.test.js.map +0 -1
- package/dist/config/__tests__/mcp-registry.test.d.ts +0 -2
- package/dist/config/__tests__/mcp-registry.test.d.ts.map +0 -1
- package/dist/config/__tests__/mcp-registry.test.js +0 -190
- package/dist/config/__tests__/mcp-registry.test.js.map +0 -1
- package/dist/config/__tests__/models.test.d.ts +0 -2
- package/dist/config/__tests__/models.test.d.ts.map +0 -1
- package/dist/config/__tests__/models.test.js +0 -224
- package/dist/config/__tests__/models.test.js.map +0 -1
- package/dist/config/__tests__/wiki-config-contract.test.d.ts +0 -2
- package/dist/config/__tests__/wiki-config-contract.test.d.ts.map +0 -1
- package/dist/config/__tests__/wiki-config-contract.test.js +0 -19
- package/dist/config/__tests__/wiki-config-contract.test.js.map +0 -1
- package/dist/document-refresh/__tests__/enforcer.test.d.ts +0 -2
- package/dist/document-refresh/__tests__/enforcer.test.d.ts.map +0 -1
- package/dist/document-refresh/__tests__/enforcer.test.js +0 -128
- package/dist/document-refresh/__tests__/enforcer.test.js.map +0 -1
- package/dist/hooks/__tests__/agents-overlay.test.d.ts +0 -8
- package/dist/hooks/__tests__/agents-overlay.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/agents-overlay.test.js +0 -644
- package/dist/hooks/__tests__/agents-overlay.test.js.map +0 -1
- package/dist/hooks/__tests__/analyze-routing-contract.test.d.ts +0 -2
- package/dist/hooks/__tests__/analyze-routing-contract.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/analyze-routing-contract.test.js +0 -45
- package/dist/hooks/__tests__/analyze-routing-contract.test.js.map +0 -1
- package/dist/hooks/__tests__/analyze-skill-contract.test.d.ts +0 -2
- package/dist/hooks/__tests__/analyze-skill-contract.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/analyze-skill-contract.test.js +0 -48
- package/dist/hooks/__tests__/analyze-skill-contract.test.js.map +0 -1
- package/dist/hooks/__tests__/anti-slop-workflow.test.d.ts +0 -2
- package/dist/hooks/__tests__/anti-slop-workflow.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/anti-slop-workflow.test.js +0 -146
- package/dist/hooks/__tests__/anti-slop-workflow.test.js.map +0 -1
- package/dist/hooks/__tests__/autopilot-skill-contract.test.d.ts +0 -2
- package/dist/hooks/__tests__/autopilot-skill-contract.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/autopilot-skill-contract.test.js +0 -37
- package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +0 -1
- package/dist/hooks/__tests__/clawhip-event-contract.test.d.ts +0 -2
- package/dist/hooks/__tests__/clawhip-event-contract.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/clawhip-event-contract.test.js +0 -37
- package/dist/hooks/__tests__/clawhip-event-contract.test.js.map +0 -1
- package/dist/hooks/__tests__/code-review-skill-contract.test.d.ts +0 -2
- package/dist/hooks/__tests__/code-review-skill-contract.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/code-review-skill-contract.test.js +0 -56
- package/dist/hooks/__tests__/code-review-skill-contract.test.js.map +0 -1
- package/dist/hooks/__tests__/codebase-map.test.d.ts +0 -8
- package/dist/hooks/__tests__/codebase-map.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/codebase-map.test.js +0 -218
- package/dist/hooks/__tests__/codebase-map.test.js.map +0 -1
- package/dist/hooks/__tests__/consensus-execution-handoff.test.d.ts +0 -18
- package/dist/hooks/__tests__/consensus-execution-handoff.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/consensus-execution-handoff.test.js +0 -234
- package/dist/hooks/__tests__/consensus-execution-handoff.test.js.map +0 -1
- package/dist/hooks/__tests__/debugger-log-recency-contract.test.d.ts +0 -2
- package/dist/hooks/__tests__/debugger-log-recency-contract.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/debugger-log-recency-contract.test.js +0 -20
- package/dist/hooks/__tests__/debugger-log-recency-contract.test.js.map +0 -1
- package/dist/hooks/__tests__/deep-interview-contract.test.d.ts +0 -2
- package/dist/hooks/__tests__/deep-interview-contract.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/deep-interview-contract.test.js +0 -213
- package/dist/hooks/__tests__/deep-interview-contract.test.js.map +0 -1
- package/dist/hooks/__tests__/explicit-terminal-stop-docs-contract.test.d.ts +0 -2
- package/dist/hooks/__tests__/explicit-terminal-stop-docs-contract.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/explicit-terminal-stop-docs-contract.test.js +0 -43
- package/dist/hooks/__tests__/explicit-terminal-stop-docs-contract.test.js.map +0 -1
- package/dist/hooks/__tests__/explicit-terminal-stop-model-docs-contract.test.d.ts +0 -2
- package/dist/hooks/__tests__/explicit-terminal-stop-model-docs-contract.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/explicit-terminal-stop-model-docs-contract.test.js +0 -38
- package/dist/hooks/__tests__/explicit-terminal-stop-model-docs-contract.test.js.map +0 -1
- package/dist/hooks/__tests__/explore-routing.test.d.ts +0 -2
- package/dist/hooks/__tests__/explore-routing.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/explore-routing.test.js +0 -43
- package/dist/hooks/__tests__/explore-routing.test.js.map +0 -1
- package/dist/hooks/__tests__/explore-sparkshell-guidance-contract.test.d.ts +0 -2
- package/dist/hooks/__tests__/explore-sparkshell-guidance-contract.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/explore-sparkshell-guidance-contract.test.js +0 -69
- package/dist/hooks/__tests__/explore-sparkshell-guidance-contract.test.js.map +0 -1
- package/dist/hooks/__tests__/keyword-detector.test.d.ts +0 -2
- package/dist/hooks/__tests__/keyword-detector.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +0 -1716
- package/dist/hooks/__tests__/keyword-detector.test.js.map +0 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.d.ts +0 -2
- package/dist/hooks/__tests__/notify-fallback-watcher.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +0 -3898
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +0 -1
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.d.ts +0 -2
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +0 -786
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +0 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.d.ts +0 -2
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +0 -2397
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +0 -1
- package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.d.ts +0 -2
- package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js +0 -160
- package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js.map +0 -1
- package/dist/hooks/__tests__/notify-hook-managed-tmux.test.d.ts +0 -2
- package/dist/hooks/__tests__/notify-hook-managed-tmux.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +0 -1178
- package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +0 -1
- package/dist/hooks/__tests__/notify-hook-modules.test.d.ts +0 -9
- package/dist/hooks/__tests__/notify-hook-modules.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/notify-hook-modules.test.js +0 -529
- package/dist/hooks/__tests__/notify-hook-modules.test.js.map +0 -1
- package/dist/hooks/__tests__/notify-hook-native-dispatch-contract.test.d.ts +0 -2
- package/dist/hooks/__tests__/notify-hook-native-dispatch-contract.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/notify-hook-native-dispatch-contract.test.js +0 -14
- package/dist/hooks/__tests__/notify-hook-native-dispatch-contract.test.js.map +0 -1
- package/dist/hooks/__tests__/notify-hook-ralph-resume.test.d.ts +0 -2
- package/dist/hooks/__tests__/notify-hook-ralph-resume.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js +0 -682
- package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js.map +0 -1
- package/dist/hooks/__tests__/notify-hook-regression-205.test.d.ts +0 -9
- package/dist/hooks/__tests__/notify-hook-regression-205.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/notify-hook-regression-205.test.js +0 -255
- package/dist/hooks/__tests__/notify-hook-regression-205.test.js.map +0 -1
- package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.d.ts +0 -2
- package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js +0 -162
- package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js.map +0 -1
- package/dist/hooks/__tests__/notify-hook-session-scope.test.d.ts +0 -2
- package/dist/hooks/__tests__/notify-hook-session-scope.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/notify-hook-session-scope.test.js +0 -301
- package/dist/hooks/__tests__/notify-hook-session-scope.test.js.map +0 -1
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.d.ts +0 -2
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +0 -1510
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +0 -1
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.d.ts +0 -2
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +0 -2879
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +0 -1
- package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.d.ts +0 -2
- package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +0 -228
- package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +0 -1
- package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.d.ts +0 -2
- package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.js +0 -35
- package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.js.map +0 -1
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.d.ts +0 -2
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +0 -1589
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +0 -1
- package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.d.ts +0 -10
- package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js +0 -0
- package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +0 -1
- package/dist/hooks/__tests__/notify-hook-visual-verdict.test.d.ts +0 -11
- package/dist/hooks/__tests__/notify-hook-visual-verdict.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/notify-hook-visual-verdict.test.js +0 -266
- package/dist/hooks/__tests__/notify-hook-visual-verdict.test.js.map +0 -1
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.d.ts +0 -2
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +0 -895
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +0 -1
- package/dist/hooks/__tests__/openclaw-setup-contract.test.d.ts +0 -2
- package/dist/hooks/__tests__/openclaw-setup-contract.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/openclaw-setup-contract.test.js +0 -61
- package/dist/hooks/__tests__/openclaw-setup-contract.test.js.map +0 -1
- package/dist/hooks/__tests__/pre-context-gate-skills.test.d.ts +0 -2
- package/dist/hooks/__tests__/pre-context-gate-skills.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/pre-context-gate-skills.test.js +0 -40
- package/dist/hooks/__tests__/pre-context-gate-skills.test.js.map +0 -1
- package/dist/hooks/__tests__/prompt-guidance-catalog.test.d.ts +0 -2
- package/dist/hooks/__tests__/prompt-guidance-catalog.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/prompt-guidance-catalog.test.js +0 -11
- package/dist/hooks/__tests__/prompt-guidance-catalog.test.js.map +0 -1
- package/dist/hooks/__tests__/prompt-guidance-contract.test.d.ts +0 -2
- package/dist/hooks/__tests__/prompt-guidance-contract.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/prompt-guidance-contract.test.js +0 -38
- package/dist/hooks/__tests__/prompt-guidance-contract.test.js.map +0 -1
- package/dist/hooks/__tests__/prompt-guidance-fragments.test.d.ts +0 -2
- package/dist/hooks/__tests__/prompt-guidance-fragments.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/prompt-guidance-fragments.test.js +0 -48
- package/dist/hooks/__tests__/prompt-guidance-fragments.test.js.map +0 -1
- package/dist/hooks/__tests__/prompt-guidance-scenarios.test.d.ts +0 -2
- package/dist/hooks/__tests__/prompt-guidance-scenarios.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/prompt-guidance-scenarios.test.js +0 -11
- package/dist/hooks/__tests__/prompt-guidance-scenarios.test.js.map +0 -1
- package/dist/hooks/__tests__/prompt-guidance-test-helpers.d.ts +0 -5
- package/dist/hooks/__tests__/prompt-guidance-test-helpers.d.ts.map +0 -1
- package/dist/hooks/__tests__/prompt-guidance-test-helpers.js +0 -34
- package/dist/hooks/__tests__/prompt-guidance-test-helpers.js.map +0 -1
- package/dist/hooks/__tests__/prompt-guidance-wave-two.test.d.ts +0 -2
- package/dist/hooks/__tests__/prompt-guidance-wave-two.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js +0 -65
- package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js.map +0 -1
- package/dist/hooks/__tests__/prompt-orchestration-boundary.test.d.ts +0 -2
- package/dist/hooks/__tests__/prompt-orchestration-boundary.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/prompt-orchestration-boundary.test.js +0 -38
- package/dist/hooks/__tests__/prompt-orchestration-boundary.test.js.map +0 -1
- package/dist/hooks/__tests__/prompt-refactor-contract.test.d.ts +0 -2
- package/dist/hooks/__tests__/prompt-refactor-contract.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/prompt-refactor-contract.test.js +0 -22
- package/dist/hooks/__tests__/prompt-refactor-contract.test.js.map +0 -1
- package/dist/hooks/__tests__/prompt-team-routing.test.d.ts +0 -2
- package/dist/hooks/__tests__/prompt-team-routing.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/prompt-team-routing.test.js +0 -49
- package/dist/hooks/__tests__/prompt-team-routing.test.js.map +0 -1
- package/dist/hooks/__tests__/session.test.d.ts +0 -2
- package/dist/hooks/__tests__/session.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/session.test.js +0 -322
- package/dist/hooks/__tests__/session.test.js.map +0 -1
- package/dist/hooks/__tests__/skill-guidance-contract.test.d.ts +0 -2
- package/dist/hooks/__tests__/skill-guidance-contract.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/skill-guidance-contract.test.js +0 -29
- package/dist/hooks/__tests__/skill-guidance-contract.test.js.map +0 -1
- package/dist/hooks/__tests__/task-size-detector.test.d.ts +0 -2
- package/dist/hooks/__tests__/task-size-detector.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/task-size-detector.test.js +0 -330
- package/dist/hooks/__tests__/task-size-detector.test.js.map +0 -1
- package/dist/hooks/__tests__/team-runtime-gating-docs-contract.test.d.ts +0 -2
- package/dist/hooks/__tests__/team-runtime-gating-docs-contract.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/team-runtime-gating-docs-contract.test.js +0 -28
- package/dist/hooks/__tests__/team-runtime-gating-docs-contract.test.js.map +0 -1
- package/dist/hooks/__tests__/tmux-hook-engine-types-sync.test.d.ts +0 -2
- package/dist/hooks/__tests__/tmux-hook-engine-types-sync.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/tmux-hook-engine-types-sync.test.js +0 -24
- package/dist/hooks/__tests__/tmux-hook-engine-types-sync.test.js.map +0 -1
- package/dist/hooks/__tests__/tmux-hook-engine.test.d.ts +0 -2
- package/dist/hooks/__tests__/tmux-hook-engine.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/tmux-hook-engine.test.js +0 -403
- package/dist/hooks/__tests__/tmux-hook-engine.test.js.map +0 -1
- package/dist/hooks/__tests__/triage-config.test.d.ts +0 -2
- package/dist/hooks/__tests__/triage-config.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/triage-config.test.js +0 -211
- package/dist/hooks/__tests__/triage-config.test.js.map +0 -1
- package/dist/hooks/__tests__/triage-heuristic.test.d.ts +0 -2
- package/dist/hooks/__tests__/triage-heuristic.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/triage-heuristic.test.js +0 -285
- package/dist/hooks/__tests__/triage-heuristic.test.js.map +0 -1
- package/dist/hooks/__tests__/triage-state.test.d.ts +0 -2
- package/dist/hooks/__tests__/triage-state.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/triage-state.test.js +0 -426
- package/dist/hooks/__tests__/triage-state.test.js.map +0 -1
- package/dist/hooks/__tests__/visual-ralph-skill.test.d.ts +0 -2
- package/dist/hooks/__tests__/visual-ralph-skill.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/visual-ralph-skill.test.js +0 -44
- package/dist/hooks/__tests__/visual-ralph-skill.test.js.map +0 -1
- package/dist/hooks/__tests__/visual-verdict-loop.test.d.ts +0 -2
- package/dist/hooks/__tests__/visual-verdict-loop.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/visual-verdict-loop.test.js +0 -35
- package/dist/hooks/__tests__/visual-verdict-loop.test.js.map +0 -1
- package/dist/hooks/__tests__/wiki-docs-contract.test.d.ts +0 -2
- package/dist/hooks/__tests__/wiki-docs-contract.test.d.ts.map +0 -1
- package/dist/hooks/__tests__/wiki-docs-contract.test.js +0 -34
- package/dist/hooks/__tests__/wiki-docs-contract.test.js.map +0 -1
- package/dist/hooks/code-simplifier/__tests__/index.test.d.ts +0 -2
- package/dist/hooks/code-simplifier/__tests__/index.test.d.ts.map +0 -1
- package/dist/hooks/code-simplifier/__tests__/index.test.js +0 -187
- package/dist/hooks/code-simplifier/__tests__/index.test.js.map +0 -1
- package/dist/hooks/extensibility/__tests__/dispatcher.test.d.ts +0 -2
- package/dist/hooks/extensibility/__tests__/dispatcher.test.d.ts.map +0 -1
- package/dist/hooks/extensibility/__tests__/dispatcher.test.js +0 -242
- package/dist/hooks/extensibility/__tests__/dispatcher.test.js.map +0 -1
- package/dist/hooks/extensibility/__tests__/events.test.d.ts +0 -2
- package/dist/hooks/extensibility/__tests__/events.test.d.ts.map +0 -1
- package/dist/hooks/extensibility/__tests__/events.test.js +0 -125
- package/dist/hooks/extensibility/__tests__/events.test.js.map +0 -1
- package/dist/hooks/extensibility/__tests__/example-hook-plugins.test.d.ts +0 -2
- package/dist/hooks/extensibility/__tests__/example-hook-plugins.test.d.ts.map +0 -1
- package/dist/hooks/extensibility/__tests__/example-hook-plugins.test.js +0 -153
- package/dist/hooks/extensibility/__tests__/example-hook-plugins.test.js.map +0 -1
- package/dist/hooks/extensibility/__tests__/loader.test.d.ts +0 -2
- package/dist/hooks/extensibility/__tests__/loader.test.d.ts.map +0 -1
- package/dist/hooks/extensibility/__tests__/loader.test.js +0 -254
- package/dist/hooks/extensibility/__tests__/loader.test.js.map +0 -1
- package/dist/hooks/extensibility/__tests__/logging.test.d.ts +0 -2
- package/dist/hooks/extensibility/__tests__/logging.test.d.ts.map +0 -1
- package/dist/hooks/extensibility/__tests__/logging.test.js +0 -74
- package/dist/hooks/extensibility/__tests__/logging.test.js.map +0 -1
- package/dist/hooks/extensibility/__tests__/plugin-runner.test.d.ts +0 -2
- package/dist/hooks/extensibility/__tests__/plugin-runner.test.d.ts.map +0 -1
- package/dist/hooks/extensibility/__tests__/plugin-runner.test.js +0 -202
- package/dist/hooks/extensibility/__tests__/plugin-runner.test.js.map +0 -1
- package/dist/hooks/extensibility/__tests__/runtime.test.d.ts +0 -2
- package/dist/hooks/extensibility/__tests__/runtime.test.d.ts.map +0 -1
- package/dist/hooks/extensibility/__tests__/runtime.test.js +0 -198
- package/dist/hooks/extensibility/__tests__/runtime.test.js.map +0 -1
- package/dist/hooks/extensibility/__tests__/sdk-public-surface.test.d.ts +0 -2
- package/dist/hooks/extensibility/__tests__/sdk-public-surface.test.d.ts.map +0 -1
- package/dist/hooks/extensibility/__tests__/sdk-public-surface.test.js +0 -32
- package/dist/hooks/extensibility/__tests__/sdk-public-surface.test.js.map +0 -1
- package/dist/hooks/extensibility/__tests__/sdk.test.d.ts +0 -2
- package/dist/hooks/extensibility/__tests__/sdk.test.d.ts.map +0 -1
- package/dist/hooks/extensibility/__tests__/sdk.test.js +0 -479
- package/dist/hooks/extensibility/__tests__/sdk.test.js.map +0 -1
- package/dist/hud/__tests__/authority.test.d.ts +0 -2
- package/dist/hud/__tests__/authority.test.d.ts.map +0 -1
- package/dist/hud/__tests__/authority.test.js +0 -56
- package/dist/hud/__tests__/authority.test.js.map +0 -1
- package/dist/hud/__tests__/colors.test.d.ts +0 -2
- package/dist/hud/__tests__/colors.test.d.ts.map +0 -1
- package/dist/hud/__tests__/colors.test.js +0 -92
- package/dist/hud/__tests__/colors.test.js.map +0 -1
- package/dist/hud/__tests__/hud-tmux-injection.test.d.ts +0 -10
- package/dist/hud/__tests__/hud-tmux-injection.test.d.ts.map +0 -1
- package/dist/hud/__tests__/hud-tmux-injection.test.js +0 -150
- package/dist/hud/__tests__/hud-tmux-injection.test.js.map +0 -1
- package/dist/hud/__tests__/index.test.d.ts +0 -2
- package/dist/hud/__tests__/index.test.d.ts.map +0 -1
- package/dist/hud/__tests__/index.test.js +0 -180
- package/dist/hud/__tests__/index.test.js.map +0 -1
- package/dist/hud/__tests__/reconcile.test.d.ts +0 -2
- package/dist/hud/__tests__/reconcile.test.d.ts.map +0 -1
- package/dist/hud/__tests__/reconcile.test.js +0 -125
- package/dist/hud/__tests__/reconcile.test.js.map +0 -1
- package/dist/hud/__tests__/render.test.d.ts +0 -2
- package/dist/hud/__tests__/render.test.d.ts.map +0 -1
- package/dist/hud/__tests__/render.test.js +0 -573
- package/dist/hud/__tests__/render.test.js.map +0 -1
- package/dist/hud/__tests__/state.test.d.ts +0 -2
- package/dist/hud/__tests__/state.test.d.ts.map +0 -1
- package/dist/hud/__tests__/state.test.js +0 -618
- package/dist/hud/__tests__/state.test.js.map +0 -1
- package/dist/hud/__tests__/types.test.d.ts +0 -2
- package/dist/hud/__tests__/types.test.d.ts.map +0 -1
- package/dist/hud/__tests__/types.test.js +0 -79
- package/dist/hud/__tests__/types.test.js.map +0 -1
- package/dist/hud/__tests__/watch.test.d.ts +0 -2
- package/dist/hud/__tests__/watch.test.d.ts.map +0 -1
- package/dist/hud/__tests__/watch.test.js +0 -63
- package/dist/hud/__tests__/watch.test.js.map +0 -1
- package/dist/mcp/__tests__/bootstrap.test.d.ts +0 -2
- package/dist/mcp/__tests__/bootstrap.test.d.ts.map +0 -1
- package/dist/mcp/__tests__/bootstrap.test.js +0 -207
- package/dist/mcp/__tests__/bootstrap.test.js.map +0 -1
- package/dist/mcp/__tests__/code-intel-server.test.d.ts +0 -2
- package/dist/mcp/__tests__/code-intel-server.test.d.ts.map +0 -1
- package/dist/mcp/__tests__/code-intel-server.test.js +0 -70
- package/dist/mcp/__tests__/code-intel-server.test.js.map +0 -1
- package/dist/mcp/__tests__/memory-server.test.d.ts +0 -2
- package/dist/mcp/__tests__/memory-server.test.d.ts.map +0 -1
- package/dist/mcp/__tests__/memory-server.test.js +0 -36
- package/dist/mcp/__tests__/memory-server.test.js.map +0 -1
- package/dist/mcp/__tests__/memory-validation.test.d.ts +0 -2
- package/dist/mcp/__tests__/memory-validation.test.d.ts.map +0 -1
- package/dist/mcp/__tests__/memory-validation.test.js +0 -29
- package/dist/mcp/__tests__/memory-validation.test.js.map +0 -1
- package/dist/mcp/__tests__/path-traversal.test.d.ts +0 -2
- package/dist/mcp/__tests__/path-traversal.test.d.ts.map +0 -1
- package/dist/mcp/__tests__/path-traversal.test.js +0 -83
- package/dist/mcp/__tests__/path-traversal.test.js.map +0 -1
- package/dist/mcp/__tests__/server-lifecycle.test.d.ts +0 -2
- package/dist/mcp/__tests__/server-lifecycle.test.d.ts.map +0 -1
- package/dist/mcp/__tests__/server-lifecycle.test.js +0 -260
- package/dist/mcp/__tests__/server-lifecycle.test.js.map +0 -1
- package/dist/mcp/__tests__/state-paths.test.d.ts +0 -2
- package/dist/mcp/__tests__/state-paths.test.d.ts.map +0 -1
- package/dist/mcp/__tests__/state-paths.test.js +0 -209
- package/dist/mcp/__tests__/state-paths.test.js.map +0 -1
- package/dist/mcp/__tests__/state-server-ralph-phase.test.d.ts +0 -2
- package/dist/mcp/__tests__/state-server-ralph-phase.test.d.ts.map +0 -1
- package/dist/mcp/__tests__/state-server-ralph-phase.test.js +0 -109
- package/dist/mcp/__tests__/state-server-ralph-phase.test.js.map +0 -1
- package/dist/mcp/__tests__/state-server-schema.test.d.ts +0 -2
- package/dist/mcp/__tests__/state-server-schema.test.d.ts.map +0 -1
- package/dist/mcp/__tests__/state-server-schema.test.js +0 -29
- package/dist/mcp/__tests__/state-server-schema.test.js.map +0 -1
- package/dist/mcp/__tests__/state-server-team-tools.test.d.ts +0 -2
- package/dist/mcp/__tests__/state-server-team-tools.test.d.ts.map +0 -1
- package/dist/mcp/__tests__/state-server-team-tools.test.js +0 -35
- package/dist/mcp/__tests__/state-server-team-tools.test.js.map +0 -1
- package/dist/mcp/__tests__/state-server.test.d.ts +0 -2
- package/dist/mcp/__tests__/state-server.test.d.ts.map +0 -1
- package/dist/mcp/__tests__/state-server.test.js +0 -965
- package/dist/mcp/__tests__/state-server.test.js.map +0 -1
- package/dist/mcp/__tests__/trace-server.test.d.ts +0 -2
- package/dist/mcp/__tests__/trace-server.test.d.ts.map +0 -1
- package/dist/mcp/__tests__/trace-server.test.js +0 -119
- package/dist/mcp/__tests__/trace-server.test.js.map +0 -1
- package/dist/mcp/__tests__/wiki-server.test.d.ts +0 -2
- package/dist/mcp/__tests__/wiki-server.test.d.ts.map +0 -1
- package/dist/mcp/__tests__/wiki-server.test.js +0 -30
- package/dist/mcp/__tests__/wiki-server.test.js.map +0 -1
- package/dist/modes/__tests__/base-autoresearch-contract.test.d.ts +0 -2
- package/dist/modes/__tests__/base-autoresearch-contract.test.d.ts.map +0 -1
- package/dist/modes/__tests__/base-autoresearch-contract.test.js +0 -123
- package/dist/modes/__tests__/base-autoresearch-contract.test.js.map +0 -1
- package/dist/modes/__tests__/base-multi-state-compat.test.d.ts +0 -2
- package/dist/modes/__tests__/base-multi-state-compat.test.d.ts.map +0 -1
- package/dist/modes/__tests__/base-multi-state-compat.test.js +0 -38
- package/dist/modes/__tests__/base-multi-state-compat.test.js.map +0 -1
- package/dist/modes/__tests__/base-ralph-contract.test.d.ts +0 -2
- package/dist/modes/__tests__/base-ralph-contract.test.d.ts.map +0 -1
- package/dist/modes/__tests__/base-ralph-contract.test.js +0 -64
- package/dist/modes/__tests__/base-ralph-contract.test.js.map +0 -1
- package/dist/modes/__tests__/base-session-scope.test.d.ts +0 -2
- package/dist/modes/__tests__/base-session-scope.test.d.ts.map +0 -1
- package/dist/modes/__tests__/base-session-scope.test.js +0 -98
- package/dist/modes/__tests__/base-session-scope.test.js.map +0 -1
- package/dist/modes/__tests__/base-tmux-pane.test.d.ts +0 -2
- package/dist/modes/__tests__/base-tmux-pane.test.d.ts.map +0 -1
- package/dist/modes/__tests__/base-tmux-pane.test.js +0 -39
- package/dist/modes/__tests__/base-tmux-pane.test.js.map +0 -1
- package/dist/notifications/__tests__/config.test.d.ts +0 -2
- package/dist/notifications/__tests__/config.test.d.ts.map +0 -1
- package/dist/notifications/__tests__/config.test.js +0 -269
- package/dist/notifications/__tests__/config.test.js.map +0 -1
- package/dist/notifications/__tests__/custom-alias-enablement.test.d.ts +0 -2
- package/dist/notifications/__tests__/custom-alias-enablement.test.d.ts.map +0 -1
- package/dist/notifications/__tests__/custom-alias-enablement.test.js +0 -84
- package/dist/notifications/__tests__/custom-alias-enablement.test.js.map +0 -1
- package/dist/notifications/__tests__/dispatch-cooldown.test.d.ts +0 -5
- package/dist/notifications/__tests__/dispatch-cooldown.test.d.ts.map +0 -1
- package/dist/notifications/__tests__/dispatch-cooldown.test.js +0 -100
- package/dist/notifications/__tests__/dispatch-cooldown.test.js.map +0 -1
- package/dist/notifications/__tests__/dispatcher.test.d.ts +0 -2
- package/dist/notifications/__tests__/dispatcher.test.d.ts.map +0 -1
- package/dist/notifications/__tests__/dispatcher.test.js +0 -202
- package/dist/notifications/__tests__/dispatcher.test.js.map +0 -1
- package/dist/notifications/__tests__/formatter.test.d.ts +0 -2
- package/dist/notifications/__tests__/formatter.test.d.ts.map +0 -1
- package/dist/notifications/__tests__/formatter.test.js +0 -270
- package/dist/notifications/__tests__/formatter.test.js.map +0 -1
- package/dist/notifications/__tests__/hook-config.test.d.ts +0 -5
- package/dist/notifications/__tests__/hook-config.test.d.ts.map +0 -1
- package/dist/notifications/__tests__/hook-config.test.js +0 -139
- package/dist/notifications/__tests__/hook-config.test.js.map +0 -1
- package/dist/notifications/__tests__/idle-cooldown.test.d.ts +0 -5
- package/dist/notifications/__tests__/idle-cooldown.test.d.ts.map +0 -1
- package/dist/notifications/__tests__/idle-cooldown.test.js +0 -209
- package/dist/notifications/__tests__/idle-cooldown.test.js.map +0 -1
- package/dist/notifications/__tests__/index.test.d.ts +0 -2
- package/dist/notifications/__tests__/index.test.d.ts.map +0 -1
- package/dist/notifications/__tests__/index.test.js +0 -188
- package/dist/notifications/__tests__/index.test.js.map +0 -1
- package/dist/notifications/__tests__/lifecycle-dedupe.test.d.ts +0 -2
- package/dist/notifications/__tests__/lifecycle-dedupe.test.d.ts.map +0 -1
- package/dist/notifications/__tests__/lifecycle-dedupe.test.js +0 -86
- package/dist/notifications/__tests__/lifecycle-dedupe.test.js.map +0 -1
- package/dist/notifications/__tests__/notifier.test.d.ts +0 -2
- package/dist/notifications/__tests__/notifier.test.d.ts.map +0 -1
- package/dist/notifications/__tests__/notifier.test.js +0 -239
- package/dist/notifications/__tests__/notifier.test.js.map +0 -1
- package/dist/notifications/__tests__/profiles.test.d.ts +0 -2
- package/dist/notifications/__tests__/profiles.test.d.ts.map +0 -1
- package/dist/notifications/__tests__/profiles.test.js +0 -404
- package/dist/notifications/__tests__/profiles.test.js.map +0 -1
- package/dist/notifications/__tests__/reply-config.test.d.ts +0 -2
- package/dist/notifications/__tests__/reply-config.test.d.ts.map +0 -1
- package/dist/notifications/__tests__/reply-config.test.js +0 -79
- package/dist/notifications/__tests__/reply-config.test.js.map +0 -1
- package/dist/notifications/__tests__/reply-listener.test.d.ts +0 -2
- package/dist/notifications/__tests__/reply-listener.test.d.ts.map +0 -1
- package/dist/notifications/__tests__/reply-listener.test.js +0 -723
- package/dist/notifications/__tests__/reply-listener.test.js.map +0 -1
- package/dist/notifications/__tests__/session-idle-tail-dedupe.test.d.ts +0 -2
- package/dist/notifications/__tests__/session-idle-tail-dedupe.test.d.ts.map +0 -1
- package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js +0 -93
- package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js.map +0 -1
- package/dist/notifications/__tests__/session-registry.test.d.ts +0 -2
- package/dist/notifications/__tests__/session-registry.test.d.ts.map +0 -1
- package/dist/notifications/__tests__/session-registry.test.js +0 -234
- package/dist/notifications/__tests__/session-registry.test.js.map +0 -1
- package/dist/notifications/__tests__/session-status.test.d.ts +0 -2
- package/dist/notifications/__tests__/session-status.test.d.ts.map +0 -1
- package/dist/notifications/__tests__/session-status.test.js +0 -249
- package/dist/notifications/__tests__/session-status.test.js.map +0 -1
- package/dist/notifications/__tests__/temp-mode.test.d.ts +0 -2
- package/dist/notifications/__tests__/temp-mode.test.d.ts.map +0 -1
- package/dist/notifications/__tests__/temp-mode.test.js +0 -172
- package/dist/notifications/__tests__/temp-mode.test.js.map +0 -1
- package/dist/notifications/__tests__/template-engine.test.d.ts +0 -5
- package/dist/notifications/__tests__/template-engine.test.d.ts.map +0 -1
- package/dist/notifications/__tests__/template-engine.test.js +0 -158
- package/dist/notifications/__tests__/template-engine.test.js.map +0 -1
- package/dist/notifications/__tests__/tmux-detector.test.d.ts +0 -2
- package/dist/notifications/__tests__/tmux-detector.test.d.ts.map +0 -1
- package/dist/notifications/__tests__/tmux-detector.test.js +0 -208
- package/dist/notifications/__tests__/tmux-detector.test.js.map +0 -1
- package/dist/notifications/__tests__/tmux.test.d.ts +0 -2
- package/dist/notifications/__tests__/tmux.test.d.ts.map +0 -1
- package/dist/notifications/__tests__/tmux.test.js +0 -285
- package/dist/notifications/__tests__/tmux.test.js.map +0 -1
- package/dist/notifications/__tests__/verbosity.test.d.ts +0 -2
- package/dist/notifications/__tests__/verbosity.test.d.ts.map +0 -1
- package/dist/notifications/__tests__/verbosity.test.js +0 -237
- package/dist/notifications/__tests__/verbosity.test.js.map +0 -1
- package/dist/openclaw/__tests__/config.test.d.ts +0 -6
- package/dist/openclaw/__tests__/config.test.d.ts.map +0 -1
- package/dist/openclaw/__tests__/config.test.js +0 -344
- package/dist/openclaw/__tests__/config.test.js.map +0 -1
- package/dist/openclaw/__tests__/dispatcher.test.d.ts +0 -5
- package/dist/openclaw/__tests__/dispatcher.test.d.ts.map +0 -1
- package/dist/openclaw/__tests__/dispatcher.test.js +0 -169
- package/dist/openclaw/__tests__/dispatcher.test.js.map +0 -1
- package/dist/openclaw/__tests__/index.test.d.ts +0 -6
- package/dist/openclaw/__tests__/index.test.d.ts.map +0 -1
- package/dist/openclaw/__tests__/index.test.js +0 -382
- package/dist/openclaw/__tests__/index.test.js.map +0 -1
- package/dist/pipeline/__tests__/orchestrator.test.d.ts +0 -2
- package/dist/pipeline/__tests__/orchestrator.test.d.ts.map +0 -1
- package/dist/pipeline/__tests__/orchestrator.test.js +0 -505
- package/dist/pipeline/__tests__/orchestrator.test.js.map +0 -1
- package/dist/pipeline/__tests__/stages.test.d.ts +0 -2
- package/dist/pipeline/__tests__/stages.test.d.ts.map +0 -1
- package/dist/pipeline/__tests__/stages.test.js +0 -754
- package/dist/pipeline/__tests__/stages.test.js.map +0 -1
- package/dist/pipeline/stages/ralph-verify.d.ts +0 -53
- package/dist/pipeline/stages/ralph-verify.d.ts.map +0 -1
- package/dist/pipeline/stages/ralph-verify.js.map +0 -1
- package/dist/pipeline/stages/ralplan.d.ts +0 -25
- package/dist/pipeline/stages/ralplan.d.ts.map +0 -1
- package/dist/pipeline/stages/ralplan.js.map +0 -1
- package/dist/planning/__tests__/artifacts.test.d.ts +0 -2
- package/dist/planning/__tests__/artifacts.test.d.ts.map +0 -1
- package/dist/planning/__tests__/artifacts.test.js +0 -544
- package/dist/planning/__tests__/artifacts.test.js.map +0 -1
- package/dist/question/__tests__/client.test.d.ts +0 -2
- package/dist/question/__tests__/client.test.d.ts.map +0 -1
- package/dist/question/__tests__/client.test.js +0 -90
- package/dist/question/__tests__/client.test.js.map +0 -1
- package/dist/question/__tests__/deep-interview.test.d.ts +0 -2
- package/dist/question/__tests__/deep-interview.test.d.ts.map +0 -1
- package/dist/question/__tests__/deep-interview.test.js +0 -209
- package/dist/question/__tests__/deep-interview.test.js.map +0 -1
- package/dist/question/__tests__/policy.test.d.ts +0 -2
- package/dist/question/__tests__/policy.test.d.ts.map +0 -1
- package/dist/question/__tests__/policy.test.js +0 -107
- package/dist/question/__tests__/policy.test.js.map +0 -1
- package/dist/question/__tests__/renderer.test.d.ts +0 -2
- package/dist/question/__tests__/renderer.test.d.ts.map +0 -1
- package/dist/question/__tests__/renderer.test.js +0 -707
- package/dist/question/__tests__/renderer.test.js.map +0 -1
- package/dist/question/__tests__/state.test.d.ts +0 -2
- package/dist/question/__tests__/state.test.d.ts.map +0 -1
- package/dist/question/__tests__/state.test.js +0 -102
- package/dist/question/__tests__/state.test.js.map +0 -1
- package/dist/question/__tests__/types.test.d.ts +0 -2
- package/dist/question/__tests__/types.test.d.ts.map +0 -1
- package/dist/question/__tests__/types.test.js +0 -65
- package/dist/question/__tests__/types.test.js.map +0 -1
- package/dist/question/__tests__/ui.test.d.ts +0 -2
- package/dist/question/__tests__/ui.test.d.ts.map +0 -1
- package/dist/question/__tests__/ui.test.js +0 -446
- package/dist/question/__tests__/ui.test.js.map +0 -1
- package/dist/ralph/__tests__/persistence.test.d.ts +0 -2
- package/dist/ralph/__tests__/persistence.test.d.ts.map +0 -1
- package/dist/ralph/__tests__/persistence.test.js +0 -116
- package/dist/ralph/__tests__/persistence.test.js.map +0 -1
- package/dist/ralph/contract.d.ts +0 -17
- package/dist/ralph/persistence.js.map +0 -1
- package/dist/ralplan/__tests__/runtime.test.d.ts +0 -2
- package/dist/ralplan/__tests__/runtime.test.d.ts.map +0 -1
- package/dist/ralplan/__tests__/runtime.test.js +0 -165
- package/dist/ralplan/__tests__/runtime.test.js.map +0 -1
- package/dist/ralplan/runtime.d.ts +0 -52
- package/dist/ralplan/runtime.d.ts.map +0 -1
- package/dist/ralplan/runtime.js.map +0 -1
- package/dist/runtime/__tests__/bridge.test.d.ts +0 -2
- package/dist/runtime/__tests__/bridge.test.d.ts.map +0 -1
- package/dist/runtime/__tests__/bridge.test.js +0 -194
- package/dist/runtime/__tests__/bridge.test.js.map +0 -1
- package/dist/runtime/__tests__/run-loop.test.d.ts +0 -2
- package/dist/runtime/__tests__/run-loop.test.d.ts.map +0 -1
- package/dist/runtime/__tests__/run-loop.test.js +0 -35
- package/dist/runtime/__tests__/run-loop.test.js.map +0 -1
- package/dist/runtime/__tests__/run-outcome.test.d.ts +0 -2
- package/dist/runtime/__tests__/run-outcome.test.d.ts.map +0 -1
- package/dist/runtime/__tests__/run-outcome.test.js +0 -102
- package/dist/runtime/__tests__/run-outcome.test.js.map +0 -1
- package/dist/runtime/__tests__/run-state.test.d.ts +0 -2
- package/dist/runtime/__tests__/run-state.test.d.ts.map +0 -1
- package/dist/runtime/__tests__/run-state.test.js +0 -37
- package/dist/runtime/__tests__/run-state.test.js.map +0 -1
- package/dist/scripts/__tests__/codex-native-hook.test.d.ts +0 -2
- package/dist/scripts/__tests__/codex-native-hook.test.d.ts.map +0 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +0 -6788
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +0 -1
- package/dist/scripts/__tests__/generate-release-body.test.d.ts +0 -2
- package/dist/scripts/__tests__/generate-release-body.test.d.ts.map +0 -1
- package/dist/scripts/__tests__/generate-release-body.test.js +0 -233
- package/dist/scripts/__tests__/generate-release-body.test.js.map +0 -1
- package/dist/scripts/__tests__/hook-derived-watcher.test.d.ts +0 -2
- package/dist/scripts/__tests__/hook-derived-watcher.test.d.ts.map +0 -1
- package/dist/scripts/__tests__/hook-derived-watcher.test.js +0 -195
- package/dist/scripts/__tests__/hook-derived-watcher.test.js.map +0 -1
- package/dist/scripts/__tests__/postinstall.test.d.ts +0 -2
- package/dist/scripts/__tests__/postinstall.test.d.ts.map +0 -1
- package/dist/scripts/__tests__/postinstall.test.js +0 -92
- package/dist/scripts/__tests__/postinstall.test.js.map +0 -1
- package/dist/scripts/__tests__/prompt-inventory.test.d.ts +0 -2
- package/dist/scripts/__tests__/prompt-inventory.test.d.ts.map +0 -1
- package/dist/scripts/__tests__/prompt-inventory.test.js +0 -56
- package/dist/scripts/__tests__/prompt-inventory.test.js.map +0 -1
- package/dist/scripts/__tests__/run-test-files.test.d.ts +0 -2
- package/dist/scripts/__tests__/run-test-files.test.d.ts.map +0 -1
- package/dist/scripts/__tests__/run-test-files.test.js +0 -62
- package/dist/scripts/__tests__/run-test-files.test.js.map +0 -1
- package/dist/scripts/__tests__/smoke-packed-install.test.d.ts +0 -2
- package/dist/scripts/__tests__/smoke-packed-install.test.d.ts.map +0 -1
- package/dist/scripts/__tests__/smoke-packed-install.test.js +0 -135
- package/dist/scripts/__tests__/smoke-packed-install.test.js.map +0 -1
- package/dist/scripts/__tests__/test-reply-listener-live.test.d.ts +0 -2
- package/dist/scripts/__tests__/test-reply-listener-live.test.d.ts.map +0 -1
- package/dist/scripts/__tests__/test-reply-listener-live.test.js +0 -82
- package/dist/scripts/__tests__/test-reply-listener-live.test.js.map +0 -1
- package/dist/scripts/__tests__/verify-native-agents.test.d.ts +0 -2
- package/dist/scripts/__tests__/verify-native-agents.test.d.ts.map +0 -1
- package/dist/scripts/__tests__/verify-native-agents.test.js +0 -166
- package/dist/scripts/__tests__/verify-native-agents.test.js.map +0 -1
- package/dist/scripts/eval/eval-candidate-handoff.d.ts +0 -2
- package/dist/scripts/eval/eval-candidate-handoff.d.ts.map +0 -1
- package/dist/scripts/eval/eval-candidate-handoff.js +0 -11
- package/dist/scripts/eval/eval-candidate-handoff.js.map +0 -1
- package/dist/scripts/eval/eval-cli-discoverability.d.ts +0 -3
- package/dist/scripts/eval/eval-cli-discoverability.d.ts.map +0 -1
- package/dist/scripts/eval/eval-cli-discoverability.js +0 -37
- package/dist/scripts/eval/eval-cli-discoverability.js.map +0 -1
- package/dist/scripts/eval/eval-fresh-run-tagging.d.ts +0 -2
- package/dist/scripts/eval/eval-fresh-run-tagging.d.ts.map +0 -1
- package/dist/scripts/eval/eval-fresh-run-tagging.js +0 -11
- package/dist/scripts/eval/eval-fresh-run-tagging.js.map +0 -1
- package/dist/scripts/eval/eval-help-consistency.d.ts +0 -2
- package/dist/scripts/eval/eval-help-consistency.d.ts.map +0 -1
- package/dist/scripts/eval/eval-help-consistency.js +0 -12
- package/dist/scripts/eval/eval-help-consistency.js.map +0 -1
- package/dist/scripts/eval/eval-in-action-cat-shellout-demo.d.ts +0 -2
- package/dist/scripts/eval/eval-in-action-cat-shellout-demo.d.ts.map +0 -1
- package/dist/scripts/eval/eval-in-action-cat-shellout-demo.js +0 -31
- package/dist/scripts/eval/eval-in-action-cat-shellout-demo.js.map +0 -1
- package/dist/scripts/eval/eval-parity-smoke.d.ts +0 -2
- package/dist/scripts/eval/eval-parity-smoke.d.ts.map +0 -1
- package/dist/scripts/eval/eval-parity-smoke.js +0 -23
- package/dist/scripts/eval/eval-parity-smoke.js.map +0 -1
- package/dist/scripts/eval/eval-parity-sweep.d.ts +0 -2
- package/dist/scripts/eval/eval-parity-sweep.d.ts.map +0 -1
- package/dist/scripts/eval/eval-parity-sweep.js +0 -29
- package/dist/scripts/eval/eval-parity-sweep.js.map +0 -1
- package/dist/scripts/eval/eval-resume-dirty-guard.d.ts +0 -2
- package/dist/scripts/eval/eval-resume-dirty-guard.d.ts.map +0 -1
- package/dist/scripts/eval/eval-resume-dirty-guard.js +0 -11
- package/dist/scripts/eval/eval-resume-dirty-guard.js.map +0 -1
- package/dist/scripts/eval/eval-security-path-traversal.d.ts +0 -3
- package/dist/scripts/eval/eval-security-path-traversal.d.ts.map +0 -1
- package/dist/scripts/eval/eval-security-path-traversal.js +0 -35
- package/dist/scripts/eval/eval-security-path-traversal.js.map +0 -1
- package/dist/scripts/notify-hook/__tests__/operational-events.test.d.ts +0 -2
- package/dist/scripts/notify-hook/__tests__/operational-events.test.d.ts.map +0 -1
- package/dist/scripts/notify-hook/__tests__/operational-events.test.js +0 -24
- package/dist/scripts/notify-hook/__tests__/operational-events.test.js.map +0 -1
- package/dist/scripts/notify-hook/__tests__/team-worker-posttooluse.test.d.ts +0 -2
- package/dist/scripts/notify-hook/__tests__/team-worker-posttooluse.test.d.ts.map +0 -1
- package/dist/scripts/notify-hook/__tests__/team-worker-posttooluse.test.js +0 -153
- package/dist/scripts/notify-hook/__tests__/team-worker-posttooluse.test.js.map +0 -1
- package/dist/scripts/notify-hook/ralph-session-resume.d.ts +0 -22
- package/dist/session-history/__tests__/search.test.d.ts +0 -2
- package/dist/session-history/__tests__/search.test.d.ts.map +0 -1
- package/dist/session-history/__tests__/search.test.js +0 -150
- package/dist/session-history/__tests__/search.test.js.map +0 -1
- package/dist/sidecar/__tests__/boundary.test.d.ts +0 -2
- package/dist/sidecar/__tests__/boundary.test.d.ts.map +0 -1
- package/dist/sidecar/__tests__/boundary.test.js +0 -48
- package/dist/sidecar/__tests__/boundary.test.js.map +0 -1
- package/dist/sidecar/__tests__/collector.test.d.ts +0 -2
- package/dist/sidecar/__tests__/collector.test.d.ts.map +0 -1
- package/dist/sidecar/__tests__/collector.test.js +0 -162
- package/dist/sidecar/__tests__/collector.test.js.map +0 -1
- package/dist/sidecar/__tests__/render.test.d.ts +0 -2
- package/dist/sidecar/__tests__/render.test.d.ts.map +0 -1
- package/dist/sidecar/__tests__/render.test.js +0 -67
- package/dist/sidecar/__tests__/render.test.js.map +0 -1
- package/dist/sidecar/__tests__/tmux.test.d.ts +0 -2
- package/dist/sidecar/__tests__/tmux.test.d.ts.map +0 -1
- package/dist/sidecar/__tests__/tmux.test.js +0 -30
- package/dist/sidecar/__tests__/tmux.test.js.map +0 -1
- package/dist/sidecar/__tests__/watch.test.d.ts +0 -2
- package/dist/sidecar/__tests__/watch.test.d.ts.map +0 -1
- package/dist/sidecar/__tests__/watch.test.js +0 -42
- package/dist/sidecar/__tests__/watch.test.js.map +0 -1
- package/dist/state/__tests__/mode-state-context.test.d.ts +0 -2
- package/dist/state/__tests__/mode-state-context.test.d.ts.map +0 -1
- package/dist/state/__tests__/mode-state-context.test.js +0 -35
- package/dist/state/__tests__/mode-state-context.test.js.map +0 -1
- package/dist/state/__tests__/operations-ralph-phase.test.d.ts +0 -2
- package/dist/state/__tests__/operations-ralph-phase.test.d.ts.map +0 -1
- package/dist/state/__tests__/operations-ralph-phase.test.js +0 -103
- package/dist/state/__tests__/operations-ralph-phase.test.js.map +0 -1
- package/dist/state/__tests__/operations.test.d.ts +0 -2
- package/dist/state/__tests__/operations.test.d.ts.map +0 -1
- package/dist/state/__tests__/operations.test.js +0 -439
- package/dist/state/__tests__/operations.test.js.map +0 -1
- package/dist/state/__tests__/path-traversal.test.d.ts +0 -2
- package/dist/state/__tests__/path-traversal.test.d.ts.map +0 -1
- package/dist/state/__tests__/path-traversal.test.js +0 -49
- package/dist/state/__tests__/path-traversal.test.js.map +0 -1
- package/dist/state/__tests__/skill-active.test.d.ts +0 -2
- package/dist/state/__tests__/skill-active.test.d.ts.map +0 -1
- package/dist/state/__tests__/skill-active.test.js +0 -160
- package/dist/state/__tests__/skill-active.test.js.map +0 -1
- package/dist/state/__tests__/workflow-transition.test.d.ts +0 -2
- package/dist/state/__tests__/workflow-transition.test.d.ts.map +0 -1
- package/dist/state/__tests__/workflow-transition.test.js +0 -77
- package/dist/state/__tests__/workflow-transition.test.js.map +0 -1
- package/dist/subagents/__tests__/tracker.test.d.ts +0 -2
- package/dist/subagents/__tests__/tracker.test.d.ts.map +0 -1
- package/dist/subagents/__tests__/tracker.test.js +0 -47
- package/dist/subagents/__tests__/tracker.test.js.map +0 -1
- package/dist/team/__tests__/allocation-policy.test.d.ts +0 -2
- package/dist/team/__tests__/allocation-policy.test.d.ts.map +0 -1
- package/dist/team/__tests__/allocation-policy.test.js +0 -111
- package/dist/team/__tests__/allocation-policy.test.js.map +0 -1
- package/dist/team/__tests__/api-interop.test.d.ts +0 -2
- package/dist/team/__tests__/api-interop.test.d.ts.map +0 -1
- package/dist/team/__tests__/api-interop.test.js +0 -2262
- package/dist/team/__tests__/api-interop.test.js.map +0 -1
- package/dist/team/__tests__/commit-hygiene.test.d.ts +0 -2
- package/dist/team/__tests__/commit-hygiene.test.d.ts.map +0 -1
- package/dist/team/__tests__/commit-hygiene.test.js +0 -93
- package/dist/team/__tests__/commit-hygiene.test.js.map +0 -1
- package/dist/team/__tests__/cross-rebase-smoke.test.d.ts +0 -2
- package/dist/team/__tests__/cross-rebase-smoke.test.d.ts.map +0 -1
- package/dist/team/__tests__/cross-rebase-smoke.test.js +0 -161
- package/dist/team/__tests__/cross-rebase-smoke.test.js.map +0 -1
- package/dist/team/__tests__/current-task-baseline.test.d.ts +0 -2
- package/dist/team/__tests__/current-task-baseline.test.d.ts.map +0 -1
- package/dist/team/__tests__/current-task-baseline.test.js +0 -87
- package/dist/team/__tests__/current-task-baseline.test.js.map +0 -1
- package/dist/team/__tests__/delegation-policy.test.d.ts +0 -2
- package/dist/team/__tests__/delegation-policy.test.d.ts.map +0 -1
- package/dist/team/__tests__/delegation-policy.test.js +0 -69
- package/dist/team/__tests__/delegation-policy.test.js.map +0 -1
- package/dist/team/__tests__/delivery-e2e-smoke.test.d.ts +0 -2
- package/dist/team/__tests__/delivery-e2e-smoke.test.d.ts.map +0 -1
- package/dist/team/__tests__/delivery-e2e-smoke.test.js +0 -679
- package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +0 -1
- package/dist/team/__tests__/events.test.d.ts +0 -2
- package/dist/team/__tests__/events.test.d.ts.map +0 -1
- package/dist/team/__tests__/events.test.js +0 -313
- package/dist/team/__tests__/events.test.js.map +0 -1
- package/dist/team/__tests__/followup-planner.test.d.ts +0 -2
- package/dist/team/__tests__/followup-planner.test.d.ts.map +0 -1
- package/dist/team/__tests__/followup-planner.test.js +0 -84
- package/dist/team/__tests__/followup-planner.test.js.map +0 -1
- package/dist/team/__tests__/hardening-e2e.test.d.ts +0 -2
- package/dist/team/__tests__/hardening-e2e.test.d.ts.map +0 -1
- package/dist/team/__tests__/hardening-e2e.test.js +0 -98
- package/dist/team/__tests__/hardening-e2e.test.js.map +0 -1
- package/dist/team/__tests__/hook-primary-e2e-contract.test.d.ts +0 -2
- package/dist/team/__tests__/hook-primary-e2e-contract.test.d.ts.map +0 -1
- package/dist/team/__tests__/hook-primary-e2e-contract.test.js +0 -78
- package/dist/team/__tests__/hook-primary-e2e-contract.test.js.map +0 -1
- package/dist/team/__tests__/idle-nudge.test.d.ts +0 -2
- package/dist/team/__tests__/idle-nudge.test.d.ts.map +0 -1
- package/dist/team/__tests__/idle-nudge.test.js +0 -230
- package/dist/team/__tests__/idle-nudge.test.js.map +0 -1
- package/dist/team/__tests__/leader-activity.test.d.ts +0 -2
- package/dist/team/__tests__/leader-activity.test.d.ts.map +0 -1
- package/dist/team/__tests__/leader-activity.test.js +0 -261
- package/dist/team/__tests__/leader-activity.test.js.map +0 -1
- package/dist/team/__tests__/mcp-comm.test.d.ts +0 -2
- package/dist/team/__tests__/mcp-comm.test.d.ts.map +0 -1
- package/dist/team/__tests__/mcp-comm.test.js +0 -289
- package/dist/team/__tests__/mcp-comm.test.js.map +0 -1
- package/dist/team/__tests__/model-contract.test.d.ts +0 -2
- package/dist/team/__tests__/model-contract.test.d.ts.map +0 -1
- package/dist/team/__tests__/model-contract.test.js +0 -171
- package/dist/team/__tests__/model-contract.test.js.map +0 -1
- package/dist/team/__tests__/orchestrator.test.d.ts +0 -2
- package/dist/team/__tests__/orchestrator.test.d.ts.map +0 -1
- package/dist/team/__tests__/orchestrator.test.js +0 -111
- package/dist/team/__tests__/orchestrator.test.js.map +0 -1
- package/dist/team/__tests__/phase-controller.test.d.ts +0 -2
- package/dist/team/__tests__/phase-controller.test.d.ts.map +0 -1
- package/dist/team/__tests__/phase-controller.test.js +0 -50
- package/dist/team/__tests__/phase-controller.test.js.map +0 -1
- package/dist/team/__tests__/rebalance-policy.test.d.ts +0 -2
- package/dist/team/__tests__/rebalance-policy.test.d.ts.map +0 -1
- package/dist/team/__tests__/rebalance-policy.test.js +0 -168
- package/dist/team/__tests__/rebalance-policy.test.js.map +0 -1
- package/dist/team/__tests__/repo-aware-decomposition.test.d.ts +0 -2
- package/dist/team/__tests__/repo-aware-decomposition.test.d.ts.map +0 -1
- package/dist/team/__tests__/repo-aware-decomposition.test.js +0 -136
- package/dist/team/__tests__/repo-aware-decomposition.test.js.map +0 -1
- package/dist/team/__tests__/role-router.test.d.ts +0 -2
- package/dist/team/__tests__/role-router.test.d.ts.map +0 -1
- package/dist/team/__tests__/role-router.test.js +0 -263
- package/dist/team/__tests__/role-router.test.js.map +0 -1
- package/dist/team/__tests__/runtime-cli.test.d.ts +0 -2
- package/dist/team/__tests__/runtime-cli.test.d.ts.map +0 -1
- package/dist/team/__tests__/runtime-cli.test.js +0 -304
- package/dist/team/__tests__/runtime-cli.test.js.map +0 -1
- package/dist/team/__tests__/runtime.test.d.ts +0 -2
- package/dist/team/__tests__/runtime.test.d.ts.map +0 -1
- package/dist/team/__tests__/runtime.test.js +0 -5734
- package/dist/team/__tests__/runtime.test.js.map +0 -1
- package/dist/team/__tests__/scaling.test.d.ts +0 -2
- package/dist/team/__tests__/scaling.test.d.ts.map +0 -1
- package/dist/team/__tests__/scaling.test.js +0 -1005
- package/dist/team/__tests__/scaling.test.js.map +0 -1
- package/dist/team/__tests__/shutdown-fallback.test.d.ts +0 -2
- package/dist/team/__tests__/shutdown-fallback.test.d.ts.map +0 -1
- package/dist/team/__tests__/shutdown-fallback.test.js +0 -125
- package/dist/team/__tests__/shutdown-fallback.test.js.map +0 -1
- package/dist/team/__tests__/state-root.test.d.ts +0 -2
- package/dist/team/__tests__/state-root.test.d.ts.map +0 -1
- package/dist/team/__tests__/state-root.test.js +0 -195
- package/dist/team/__tests__/state-root.test.js.map +0 -1
- package/dist/team/__tests__/state.test.d.ts +0 -2
- package/dist/team/__tests__/state.test.d.ts.map +0 -1
- package/dist/team/__tests__/state.test.js +0 -1859
- package/dist/team/__tests__/state.test.js.map +0 -1
- package/dist/team/__tests__/team-identity.test.d.ts +0 -2
- package/dist/team/__tests__/team-identity.test.d.ts.map +0 -1
- package/dist/team/__tests__/team-identity.test.js +0 -166
- package/dist/team/__tests__/team-identity.test.js.map +0 -1
- package/dist/team/__tests__/team-ops-contract.test.d.ts +0 -2
- package/dist/team/__tests__/team-ops-contract.test.d.ts.map +0 -1
- package/dist/team/__tests__/team-ops-contract.test.js +0 -96
- package/dist/team/__tests__/team-ops-contract.test.js.map +0 -1
- package/dist/team/__tests__/tmux-claude-workers-demo.test.d.ts +0 -2
- package/dist/team/__tests__/tmux-claude-workers-demo.test.d.ts.map +0 -1
- package/dist/team/__tests__/tmux-claude-workers-demo.test.js +0 -191
- package/dist/team/__tests__/tmux-claude-workers-demo.test.js.map +0 -1
- package/dist/team/__tests__/tmux-session.test.d.ts +0 -2
- package/dist/team/__tests__/tmux-session.test.d.ts.map +0 -1
- package/dist/team/__tests__/tmux-session.test.js +0 -3785
- package/dist/team/__tests__/tmux-session.test.js.map +0 -1
- package/dist/team/__tests__/tmux-test-fixture.d.ts +0 -20
- package/dist/team/__tests__/tmux-test-fixture.d.ts.map +0 -1
- package/dist/team/__tests__/tmux-test-fixture.js +0 -152
- package/dist/team/__tests__/tmux-test-fixture.js.map +0 -1
- package/dist/team/__tests__/tmux-test-fixture.test.d.ts +0 -2
- package/dist/team/__tests__/tmux-test-fixture.test.d.ts.map +0 -1
- package/dist/team/__tests__/tmux-test-fixture.test.js +0 -113
- package/dist/team/__tests__/tmux-test-fixture.test.js.map +0 -1
- package/dist/team/__tests__/worker-bootstrap.test.d.ts +0 -2
- package/dist/team/__tests__/worker-bootstrap.test.d.ts.map +0 -1
- package/dist/team/__tests__/worker-bootstrap.test.js +0 -685
- package/dist/team/__tests__/worker-bootstrap.test.js.map +0 -1
- package/dist/team/__tests__/worker-runtime-identity.test.d.ts +0 -2
- package/dist/team/__tests__/worker-runtime-identity.test.d.ts.map +0 -1
- package/dist/team/__tests__/worker-runtime-identity.test.js +0 -250
- package/dist/team/__tests__/worker-runtime-identity.test.js.map +0 -1
- package/dist/team/__tests__/worktree.test.d.ts +0 -2
- package/dist/team/__tests__/worktree.test.d.ts.map +0 -1
- package/dist/team/__tests__/worktree.test.js +0 -317
- package/dist/team/__tests__/worktree.test.js.map +0 -1
- package/dist/utils/__tests__/agents-md.test.d.ts +0 -2
- package/dist/utils/__tests__/agents-md.test.d.ts.map +0 -1
- package/dist/utils/__tests__/agents-md.test.js +0 -52
- package/dist/utils/__tests__/agents-md.test.js.map +0 -1
- package/dist/utils/__tests__/agents-model-table.test.d.ts +0 -2
- package/dist/utils/__tests__/agents-model-table.test.d.ts.map +0 -1
- package/dist/utils/__tests__/agents-model-table.test.js +0 -104
- package/dist/utils/__tests__/agents-model-table.test.js.map +0 -1
- package/dist/utils/__tests__/dep-versions.test.d.ts +0 -2
- package/dist/utils/__tests__/dep-versions.test.d.ts.map +0 -1
- package/dist/utils/__tests__/dep-versions.test.js +0 -46
- package/dist/utils/__tests__/dep-versions.test.js.map +0 -1
- package/dist/utils/__tests__/package.test.d.ts +0 -2
- package/dist/utils/__tests__/package.test.d.ts.map +0 -1
- package/dist/utils/__tests__/package.test.js +0 -21
- package/dist/utils/__tests__/package.test.js.map +0 -1
- package/dist/utils/__tests__/paths.test.d.ts +0 -2
- package/dist/utils/__tests__/paths.test.d.ts.map +0 -1
- package/dist/utils/__tests__/paths.test.js +0 -541
- package/dist/utils/__tests__/paths.test.js.map +0 -1
- package/dist/utils/__tests__/platform-command.test.d.ts +0 -2
- package/dist/utils/__tests__/platform-command.test.d.ts.map +0 -1
- package/dist/utils/__tests__/platform-command.test.js +0 -410
- package/dist/utils/__tests__/platform-command.test.js.map +0 -1
- package/dist/utils/__tests__/repo-deps.test.d.ts +0 -2
- package/dist/utils/__tests__/repo-deps.test.d.ts.map +0 -1
- package/dist/utils/__tests__/repo-deps.test.js +0 -71
- package/dist/utils/__tests__/repo-deps.test.js.map +0 -1
- package/dist/verification/__tests__/ci-rust-gates.test.d.ts +0 -2
- package/dist/verification/__tests__/ci-rust-gates.test.d.ts.map +0 -1
- package/dist/verification/__tests__/ci-rust-gates.test.js +0 -89
- package/dist/verification/__tests__/ci-rust-gates.test.js.map +0 -1
- package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.d.ts +0 -2
- package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.d.ts.map +0 -1
- package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js +0 -54
- package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +0 -1
- package/dist/verification/__tests__/explore-harness-release-workflow.test.d.ts +0 -2
- package/dist/verification/__tests__/explore-harness-release-workflow.test.d.ts.map +0 -1
- package/dist/verification/__tests__/explore-harness-release-workflow.test.js +0 -73
- package/dist/verification/__tests__/explore-harness-release-workflow.test.js.map +0 -1
- package/dist/verification/__tests__/native-release-manifest.test.d.ts +0 -2
- package/dist/verification/__tests__/native-release-manifest.test.d.ts.map +0 -1
- package/dist/verification/__tests__/native-release-manifest.test.js +0 -80
- package/dist/verification/__tests__/native-release-manifest.test.js.map +0 -1
- package/dist/verification/__tests__/pr-check-workflow.test.d.ts +0 -2
- package/dist/verification/__tests__/pr-check-workflow.test.d.ts.map +0 -1
- package/dist/verification/__tests__/pr-check-workflow.test.js +0 -27
- package/dist/verification/__tests__/pr-check-workflow.test.js.map +0 -1
- package/dist/verification/__tests__/ralph-persistence-gate.test.d.ts +0 -2
- package/dist/verification/__tests__/ralph-persistence-gate.test.d.ts.map +0 -1
- package/dist/verification/__tests__/ralph-persistence-gate.test.js +0 -55
- package/dist/verification/__tests__/ralph-persistence-gate.test.js.map +0 -1
- package/dist/verification/__tests__/rust-runtime-thin-adapter-gate.test.d.ts +0 -2
- package/dist/verification/__tests__/rust-runtime-thin-adapter-gate.test.d.ts.map +0 -1
- package/dist/verification/__tests__/rust-runtime-thin-adapter-gate.test.js +0 -32
- package/dist/verification/__tests__/rust-runtime-thin-adapter-gate.test.js.map +0 -1
- package/dist/verification/__tests__/verifier.test.d.ts +0 -2
- package/dist/verification/__tests__/verifier.test.d.ts.map +0 -1
- package/dist/verification/__tests__/verifier.test.js +0 -113
- package/dist/verification/__tests__/verifier.test.js.map +0 -1
- package/dist/visual/__tests__/verdict.test.d.ts +0 -2
- package/dist/visual/__tests__/verdict.test.d.ts.map +0 -1
- package/dist/visual/__tests__/verdict.test.js +0 -81
- package/dist/visual/__tests__/verdict.test.js.map +0 -1
- package/dist/wiki/__tests__/cjk-tokenize.test.d.ts +0 -12
- package/dist/wiki/__tests__/cjk-tokenize.test.d.ts.map +0 -1
- package/dist/wiki/__tests__/cjk-tokenize.test.js +0 -139
- package/dist/wiki/__tests__/cjk-tokenize.test.js.map +0 -1
- package/dist/wiki/__tests__/crlf-parse.test.d.ts +0 -2
- package/dist/wiki/__tests__/crlf-parse.test.d.ts.map +0 -1
- package/dist/wiki/__tests__/crlf-parse.test.js +0 -24
- package/dist/wiki/__tests__/crlf-parse.test.js.map +0 -1
- package/dist/wiki/__tests__/escape-newline.test.d.ts +0 -2
- package/dist/wiki/__tests__/escape-newline.test.d.ts.map +0 -1
- package/dist/wiki/__tests__/escape-newline.test.js +0 -45
- package/dist/wiki/__tests__/escape-newline.test.js.map +0 -1
- package/dist/wiki/__tests__/ingest.test.d.ts +0 -5
- package/dist/wiki/__tests__/ingest.test.d.ts.map +0 -1
- package/dist/wiki/__tests__/ingest.test.js +0 -181
- package/dist/wiki/__tests__/ingest.test.js.map +0 -1
- package/dist/wiki/__tests__/lint.test.d.ts +0 -5
- package/dist/wiki/__tests__/lint.test.d.ts.map +0 -1
- package/dist/wiki/__tests__/lint.test.js +0 -163
- package/dist/wiki/__tests__/lint.test.js.map +0 -1
- package/dist/wiki/__tests__/query.test.d.ts +0 -5
- package/dist/wiki/__tests__/query.test.d.ts.map +0 -1
- package/dist/wiki/__tests__/query.test.js +0 -141
- package/dist/wiki/__tests__/query.test.js.map +0 -1
- package/dist/wiki/__tests__/reserved-file-guard.test.d.ts +0 -2
- package/dist/wiki/__tests__/reserved-file-guard.test.d.ts.map +0 -1
- package/dist/wiki/__tests__/reserved-file-guard.test.js +0 -44
- package/dist/wiki/__tests__/reserved-file-guard.test.js.map +0 -1
- package/dist/wiki/__tests__/session-hooks.test.d.ts +0 -5
- package/dist/wiki/__tests__/session-hooks.test.d.ts.map +0 -1
- package/dist/wiki/__tests__/session-hooks.test.js +0 -36
- package/dist/wiki/__tests__/session-hooks.test.js.map +0 -1
- package/dist/wiki/__tests__/slug-nonascii.test.d.ts +0 -2
- package/dist/wiki/__tests__/slug-nonascii.test.d.ts.map +0 -1
- package/dist/wiki/__tests__/slug-nonascii.test.js +0 -30
- package/dist/wiki/__tests__/slug-nonascii.test.js.map +0 -1
- package/dist/wiki/__tests__/storage.test.d.ts +0 -5
- package/dist/wiki/__tests__/storage.test.d.ts.map +0 -1
- package/dist/wiki/__tests__/storage.test.js +0 -278
- package/dist/wiki/__tests__/storage.test.js.map +0 -1
- package/dist/wiki/__tests__/test-helpers.d.ts +0 -31
- package/dist/wiki/__tests__/test-helpers.d.ts.map +0 -1
- package/dist/wiki/__tests__/test-helpers.js +0 -108
- package/dist/wiki/__tests__/test-helpers.js.map +0 -1
- package/docs/contracts/ralph-cancel-contract.md +0 -23
- package/docs/contracts/ralph-state-contract.md +0 -95
- package/docs/issues/team-ralph-followup-team.md +0 -38
- package/docs/qa/ralph-persistence-gate.md +0 -59
- package/docs/reference/ralph-parity-matrix.md +0 -25
- package/docs/reference/ralph-upstream-baseline.md +0 -34
- package/plugins/roblox-ai-os-creator-skills/skills/ralph/SKILL.md +0 -269
- package/plugins/roblox-ai-os-creator-skills/skills/ralplan/SKILL.md +0 -162
- package/prompts/api-reviewer.md +0 -113
- package/prompts/information-architect.md +0 -226
- package/prompts/performance-reviewer.md +0 -109
- package/prompts/product-analyst.md +0 -304
- package/prompts/product-manager.md +0 -245
- package/prompts/qa-tester.md +0 -124
- package/prompts/quality-reviewer.md +0 -123
- package/prompts/quality-strategist.md +0 -274
- package/prompts/style-reviewer.md +0 -102
- package/prompts/ux-researcher.md +0 -327
- package/skills/frontend-ui-ux/SKILL.md +0 -34
- package/skills/ralph/SKILL.md +0 -269
- package/skills/ralplan/SKILL.md +0 -162
- package/src/scripts/eval/eval-adaptive-sort-optimization.py +0 -24
- package/src/scripts/eval/eval-candidate-handoff.ts +0 -8
- package/src/scripts/eval/eval-cli-discoverability.ts +0 -40
- package/src/scripts/eval/eval-fresh-run-tagging.ts +0 -8
- package/src/scripts/eval/eval-help-consistency.ts +0 -11
- package/src/scripts/eval/eval-in-action-cat-shellout-demo.ts +0 -31
- package/src/scripts/eval/eval-ml-kaggle-model-optimization.py +0 -29
- package/src/scripts/eval/eval-noisy-bayesopt-highdim.py +0 -44
- package/src/scripts/eval/eval-noisy-latent-subspace-discovery.py +0 -44
- package/src/scripts/eval/eval-parity-smoke.ts +0 -20
- package/src/scripts/eval/eval-parity-sweep.ts +0 -26
- package/src/scripts/eval/eval-resume-dirty-guard.ts +0 -8
- package/src/scripts/eval/eval-security-path-traversal.ts +0 -38
- package/src/scripts/run-autoresearch-showcase.sh +0 -75
- /package/docs/{migration-mainline-post-v0.4.4.md → archive/migration-mainline-post-v0.4.4.md} +0 -0
- /package/docs/{qa-plan-0.4.2.md → archive/qa-plan-0.4.2.md} +0 -0
- /package/docs/{qa-report-0.4.2.md → archive/qa-report-0.4.2.md} +0 -0
|
@@ -1,2879 +0,0 @@
|
|
|
1
|
-
import { describe, it } from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
|
-
import { spawnSync } from 'node:child_process';
|
|
4
|
-
import { chmod, mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
5
|
-
import { existsSync } from 'node:fs';
|
|
6
|
-
import { tmpdir } from 'node:os';
|
|
7
|
-
import { join } from 'node:path';
|
|
8
|
-
import { initTeamState, enqueueDispatchRequest, readDispatchRequest } from '../../team/state.js';
|
|
9
|
-
const NOTIFY_HOOK_SCRIPT = new URL('../../../dist/scripts/notify-hook.js', import.meta.url);
|
|
10
|
-
async function withTempWorkingDir(run) {
|
|
11
|
-
const cwd = await mkdtemp(join(tmpdir(), 'rcs-notify-team-nudge-'));
|
|
12
|
-
try {
|
|
13
|
-
await run(cwd);
|
|
14
|
-
}
|
|
15
|
-
finally {
|
|
16
|
-
await rm(cwd, { recursive: true, force: true });
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
async function writeJson(path, value) {
|
|
20
|
-
await writeFile(path, JSON.stringify(value, null, 2));
|
|
21
|
-
}
|
|
22
|
-
async function writeCanonicalTeamFixture(cwd, { teamName, sessionId, ownerSessionId, coarseState = 'missing', }) {
|
|
23
|
-
const stateDir = join(cwd, '.rcs', 'state');
|
|
24
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
25
|
-
const workersDir = join(teamDir, 'workers');
|
|
26
|
-
const nowIso = new Date().toISOString();
|
|
27
|
-
await mkdir(join(cwd, '.rcs', 'logs'), { recursive: true });
|
|
28
|
-
await mkdir(workersDir, { recursive: true });
|
|
29
|
-
await writeJson(join(stateDir, 'session.json'), { session_id: sessionId });
|
|
30
|
-
if (coarseState !== 'missing') {
|
|
31
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
32
|
-
active: coarseState === 'active',
|
|
33
|
-
team_name: teamName,
|
|
34
|
-
current_phase: 'team-exec',
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
await writeJson(join(stateDir, 'hud-state.json'), {
|
|
38
|
-
last_turn_at: nowIso,
|
|
39
|
-
turn_count: 1,
|
|
40
|
-
});
|
|
41
|
-
await writeJson(join(teamDir, 'manifest.v2.json'), {
|
|
42
|
-
schema_version: 2,
|
|
43
|
-
name: teamName,
|
|
44
|
-
task: 'canonical notify fallback repro',
|
|
45
|
-
leader: {
|
|
46
|
-
session_id: ownerSessionId,
|
|
47
|
-
worker_id: 'leader-fixed',
|
|
48
|
-
role: 'coordinator',
|
|
49
|
-
},
|
|
50
|
-
policy: {
|
|
51
|
-
worker_launch_mode: 'interactive',
|
|
52
|
-
display_mode: 'split_pane',
|
|
53
|
-
dispatch_mode: 'hook_preferred_with_fallback',
|
|
54
|
-
dispatch_ack_timeout_ms: 2000,
|
|
55
|
-
},
|
|
56
|
-
governance: {
|
|
57
|
-
delegation_only: false,
|
|
58
|
-
plan_approval_required: false,
|
|
59
|
-
nested_teams_allowed: false,
|
|
60
|
-
one_team_per_leader_session: true,
|
|
61
|
-
cleanup_requires_all_workers_inactive: true,
|
|
62
|
-
},
|
|
63
|
-
lifecycle_profile: 'default',
|
|
64
|
-
permissions_snapshot: {
|
|
65
|
-
approval_mode: 'never',
|
|
66
|
-
sandbox_mode: 'danger-full-access',
|
|
67
|
-
network_access: true,
|
|
68
|
-
},
|
|
69
|
-
tmux_session: `${teamName}:0`,
|
|
70
|
-
leader_pane_id: '%97',
|
|
71
|
-
hud_pane_id: null,
|
|
72
|
-
resize_hook_name: null,
|
|
73
|
-
resize_hook_target: null,
|
|
74
|
-
worker_count: 2,
|
|
75
|
-
next_task_id: 1,
|
|
76
|
-
workers: [
|
|
77
|
-
{ name: 'worker-1', index: 1, pane_id: '%1', role: 'executor' },
|
|
78
|
-
{ name: 'worker-2', index: 2, pane_id: '%2', role: 'executor' },
|
|
79
|
-
],
|
|
80
|
-
created_at: nowIso,
|
|
81
|
-
});
|
|
82
|
-
await writeJson(join(teamDir, 'phase.json'), {
|
|
83
|
-
current_phase: 'team-exec',
|
|
84
|
-
updated_at: nowIso,
|
|
85
|
-
transitions: [],
|
|
86
|
-
});
|
|
87
|
-
for (const worker of ['worker-1', 'worker-2']) {
|
|
88
|
-
await mkdir(join(workersDir, worker), { recursive: true });
|
|
89
|
-
await writeJson(join(workersDir, worker, 'status.json'), {
|
|
90
|
-
state: 'idle',
|
|
91
|
-
updated_at: nowIso,
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
async function readTeamDeliveryLog(cwd) {
|
|
96
|
-
const path = join(cwd, '.rcs', 'logs', `team-delivery-${new Date().toISOString().slice(0, 10)}.jsonl`);
|
|
97
|
-
const raw = await readFile(path, 'utf-8').catch(() => '');
|
|
98
|
-
return raw
|
|
99
|
-
.split('\n')
|
|
100
|
-
.map((line) => line.trim())
|
|
101
|
-
.filter(Boolean)
|
|
102
|
-
.map((line) => JSON.parse(line));
|
|
103
|
-
}
|
|
104
|
-
function buildFakeTmux(tmuxLogPath) {
|
|
105
|
-
return `#!/usr/bin/env bash
|
|
106
|
-
set -eu
|
|
107
|
-
echo "$@" >> "${tmuxLogPath}"
|
|
108
|
-
cmd="$1"
|
|
109
|
-
shift || true
|
|
110
|
-
if [[ "$cmd" == "display-message" ]]; then
|
|
111
|
-
exit 0
|
|
112
|
-
fi
|
|
113
|
-
if [[ "$cmd" == "send-keys" ]]; then
|
|
114
|
-
exit 0
|
|
115
|
-
fi
|
|
116
|
-
if [[ "$cmd" == "list-panes" ]]; then
|
|
117
|
-
echo "%1 12345"
|
|
118
|
-
echo "%2 12346"
|
|
119
|
-
exit 0
|
|
120
|
-
fi
|
|
121
|
-
exit 0
|
|
122
|
-
`;
|
|
123
|
-
}
|
|
124
|
-
function buildFakeTmuxWithListPanes(tmuxLogPath, listPaneLines) {
|
|
125
|
-
const escapedLines = listPaneLines
|
|
126
|
-
.map((line) => line.replaceAll('\\', '\\\\').replaceAll('"', '\\"'))
|
|
127
|
-
.join('\\n');
|
|
128
|
-
return `#!/usr/bin/env bash
|
|
129
|
-
set -eu
|
|
130
|
-
echo "$@" >> "${tmuxLogPath}"
|
|
131
|
-
cmd="$1"
|
|
132
|
-
shift || true
|
|
133
|
-
if [[ "$cmd" == "display-message" ]]; then
|
|
134
|
-
exit 0
|
|
135
|
-
fi
|
|
136
|
-
if [[ "$cmd" == "send-keys" ]]; then
|
|
137
|
-
exit 0
|
|
138
|
-
fi
|
|
139
|
-
if [[ "$cmd" == "list-panes" ]]; then
|
|
140
|
-
printf "%b\\n" "${escapedLines}"
|
|
141
|
-
exit 0
|
|
142
|
-
fi
|
|
143
|
-
exit 0
|
|
144
|
-
`;
|
|
145
|
-
}
|
|
146
|
-
function runNotifyHook(cwd, fakeBinDir, extraEnv = {}) {
|
|
147
|
-
const payload = {
|
|
148
|
-
cwd,
|
|
149
|
-
type: 'agent-turn-complete',
|
|
150
|
-
'thread-id': 'thread-test',
|
|
151
|
-
'turn-id': `turn-${Date.now()}`,
|
|
152
|
-
'input-messages': ['test'],
|
|
153
|
-
'last-assistant-message': 'output',
|
|
154
|
-
};
|
|
155
|
-
return spawnSync(process.execPath, [NOTIFY_HOOK_SCRIPT.pathname, JSON.stringify(payload)], {
|
|
156
|
-
encoding: 'utf8',
|
|
157
|
-
env: {
|
|
158
|
-
...process.env,
|
|
159
|
-
PATH: `${fakeBinDir}:${process.env.PATH || ''}`,
|
|
160
|
-
RCS_TEAM_LEADER_NUDGE_MS: '10000',
|
|
161
|
-
RCS_TEAM_LEADER_STALE_MS: '10000',
|
|
162
|
-
RCS_TEAM_WORKER: '',
|
|
163
|
-
RCS_TEAM_STATE_ROOT: '',
|
|
164
|
-
RCS_TEAM_LEADER_CWD: '',
|
|
165
|
-
RCS_MODEL_INSTRUCTIONS_FILE: '',
|
|
166
|
-
TMUX: '',
|
|
167
|
-
TMUX_PANE: '',
|
|
168
|
-
...extraEnv,
|
|
169
|
-
},
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
describe('notify-hook leader-side authority handoff', () => {
|
|
173
|
-
it('does not inject leader nudge from notify-hook when team is active and stale', async () => {
|
|
174
|
-
await withTempWorkingDir(async (cwd) => {
|
|
175
|
-
const rcsDir = join(cwd, '.rcs');
|
|
176
|
-
const stateDir = join(rcsDir, 'state');
|
|
177
|
-
const logsDir = join(rcsDir, 'logs');
|
|
178
|
-
const teamName = 'handoff-alpha';
|
|
179
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
180
|
-
const mailboxDir = join(teamDir, 'mailbox');
|
|
181
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
182
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
183
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
184
|
-
await mkdir(logsDir, { recursive: true });
|
|
185
|
-
await mkdir(mailboxDir, { recursive: true });
|
|
186
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
187
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
188
|
-
active: true,
|
|
189
|
-
team_name: teamName,
|
|
190
|
-
current_phase: 'team-exec',
|
|
191
|
-
});
|
|
192
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
193
|
-
name: teamName,
|
|
194
|
-
tmux_session: 'handoff-sess:0',
|
|
195
|
-
leader_pane_id: '%91',
|
|
196
|
-
});
|
|
197
|
-
await writeJson(join(stateDir, 'hud-state.json'), {
|
|
198
|
-
last_turn_at: new Date(Date.now() - 300_000).toISOString(),
|
|
199
|
-
turn_count: 1,
|
|
200
|
-
});
|
|
201
|
-
await writeJson(join(mailboxDir, 'leader-fixed.json'), {
|
|
202
|
-
worker: 'leader-fixed',
|
|
203
|
-
messages: [
|
|
204
|
-
{
|
|
205
|
-
message_id: 'm1',
|
|
206
|
-
from_worker: 'worker-1',
|
|
207
|
-
to_worker: 'leader-fixed',
|
|
208
|
-
body: 'ACK',
|
|
209
|
-
created_at: '2026-02-14T00:00:00.000Z',
|
|
210
|
-
},
|
|
211
|
-
],
|
|
212
|
-
});
|
|
213
|
-
await writeFile(fakeTmuxPath, buildFakeTmux(tmuxLogPath));
|
|
214
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
215
|
-
const result = runNotifyHook(cwd, fakeBinDir, {
|
|
216
|
-
RCS_SESSION_ID: 'sess-canonical-missing',
|
|
217
|
-
});
|
|
218
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
219
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8').catch(() => '');
|
|
220
|
-
assert.match(tmuxLog, /send-keys/, 'current implementation nudges the leader directly in this stale-leader path');
|
|
221
|
-
});
|
|
222
|
-
});
|
|
223
|
-
it('does not drain pending dispatch requests from notify-hook leader context', async () => {
|
|
224
|
-
await withTempWorkingDir(async (cwd) => {
|
|
225
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
226
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
227
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
228
|
-
await mkdir(join(cwd, '.rcs', 'logs'), { recursive: true });
|
|
229
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
230
|
-
await writeFile(fakeTmuxPath, buildFakeTmux(tmuxLogPath));
|
|
231
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
232
|
-
await initTeamState('handoff-dispatch', 'task', 'executor', 1, cwd);
|
|
233
|
-
const queued = await enqueueDispatchRequest('handoff-dispatch', {
|
|
234
|
-
kind: 'inbox',
|
|
235
|
-
to_worker: 'worker-1',
|
|
236
|
-
worker_index: 1,
|
|
237
|
-
trigger_message: 'dispatch ping',
|
|
238
|
-
}, cwd);
|
|
239
|
-
const result = runNotifyHook(cwd, fakeBinDir, {
|
|
240
|
-
RCS_SESSION_ID: 'sess-canonical-inactive',
|
|
241
|
-
});
|
|
242
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
243
|
-
const request = await readDispatchRequest('handoff-dispatch', queued.request.request_id, cwd);
|
|
244
|
-
assert.equal(request?.status, 'failed');
|
|
245
|
-
});
|
|
246
|
-
});
|
|
247
|
-
it('does not nudge stale leader when recent team status activity proves the leader is active', async () => {
|
|
248
|
-
await withTempWorkingDir(async (cwd) => {
|
|
249
|
-
const rcsDir = join(cwd, '.rcs');
|
|
250
|
-
const stateDir = join(rcsDir, 'state');
|
|
251
|
-
const logsDir = join(rcsDir, 'logs');
|
|
252
|
-
const teamName = 'beta-active-status';
|
|
253
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
254
|
-
const workersDir = join(teamDir, 'workers');
|
|
255
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
256
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
257
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
258
|
-
await mkdir(logsDir, { recursive: true });
|
|
259
|
-
await mkdir(join(workersDir, 'worker-1'), { recursive: true });
|
|
260
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
261
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
262
|
-
active: true,
|
|
263
|
-
team_name: teamName,
|
|
264
|
-
current_phase: 'team-exec',
|
|
265
|
-
});
|
|
266
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
267
|
-
name: teamName,
|
|
268
|
-
tmux_session: 'rcs-team-beta-active-status',
|
|
269
|
-
leader_pane_id: '%92',
|
|
270
|
-
workers: [
|
|
271
|
-
{ name: 'worker-1', index: 1, pane_id: '%10' },
|
|
272
|
-
],
|
|
273
|
-
});
|
|
274
|
-
await writeJson(join(stateDir, 'hud-state.json'), {
|
|
275
|
-
last_turn_at: new Date(Date.now() - 300_000).toISOString(),
|
|
276
|
-
turn_count: 5,
|
|
277
|
-
});
|
|
278
|
-
await writeJson(join(stateDir, 'leader-runtime-activity.json'), {
|
|
279
|
-
last_activity_at: new Date(Date.now() - 5_000).toISOString(),
|
|
280
|
-
last_team_status_at: new Date(Date.now() - 5_000).toISOString(),
|
|
281
|
-
last_source: 'team_status',
|
|
282
|
-
last_team_name: teamName,
|
|
283
|
-
});
|
|
284
|
-
await writeJson(join(workersDir, 'worker-1', 'status.json'), {
|
|
285
|
-
state: 'working',
|
|
286
|
-
current_task_id: '1',
|
|
287
|
-
updated_at: new Date().toISOString(),
|
|
288
|
-
});
|
|
289
|
-
await writeFile(fakeTmuxPath, buildFakeTmuxWithListPanes(tmuxLogPath, ['%10 12345']));
|
|
290
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
291
|
-
const result = runNotifyHook(cwd, fakeBinDir, {
|
|
292
|
-
RCS_SESSION_ID: 'sess-current',
|
|
293
|
-
});
|
|
294
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
295
|
-
if (existsSync(tmuxLogPath)) {
|
|
296
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
297
|
-
assert.doesNotMatch(tmuxLog, /Team beta-active-status:/);
|
|
298
|
-
assert.doesNotMatch(tmuxLog, /leader stale/);
|
|
299
|
-
}
|
|
300
|
-
});
|
|
301
|
-
});
|
|
302
|
-
});
|
|
303
|
-
describe('notify-hook team leader nudge', () => {
|
|
304
|
-
it('disables leader nudges when deep-interview state is active', async () => {
|
|
305
|
-
await withTempWorkingDir(async (cwd) => {
|
|
306
|
-
const rcsDir = join(cwd, '.rcs');
|
|
307
|
-
const stateDir = join(rcsDir, 'state');
|
|
308
|
-
const logsDir = join(rcsDir, 'logs');
|
|
309
|
-
const teamName = 'deep-interview-suppressed';
|
|
310
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
311
|
-
const mailboxDir = join(teamDir, 'mailbox');
|
|
312
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
313
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
314
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
315
|
-
await mkdir(logsDir, { recursive: true });
|
|
316
|
-
await mkdir(mailboxDir, { recursive: true });
|
|
317
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
318
|
-
await writeJson(join(stateDir, 'deep-interview-state.json'), {
|
|
319
|
-
active: true,
|
|
320
|
-
mode: 'deep-interview',
|
|
321
|
-
current_phase: 'deep-interview',
|
|
322
|
-
});
|
|
323
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
324
|
-
active: true,
|
|
325
|
-
team_name: teamName,
|
|
326
|
-
current_phase: 'team-exec',
|
|
327
|
-
});
|
|
328
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
329
|
-
name: teamName,
|
|
330
|
-
tmux_session: 'deep-interview-suppressed:0',
|
|
331
|
-
leader_pane_id: '%97',
|
|
332
|
-
});
|
|
333
|
-
await writeJson(join(stateDir, 'hud-state.json'), {
|
|
334
|
-
last_turn_at: new Date(Date.now() - 300_000).toISOString(),
|
|
335
|
-
turn_count: 1,
|
|
336
|
-
});
|
|
337
|
-
await writeJson(join(mailboxDir, 'leader-fixed.json'), {
|
|
338
|
-
worker: 'leader-fixed',
|
|
339
|
-
messages: [{ message_id: 'msg-1', from_worker: 'worker-1', to_worker: 'leader-fixed', body: 'review', created_at: new Date().toISOString() }],
|
|
340
|
-
});
|
|
341
|
-
await writeFile(fakeTmuxPath, buildFakeTmux(tmuxLogPath));
|
|
342
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
343
|
-
const result = runNotifyHook(cwd, fakeBinDir, {
|
|
344
|
-
RCS_SESSION_ID: 'sess-canonical-missing',
|
|
345
|
-
});
|
|
346
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
347
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8').catch(() => '');
|
|
348
|
-
assert.doesNotMatch(tmuxLog, /send-keys -t %97 -l Team deep-interview-suppressed:/);
|
|
349
|
-
});
|
|
350
|
-
});
|
|
351
|
-
it('sends immediate all-workers-idle nudge for active team (leader context)', async () => {
|
|
352
|
-
await withTempWorkingDir(async (cwd) => {
|
|
353
|
-
const rcsDir = join(cwd, '.rcs');
|
|
354
|
-
const stateDir = join(rcsDir, 'state');
|
|
355
|
-
const logsDir = join(rcsDir, 'logs');
|
|
356
|
-
const teamName = 'idle-alpha';
|
|
357
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
358
|
-
const workersDir = join(teamDir, 'workers');
|
|
359
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
360
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
361
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
362
|
-
await mkdir(logsDir, { recursive: true });
|
|
363
|
-
await mkdir(workersDir, { recursive: true });
|
|
364
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
365
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
366
|
-
active: true,
|
|
367
|
-
team_name: teamName,
|
|
368
|
-
current_phase: 'team-exec',
|
|
369
|
-
});
|
|
370
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
371
|
-
name: teamName,
|
|
372
|
-
tmux_session: 'idle-sess:0',
|
|
373
|
-
leader_pane_id: '%99',
|
|
374
|
-
workers: [
|
|
375
|
-
{ name: 'worker-1', index: 1, role: 'executor', assigned_tasks: [] },
|
|
376
|
-
{ name: 'worker-2', index: 2, role: 'executor', assigned_tasks: [] },
|
|
377
|
-
],
|
|
378
|
-
});
|
|
379
|
-
await writeJson(join(stateDir, 'hud-state.json'), {
|
|
380
|
-
last_turn_at: new Date().toISOString(),
|
|
381
|
-
turn_count: 1,
|
|
382
|
-
});
|
|
383
|
-
await mkdir(join(workersDir, 'worker-1'), { recursive: true });
|
|
384
|
-
await writeJson(join(workersDir, 'worker-1', 'status.json'), {
|
|
385
|
-
state: 'idle',
|
|
386
|
-
updated_at: new Date().toISOString(),
|
|
387
|
-
});
|
|
388
|
-
await mkdir(join(workersDir, 'worker-2'), { recursive: true });
|
|
389
|
-
await writeJson(join(workersDir, 'worker-2', 'status.json'), {
|
|
390
|
-
state: 'idle',
|
|
391
|
-
updated_at: new Date().toISOString(),
|
|
392
|
-
});
|
|
393
|
-
await writeFile(fakeTmuxPath, buildFakeTmux(tmuxLogPath));
|
|
394
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
395
|
-
const result = runNotifyHook(cwd, fakeBinDir, {
|
|
396
|
-
RCS_SESSION_ID: 'sess-canonical-inactive',
|
|
397
|
-
});
|
|
398
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
399
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
400
|
-
assert.match(tmuxLog, /send-keys/);
|
|
401
|
-
assert.match(tmuxLog, /-t %99/, 'should target leader pane when present');
|
|
402
|
-
assert.match(tmuxLog, /\[RCS\] All 2 workers idle/, 'should emit all-workers-idle nudge');
|
|
403
|
-
assert.doesNotMatch(tmuxLog, /\[RCS_INTENT:/, 'should keep orchestration intent out of injected display text');
|
|
404
|
-
assert.match(tmuxLog, /\[RCS_TMUX_INJECT\]/, 'should include injection marker');
|
|
405
|
-
const submitMatches = tmuxLog.match(/send-keys -t %99 C-m/g) || [];
|
|
406
|
-
assert.equal(submitMatches.length, 2, 'leader nudge should submit with isolated double C-m');
|
|
407
|
-
assert.ok(!/send-keys[^\n]*-l[^\n]*C-m/.test(tmuxLog), 'must not mix literal payload with submit keypresses');
|
|
408
|
-
const eventsPath = join(teamDir, 'events', 'events.ndjson');
|
|
409
|
-
assert.ok(existsSync(eventsPath), 'events.ndjson should exist');
|
|
410
|
-
const eventsContent = await readFile(eventsPath, 'utf-8');
|
|
411
|
-
const events = eventsContent.trim().split('\n').map(line => JSON.parse(line));
|
|
412
|
-
const nudgeEvent = events.find((e) => e.type === 'team_leader_nudge');
|
|
413
|
-
assert.ok(nudgeEvent, 'should have team_leader_nudge event');
|
|
414
|
-
assert.equal(nudgeEvent.reason, 'done_waiting_on_leader');
|
|
415
|
-
assert.equal(nudgeEvent.orchestration_intent, 'done-review-or-shutdown');
|
|
416
|
-
});
|
|
417
|
-
});
|
|
418
|
-
it('suggests shutdown when all workers are idle and the current task set is complete', async () => {
|
|
419
|
-
await withTempWorkingDir(async (cwd) => {
|
|
420
|
-
const rcsDir = join(cwd, '.rcs');
|
|
421
|
-
const stateDir = join(rcsDir, 'state');
|
|
422
|
-
const logsDir = join(rcsDir, 'logs');
|
|
423
|
-
const teamName = 'idle-shutdown';
|
|
424
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
425
|
-
const workersDir = join(teamDir, 'workers');
|
|
426
|
-
const tasksDir = join(teamDir, 'tasks');
|
|
427
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
428
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
429
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
430
|
-
const nowIso = new Date().toISOString();
|
|
431
|
-
await mkdir(logsDir, { recursive: true });
|
|
432
|
-
await mkdir(workersDir, { recursive: true });
|
|
433
|
-
await mkdir(tasksDir, { recursive: true });
|
|
434
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
435
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
436
|
-
active: true,
|
|
437
|
-
team_name: teamName,
|
|
438
|
-
current_phase: 'team-exec',
|
|
439
|
-
});
|
|
440
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
441
|
-
name: teamName,
|
|
442
|
-
tmux_session: 'idle-shutdown:0',
|
|
443
|
-
leader_pane_id: '%96',
|
|
444
|
-
workers: [
|
|
445
|
-
{ name: 'worker-1', index: 1, pane_id: '%10', role: 'executor' },
|
|
446
|
-
{ name: 'worker-2', index: 2, pane_id: '%11', role: 'executor' },
|
|
447
|
-
],
|
|
448
|
-
});
|
|
449
|
-
await writeJson(join(stateDir, 'hud-state.json'), {
|
|
450
|
-
last_turn_at: nowIso,
|
|
451
|
-
turn_count: 1,
|
|
452
|
-
});
|
|
453
|
-
await writeJson(join(tasksDir, 'task-1.json'), {
|
|
454
|
-
id: '1',
|
|
455
|
-
subject: 'Done',
|
|
456
|
-
description: 'completed work item',
|
|
457
|
-
status: 'completed',
|
|
458
|
-
owner: 'worker-1',
|
|
459
|
-
created_at: nowIso,
|
|
460
|
-
});
|
|
461
|
-
for (const worker of ['worker-1', 'worker-2']) {
|
|
462
|
-
await mkdir(join(workersDir, worker), { recursive: true });
|
|
463
|
-
await writeJson(join(workersDir, worker, 'status.json'), {
|
|
464
|
-
state: 'idle',
|
|
465
|
-
updated_at: nowIso,
|
|
466
|
-
});
|
|
467
|
-
}
|
|
468
|
-
await writeFile(fakeTmuxPath, buildFakeTmuxWithListPanes(tmuxLogPath, ['%10 12345', '%11 12346']));
|
|
469
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
470
|
-
const result = runNotifyHook(cwd, fakeBinDir, {
|
|
471
|
-
RCS_SESSION_ID: 'sess-canonical-missing',
|
|
472
|
-
});
|
|
473
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
474
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
475
|
-
assert.match(tmuxLog, /\[RCS\] All 2 workers idle\./);
|
|
476
|
-
assert.match(tmuxLog, /Team idle-shutdown looks complete\./);
|
|
477
|
-
assert.match(tmuxLog, /Next: decide whether to reconcile\/merge results or gracefully shut down: rcs team shutdown idle-shutdown\./);
|
|
478
|
-
assert.doesNotMatch(tmuxLog, /keep polling/);
|
|
479
|
-
});
|
|
480
|
-
});
|
|
481
|
-
it('suggests reusing the team when follow-up tasks are pending and worker panes are still reusable', async () => {
|
|
482
|
-
await withTempWorkingDir(async (cwd) => {
|
|
483
|
-
const rcsDir = join(cwd, '.rcs');
|
|
484
|
-
const stateDir = join(rcsDir, 'state');
|
|
485
|
-
const logsDir = join(rcsDir, 'logs');
|
|
486
|
-
const teamName = 'idle-followup-reuse';
|
|
487
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
488
|
-
const workersDir = join(teamDir, 'workers');
|
|
489
|
-
const tasksDir = join(teamDir, 'tasks');
|
|
490
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
491
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
492
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
493
|
-
const nowIso = new Date().toISOString();
|
|
494
|
-
await mkdir(logsDir, { recursive: true });
|
|
495
|
-
await mkdir(workersDir, { recursive: true });
|
|
496
|
-
await mkdir(tasksDir, { recursive: true });
|
|
497
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
498
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
499
|
-
active: true,
|
|
500
|
-
team_name: teamName,
|
|
501
|
-
current_phase: 'team-exec',
|
|
502
|
-
});
|
|
503
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
504
|
-
name: teamName,
|
|
505
|
-
tmux_session: 'idle-followup-reuse:0',
|
|
506
|
-
leader_pane_id: '%97',
|
|
507
|
-
workers: [
|
|
508
|
-
{ name: 'worker-1', index: 1, pane_id: '%10', role: 'executor' },
|
|
509
|
-
{ name: 'worker-2', index: 2, pane_id: '%11', role: 'executor' },
|
|
510
|
-
],
|
|
511
|
-
});
|
|
512
|
-
await writeJson(join(stateDir, 'hud-state.json'), {
|
|
513
|
-
last_turn_at: nowIso,
|
|
514
|
-
turn_count: 1,
|
|
515
|
-
});
|
|
516
|
-
await writeJson(join(tasksDir, 'task-2.json'), {
|
|
517
|
-
id: '2',
|
|
518
|
-
subject: 'Follow-up',
|
|
519
|
-
description: 'queued follow-up task',
|
|
520
|
-
status: 'pending',
|
|
521
|
-
created_at: nowIso,
|
|
522
|
-
});
|
|
523
|
-
for (const worker of ['worker-1', 'worker-2']) {
|
|
524
|
-
await mkdir(join(workersDir, worker), { recursive: true });
|
|
525
|
-
await writeJson(join(workersDir, worker, 'status.json'), {
|
|
526
|
-
state: 'idle',
|
|
527
|
-
updated_at: nowIso,
|
|
528
|
-
});
|
|
529
|
-
}
|
|
530
|
-
await writeFile(fakeTmuxPath, buildFakeTmuxWithListPanes(tmuxLogPath, ['%10 12345', '%11 12346']));
|
|
531
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
532
|
-
const result = runNotifyHook(cwd, fakeBinDir, {
|
|
533
|
-
RCS_SESSION_ID: 'sess-canonical-inactive',
|
|
534
|
-
});
|
|
535
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
536
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
537
|
-
assert.match(tmuxLog, /\[RCS\] All 2 workers idle/);
|
|
538
|
-
assert.match(tmuxLog, /Team idle-followup-reuse has idle workers ready\./);
|
|
539
|
-
assert.match(tmuxLog, /Next: assign the next follow-up task to this idle team\./);
|
|
540
|
-
assert.doesNotMatch(tmuxLog, /launch a new team/);
|
|
541
|
-
});
|
|
542
|
-
});
|
|
543
|
-
it('suggests launching a new team when follow-up tasks are pending but worker panes are no longer reusable', async () => {
|
|
544
|
-
await withTempWorkingDir(async (cwd) => {
|
|
545
|
-
const rcsDir = join(cwd, '.rcs');
|
|
546
|
-
const stateDir = join(rcsDir, 'state');
|
|
547
|
-
const logsDir = join(rcsDir, 'logs');
|
|
548
|
-
const teamName = 'idle-followup-relaunch';
|
|
549
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
550
|
-
const workersDir = join(teamDir, 'workers');
|
|
551
|
-
const tasksDir = join(teamDir, 'tasks');
|
|
552
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
553
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
554
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
555
|
-
const nowIso = new Date().toISOString();
|
|
556
|
-
await mkdir(logsDir, { recursive: true });
|
|
557
|
-
await mkdir(workersDir, { recursive: true });
|
|
558
|
-
await mkdir(tasksDir, { recursive: true });
|
|
559
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
560
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
561
|
-
active: true,
|
|
562
|
-
team_name: teamName,
|
|
563
|
-
current_phase: 'team-exec',
|
|
564
|
-
});
|
|
565
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
566
|
-
name: teamName,
|
|
567
|
-
tmux_session: 'idle-followup-relaunch:0',
|
|
568
|
-
leader_pane_id: '%98',
|
|
569
|
-
workers: [
|
|
570
|
-
{ name: 'worker-1', index: 1, pane_id: '%10', role: 'executor' },
|
|
571
|
-
{ name: 'worker-2', index: 2, pane_id: '%11', role: 'executor' },
|
|
572
|
-
],
|
|
573
|
-
});
|
|
574
|
-
await writeJson(join(stateDir, 'hud-state.json'), {
|
|
575
|
-
last_turn_at: nowIso,
|
|
576
|
-
turn_count: 1,
|
|
577
|
-
});
|
|
578
|
-
await writeJson(join(tasksDir, 'task-2.json'), {
|
|
579
|
-
id: '2',
|
|
580
|
-
subject: 'Follow-up',
|
|
581
|
-
description: 'queued follow-up task',
|
|
582
|
-
status: 'pending',
|
|
583
|
-
created_at: nowIso,
|
|
584
|
-
});
|
|
585
|
-
for (const worker of ['worker-1', 'worker-2']) {
|
|
586
|
-
await mkdir(join(workersDir, worker), { recursive: true });
|
|
587
|
-
await writeJson(join(workersDir, worker, 'status.json'), {
|
|
588
|
-
state: 'idle',
|
|
589
|
-
updated_at: nowIso,
|
|
590
|
-
});
|
|
591
|
-
}
|
|
592
|
-
await writeFile(fakeTmuxPath, buildFakeTmuxWithListPanes(tmuxLogPath, ['%98 12349']));
|
|
593
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
594
|
-
const result = runNotifyHook(cwd, fakeBinDir);
|
|
595
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
596
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
597
|
-
assert.match(tmuxLog, /\[RCS\] All 2 workers idle/);
|
|
598
|
-
assert.match(tmuxLog, /Team idle-followup-relaunch has follow-up work ready\./);
|
|
599
|
-
assert.match(tmuxLog, /Next: launch a new team for the next task set\./);
|
|
600
|
-
assert.doesNotMatch(tmuxLog, /idle workers ready/);
|
|
601
|
-
});
|
|
602
|
-
});
|
|
603
|
-
it('falls back to global team-state when session-scoped state is active but team-state.json remains global', async () => {
|
|
604
|
-
await withTempWorkingDir(async (cwd) => {
|
|
605
|
-
const rcsDir = join(cwd, '.rcs');
|
|
606
|
-
const stateDir = join(rcsDir, 'state');
|
|
607
|
-
const logsDir = join(rcsDir, 'logs');
|
|
608
|
-
const sessionId = 'sess-idle-fallback';
|
|
609
|
-
const sessionDir = join(stateDir, 'sessions', sessionId);
|
|
610
|
-
const teamName = 'idle-global-fallback';
|
|
611
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
612
|
-
const workersDir = join(teamDir, 'workers');
|
|
613
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
614
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
615
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
616
|
-
await mkdir(logsDir, { recursive: true });
|
|
617
|
-
await mkdir(sessionDir, { recursive: true });
|
|
618
|
-
await mkdir(workersDir, { recursive: true });
|
|
619
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
620
|
-
await writeJson(join(stateDir, 'session.json'), { session_id: sessionId });
|
|
621
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
622
|
-
active: true,
|
|
623
|
-
team_name: teamName,
|
|
624
|
-
current_phase: 'team-exec',
|
|
625
|
-
});
|
|
626
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
627
|
-
name: teamName,
|
|
628
|
-
tmux_session: 'idle-global:0',
|
|
629
|
-
leader_pane_id: '%97',
|
|
630
|
-
workers: [
|
|
631
|
-
{ name: 'worker-1', index: 1, role: 'executor', assigned_tasks: [] },
|
|
632
|
-
{ name: 'worker-2', index: 2, role: 'executor', assigned_tasks: [] },
|
|
633
|
-
],
|
|
634
|
-
});
|
|
635
|
-
await writeJson(join(stateDir, 'hud-state.json'), {
|
|
636
|
-
last_turn_at: new Date().toISOString(),
|
|
637
|
-
turn_count: 1,
|
|
638
|
-
});
|
|
639
|
-
for (const worker of ['worker-1', 'worker-2']) {
|
|
640
|
-
await mkdir(join(workersDir, worker), { recursive: true });
|
|
641
|
-
await writeJson(join(workersDir, worker, 'status.json'), {
|
|
642
|
-
state: 'idle',
|
|
643
|
-
updated_at: new Date().toISOString(),
|
|
644
|
-
});
|
|
645
|
-
}
|
|
646
|
-
await writeFile(fakeTmuxPath, buildFakeTmux(tmuxLogPath));
|
|
647
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
648
|
-
const result = runNotifyHook(cwd, fakeBinDir);
|
|
649
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
650
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
651
|
-
assert.match(tmuxLog, /send-keys/);
|
|
652
|
-
assert.match(tmuxLog, /-t %97/, 'should still target the leader pane');
|
|
653
|
-
assert.match(tmuxLog, /\[RCS\] All 2 workers idle/, 'global team-state fallback should still fire idle nudge');
|
|
654
|
-
});
|
|
655
|
-
});
|
|
656
|
-
it('falls back to canonical team state when coarse team-state is missing', async () => {
|
|
657
|
-
await withTempWorkingDir(async (cwd) => {
|
|
658
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
659
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
660
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
661
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
662
|
-
await writeCanonicalTeamFixture(cwd, {
|
|
663
|
-
teamName: 'canonical-missing',
|
|
664
|
-
sessionId: 'sess-canonical-missing',
|
|
665
|
-
ownerSessionId: 'sess-canonical-missing',
|
|
666
|
-
});
|
|
667
|
-
await writeFile(fakeTmuxPath, buildFakeTmux(tmuxLogPath));
|
|
668
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
669
|
-
const result = runNotifyHook(cwd, fakeBinDir, {
|
|
670
|
-
RCS_SESSION_ID: 'sess-canonical-missing',
|
|
671
|
-
});
|
|
672
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
673
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
674
|
-
assert.match(tmuxLog, /send-keys/);
|
|
675
|
-
assert.match(tmuxLog, /-t %97/, 'should target canonical leader pane');
|
|
676
|
-
assert.match(tmuxLog, /\[RCS\] All 2 workers idle/, 'canonical fallback should still fire idle nudge');
|
|
677
|
-
});
|
|
678
|
-
});
|
|
679
|
-
it('falls back to canonical team state when coarse team-state is inactive', async () => {
|
|
680
|
-
await withTempWorkingDir(async (cwd) => {
|
|
681
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
682
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
683
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
684
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
685
|
-
await writeCanonicalTeamFixture(cwd, {
|
|
686
|
-
teamName: 'canonical-inactive',
|
|
687
|
-
sessionId: 'sess-canonical-inactive',
|
|
688
|
-
ownerSessionId: 'sess-canonical-inactive',
|
|
689
|
-
coarseState: 'inactive',
|
|
690
|
-
});
|
|
691
|
-
await writeFile(fakeTmuxPath, buildFakeTmux(tmuxLogPath));
|
|
692
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
693
|
-
const result = runNotifyHook(cwd, fakeBinDir, {
|
|
694
|
-
RCS_SESSION_ID: 'sess-canonical-inactive',
|
|
695
|
-
});
|
|
696
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
697
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
698
|
-
assert.match(tmuxLog, /send-keys/);
|
|
699
|
-
assert.match(tmuxLog, /-t %97/, 'should still target canonical leader pane');
|
|
700
|
-
assert.match(tmuxLog, /\[RCS\] All 2 workers idle/, 'inactive coarse state should still fall back canonically');
|
|
701
|
-
});
|
|
702
|
-
});
|
|
703
|
-
it('ignores invalid team_name before canonical leader follow-up team path joins', async () => {
|
|
704
|
-
await withTempWorkingDir(async (cwd) => {
|
|
705
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
706
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
707
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
708
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
709
|
-
await writeCanonicalTeamFixture(cwd, {
|
|
710
|
-
teamName: 'canonical-safe',
|
|
711
|
-
sessionId: 'sess-canonical-safe',
|
|
712
|
-
ownerSessionId: 'sess-canonical-safe',
|
|
713
|
-
coarseState: 'inactive',
|
|
714
|
-
});
|
|
715
|
-
const teamRoot = join(cwd, '.rcs', 'state', 'team');
|
|
716
|
-
await mkdir(join(teamRoot, '..-bad-team'), { recursive: true });
|
|
717
|
-
await writeJson(join(teamRoot, '..-bad-team', 'manifest.v2.json'), {
|
|
718
|
-
schema_version: 2,
|
|
719
|
-
name: '..-bad-team',
|
|
720
|
-
task: 'invalid canonical fallback fixture',
|
|
721
|
-
leader: { session_id: 'sess-canonical-safe', worker_id: 'leader-fixed', role: 'coordinator' },
|
|
722
|
-
tmux_session: 'bad:0',
|
|
723
|
-
leader_pane_id: '%666',
|
|
724
|
-
hud_pane_id: null,
|
|
725
|
-
resize_hook_name: null,
|
|
726
|
-
resize_hook_target: null,
|
|
727
|
-
worker_count: 1,
|
|
728
|
-
next_task_id: 1,
|
|
729
|
-
workers: [{ name: 'worker-1', index: 1, pane_id: '%666', role: 'executor' }],
|
|
730
|
-
created_at: new Date().toISOString(),
|
|
731
|
-
policy: {
|
|
732
|
-
worker_launch_mode: 'interactive',
|
|
733
|
-
display_mode: 'split_pane',
|
|
734
|
-
dispatch_mode: 'hook_preferred_with_fallback',
|
|
735
|
-
dispatch_ack_timeout_ms: 2000,
|
|
736
|
-
},
|
|
737
|
-
governance: {
|
|
738
|
-
delegation_only: false,
|
|
739
|
-
plan_approval_required: false,
|
|
740
|
-
nested_teams_allowed: false,
|
|
741
|
-
one_team_per_leader_session: true,
|
|
742
|
-
cleanup_requires_all_workers_inactive: true,
|
|
743
|
-
},
|
|
744
|
-
lifecycle_profile: 'default',
|
|
745
|
-
permissions_snapshot: {
|
|
746
|
-
approval_mode: 'never',
|
|
747
|
-
sandbox_mode: 'danger-full-access',
|
|
748
|
-
network_access: true,
|
|
749
|
-
},
|
|
750
|
-
});
|
|
751
|
-
await writeJson(join(teamRoot, '..-bad-team', 'phase.json'), {
|
|
752
|
-
current_phase: 'team-exec',
|
|
753
|
-
updated_at: new Date().toISOString(),
|
|
754
|
-
transitions: [],
|
|
755
|
-
});
|
|
756
|
-
await writeFile(fakeTmuxPath, buildFakeTmux(tmuxLogPath));
|
|
757
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
758
|
-
const result = runNotifyHook(cwd, fakeBinDir, {
|
|
759
|
-
RCS_SESSION_ID: 'sess-canonical-safe',
|
|
760
|
-
});
|
|
761
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
762
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
763
|
-
assert.match(tmuxLog, /-t %97/, 'should still target the valid canonical leader pane');
|
|
764
|
-
assert.doesNotMatch(tmuxLog, /%666/, 'invalid canonical team names must be ignored before joins');
|
|
765
|
-
assert.match(tmuxLog, /\[RCS\] All 2 workers idle/, 'valid canonical fallback should still fire idle nudge');
|
|
766
|
-
});
|
|
767
|
-
});
|
|
768
|
-
it('nudges leader via tmux send-keys when team is active and mailbox has messages', async () => {
|
|
769
|
-
await withTempWorkingDir(async (cwd) => {
|
|
770
|
-
const rcsDir = join(cwd, '.rcs');
|
|
771
|
-
const stateDir = join(rcsDir, 'state');
|
|
772
|
-
const logsDir = join(rcsDir, 'logs');
|
|
773
|
-
const teamName = 'alpha';
|
|
774
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
775
|
-
const mailboxDir = join(teamDir, 'mailbox');
|
|
776
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
777
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
778
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
779
|
-
await mkdir(logsDir, { recursive: true });
|
|
780
|
-
await mkdir(mailboxDir, { recursive: true });
|
|
781
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
782
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
783
|
-
active: true,
|
|
784
|
-
team_name: teamName,
|
|
785
|
-
current_phase: 'team-exec',
|
|
786
|
-
});
|
|
787
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
788
|
-
name: teamName,
|
|
789
|
-
tmux_session: 'devsess:0',
|
|
790
|
-
leader_pane_id: '%91',
|
|
791
|
-
});
|
|
792
|
-
await writeJson(join(mailboxDir, 'leader-fixed.json'), {
|
|
793
|
-
worker: 'leader-fixed',
|
|
794
|
-
messages: [
|
|
795
|
-
{
|
|
796
|
-
message_id: 'm1',
|
|
797
|
-
from_worker: 'worker-1',
|
|
798
|
-
to_worker: 'leader-fixed',
|
|
799
|
-
body: 'ACK',
|
|
800
|
-
created_at: '2026-02-14T00:00:00.000Z',
|
|
801
|
-
},
|
|
802
|
-
],
|
|
803
|
-
});
|
|
804
|
-
await writeFile(fakeTmuxPath, buildFakeTmux(tmuxLogPath));
|
|
805
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
806
|
-
const result = runNotifyHook(cwd, fakeBinDir);
|
|
807
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
808
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
809
|
-
assert.match(tmuxLog, /send-keys/);
|
|
810
|
-
assert.match(tmuxLog, /-t %91/);
|
|
811
|
-
assert.doesNotMatch(tmuxLog, /-t devsess:0/);
|
|
812
|
-
assert.match(tmuxLog, /Team alpha:/);
|
|
813
|
-
assert.match(tmuxLog, /\[RCS_TMUX_INJECT\]/, 'should include injection marker');
|
|
814
|
-
const deliveryLog = await readTeamDeliveryLog(cwd);
|
|
815
|
-
assert.ok(deliveryLog.some((entry) => entry.event === 'nudge_triggered'
|
|
816
|
-
&& entry.source === 'notify_hook'
|
|
817
|
-
&& entry.team === teamName
|
|
818
|
-
&& entry.to_worker === 'leader-fixed'
|
|
819
|
-
&& entry.transport === 'send-keys'
|
|
820
|
-
&& entry.result === 'sent'));
|
|
821
|
-
});
|
|
822
|
-
});
|
|
823
|
-
it('injects leader nudge into a busy live Codex pane so the message can queue', async () => {
|
|
824
|
-
await withTempWorkingDir(async (cwd) => {
|
|
825
|
-
const rcsDir = join(cwd, '.rcs');
|
|
826
|
-
const stateDir = join(rcsDir, 'state');
|
|
827
|
-
const logsDir = join(rcsDir, 'logs');
|
|
828
|
-
const teamName = 'busy-live-pane';
|
|
829
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
830
|
-
const mailboxDir = join(teamDir, 'mailbox');
|
|
831
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
832
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
833
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
834
|
-
await mkdir(logsDir, { recursive: true });
|
|
835
|
-
await mkdir(mailboxDir, { recursive: true });
|
|
836
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
837
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
838
|
-
active: true,
|
|
839
|
-
team_name: teamName,
|
|
840
|
-
current_phase: 'team-exec',
|
|
841
|
-
});
|
|
842
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
843
|
-
name: teamName,
|
|
844
|
-
tmux_session: 'busy-live-pane:0',
|
|
845
|
-
leader_pane_id: '%93',
|
|
846
|
-
});
|
|
847
|
-
await writeJson(join(mailboxDir, 'leader-fixed.json'), {
|
|
848
|
-
worker: 'leader-fixed',
|
|
849
|
-
messages: [
|
|
850
|
-
{
|
|
851
|
-
message_id: 'busy-msg-1',
|
|
852
|
-
from_worker: 'worker-1',
|
|
853
|
-
to_worker: 'leader-fixed',
|
|
854
|
-
body: 'Need leader review',
|
|
855
|
-
created_at: '2026-03-12T00:00:00.000Z',
|
|
856
|
-
},
|
|
857
|
-
],
|
|
858
|
-
});
|
|
859
|
-
const fakeTmux = `#!/usr/bin/env bash
|
|
860
|
-
set -eu
|
|
861
|
-
echo "$@" >> "${tmuxLogPath}"
|
|
862
|
-
cmd="$1"
|
|
863
|
-
shift || true
|
|
864
|
-
if [[ "$cmd" == "display-message" ]]; then
|
|
865
|
-
target=""
|
|
866
|
-
format=""
|
|
867
|
-
while (($#)); do
|
|
868
|
-
case "$1" in
|
|
869
|
-
-p) shift ;;
|
|
870
|
-
-t) target="$2"; shift 2 ;;
|
|
871
|
-
*) format="$1"; shift ;;
|
|
872
|
-
esac
|
|
873
|
-
done
|
|
874
|
-
if [[ "$format" == "#{pane_in_mode}" && "$target" == "%93" ]]; then
|
|
875
|
-
echo "0"
|
|
876
|
-
exit 0
|
|
877
|
-
fi
|
|
878
|
-
if [[ "$format" == "#{pane_current_command}" && "$target" == "%93" ]]; then
|
|
879
|
-
echo "codex"
|
|
880
|
-
exit 0
|
|
881
|
-
fi
|
|
882
|
-
exit 0
|
|
883
|
-
fi
|
|
884
|
-
if [[ "$cmd" == "capture-pane" ]]; then
|
|
885
|
-
cat <<'EOF'
|
|
886
|
-
OpenAI Codex
|
|
887
|
-
• Working… (esc to interrupt)
|
|
888
|
-
›
|
|
889
|
-
EOF
|
|
890
|
-
exit 0
|
|
891
|
-
fi
|
|
892
|
-
if [[ "$cmd" == "send-keys" ]]; then
|
|
893
|
-
exit 0
|
|
894
|
-
fi
|
|
895
|
-
if [[ "$cmd" == "list-panes" ]]; then
|
|
896
|
-
echo "%1 12345"
|
|
897
|
-
exit 0
|
|
898
|
-
fi
|
|
899
|
-
exit 0
|
|
900
|
-
`;
|
|
901
|
-
await writeFile(fakeTmuxPath, fakeTmux);
|
|
902
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
903
|
-
const result = runNotifyHook(cwd, fakeBinDir);
|
|
904
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
905
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
906
|
-
assert.match(tmuxLog, /display-message -p -t %93 #\{pane_in_mode\}/);
|
|
907
|
-
assert.match(tmuxLog, /capture-pane -t %93 -p -S -80/);
|
|
908
|
-
assert.match(tmuxLog, /send-keys -t %93 -l .*Team busy-live-pane:/);
|
|
909
|
-
assert.match(tmuxLog, /\[RCS_TMUX_INJECT\]/, 'should keep the injection marker on busy-pane sends');
|
|
910
|
-
const eventsPath = join(teamDir, 'events', 'events.ndjson');
|
|
911
|
-
assert.ok(existsSync(eventsPath), 'events.ndjson should exist');
|
|
912
|
-
const events = (await readFile(eventsPath, 'utf-8')).trim().split('\n').filter(Boolean).map((line) => JSON.parse(line));
|
|
913
|
-
assert.ok(events.some((entry) => entry.type === 'team_leader_nudge' && entry.reason === 'new_mailbox_message'));
|
|
914
|
-
assert.ok(!events.some((entry) => entry.type === 'leader_notification_deferred' && entry.reason === 'pane_has_active_task'));
|
|
915
|
-
});
|
|
916
|
-
});
|
|
917
|
-
it('surfaces ack-like mailbox replies without work-start evidence as missing-start nudges', async () => {
|
|
918
|
-
await withTempWorkingDir(async (cwd) => {
|
|
919
|
-
const rcsDir = join(cwd, '.rcs');
|
|
920
|
-
const stateDir = join(rcsDir, 'state');
|
|
921
|
-
const logsDir = join(rcsDir, 'logs');
|
|
922
|
-
const teamName = 'ack-missing-start';
|
|
923
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
924
|
-
const mailboxDir = join(teamDir, 'mailbox');
|
|
925
|
-
const workersDir = join(teamDir, 'workers');
|
|
926
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
927
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
928
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
929
|
-
await mkdir(logsDir, { recursive: true });
|
|
930
|
-
await mkdir(mailboxDir, { recursive: true });
|
|
931
|
-
await mkdir(join(workersDir, 'worker-1'), { recursive: true });
|
|
932
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
933
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
934
|
-
active: true,
|
|
935
|
-
team_name: teamName,
|
|
936
|
-
current_phase: 'team-exec',
|
|
937
|
-
});
|
|
938
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
939
|
-
name: teamName,
|
|
940
|
-
tmux_session: 'ack-sess:0',
|
|
941
|
-
leader_pane_id: '%94',
|
|
942
|
-
workers: [
|
|
943
|
-
{ name: 'worker-1', index: 1, role: 'executor', assigned_tasks: ['1'] },
|
|
944
|
-
],
|
|
945
|
-
});
|
|
946
|
-
await writeJson(join(mailboxDir, 'leader-fixed.json'), {
|
|
947
|
-
worker: 'leader-fixed',
|
|
948
|
-
messages: [
|
|
949
|
-
{
|
|
950
|
-
message_id: 'ack-1',
|
|
951
|
-
from_worker: 'worker-1',
|
|
952
|
-
to_worker: 'leader-fixed',
|
|
953
|
-
body: 'on it',
|
|
954
|
-
created_at: '2026-02-14T00:00:00.000Z',
|
|
955
|
-
},
|
|
956
|
-
],
|
|
957
|
-
});
|
|
958
|
-
await writeFile(fakeTmuxPath, buildFakeTmux(tmuxLogPath));
|
|
959
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
960
|
-
const result = runNotifyHook(cwd, fakeBinDir);
|
|
961
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
962
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
963
|
-
assert.match(tmuxLog, /worker-1 said "on it"/);
|
|
964
|
-
assert.match(tmuxLog, /no start evidence/);
|
|
965
|
-
assert.match(tmuxLog, /status: unknown/);
|
|
966
|
-
assert.match(tmuxLog, /Next: check worker-1 msg\/output, confirm task in rcs team status ack-missing-start/);
|
|
967
|
-
assert.doesNotMatch(tmuxLog, /\[RCS_INTENT:/);
|
|
968
|
-
const eventsPath = join(teamDir, 'events', 'events.ndjson');
|
|
969
|
-
const events = (await readFile(eventsPath, 'utf-8')).trim().split('\n').map(line => JSON.parse(line));
|
|
970
|
-
const nudgeEvent = events.find((e) => e.type === 'team_leader_nudge' && e.reason === 'ack_without_start_evidence');
|
|
971
|
-
assert.ok(nudgeEvent, 'should emit an ack_without_start_evidence leader nudge');
|
|
972
|
-
assert.equal(nudgeEvent.orchestration_intent, 'followup-relaunch');
|
|
973
|
-
});
|
|
974
|
-
});
|
|
975
|
-
it('does not classify ack-like replies as missing-start after a worker has claimed work', async () => {
|
|
976
|
-
await withTempWorkingDir(async (cwd) => {
|
|
977
|
-
const rcsDir = join(cwd, '.rcs');
|
|
978
|
-
const stateDir = join(rcsDir, 'state');
|
|
979
|
-
const logsDir = join(rcsDir, 'logs');
|
|
980
|
-
const teamName = 'ack-with-start';
|
|
981
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
982
|
-
const mailboxDir = join(teamDir, 'mailbox');
|
|
983
|
-
const workersDir = join(teamDir, 'workers');
|
|
984
|
-
const tasksDir = join(teamDir, 'tasks');
|
|
985
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
986
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
987
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
988
|
-
await mkdir(logsDir, { recursive: true });
|
|
989
|
-
await mkdir(mailboxDir, { recursive: true });
|
|
990
|
-
await mkdir(join(workersDir, 'worker-1'), { recursive: true });
|
|
991
|
-
await mkdir(tasksDir, { recursive: true });
|
|
992
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
993
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
994
|
-
active: true,
|
|
995
|
-
team_name: teamName,
|
|
996
|
-
current_phase: 'team-exec',
|
|
997
|
-
});
|
|
998
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
999
|
-
name: teamName,
|
|
1000
|
-
tmux_session: 'ack-started:0',
|
|
1001
|
-
leader_pane_id: '%95',
|
|
1002
|
-
workers: [
|
|
1003
|
-
{ name: 'worker-1', index: 1, role: 'executor', assigned_tasks: ['1'] },
|
|
1004
|
-
],
|
|
1005
|
-
});
|
|
1006
|
-
await writeJson(join(stateDir, 'hud-state.json'), {
|
|
1007
|
-
last_turn_at: new Date().toISOString(),
|
|
1008
|
-
turn_count: 1,
|
|
1009
|
-
});
|
|
1010
|
-
await writeJson(join(workersDir, 'worker-1', 'status.json'), {
|
|
1011
|
-
state: 'working',
|
|
1012
|
-
current_task_id: '1',
|
|
1013
|
-
updated_at: new Date().toISOString(),
|
|
1014
|
-
});
|
|
1015
|
-
await writeJson(join(tasksDir, 'task-1.json'), {
|
|
1016
|
-
id: '1',
|
|
1017
|
-
subject: 'Investigate failure',
|
|
1018
|
-
description: 'trace ack without start',
|
|
1019
|
-
status: 'in_progress',
|
|
1020
|
-
owner: 'worker-1',
|
|
1021
|
-
created_at: new Date().toISOString(),
|
|
1022
|
-
});
|
|
1023
|
-
await writeJson(join(mailboxDir, 'leader-fixed.json'), {
|
|
1024
|
-
worker: 'leader-fixed',
|
|
1025
|
-
messages: [
|
|
1026
|
-
{
|
|
1027
|
-
message_id: 'ack-2',
|
|
1028
|
-
from_worker: 'worker-1',
|
|
1029
|
-
to_worker: 'leader-fixed',
|
|
1030
|
-
body: 'on it',
|
|
1031
|
-
created_at: '2026-02-14T00:00:00.000Z',
|
|
1032
|
-
},
|
|
1033
|
-
],
|
|
1034
|
-
});
|
|
1035
|
-
await writeFile(fakeTmuxPath, buildFakeTmux(tmuxLogPath));
|
|
1036
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
1037
|
-
const result = runNotifyHook(cwd, fakeBinDir);
|
|
1038
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
1039
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
1040
|
-
assert.doesNotMatch(tmuxLog, /no start evidence/);
|
|
1041
|
-
assert.match(tmuxLog, /Team ack-with-start: 1 msg\(s\) for leader\./);
|
|
1042
|
-
assert.match(tmuxLog, /Next: read messages; keep orchestrating; if done, gracefully shut down: rcs team shutdown ack-with-start\./);
|
|
1043
|
-
const eventsPath = join(teamDir, 'events', 'events.ndjson');
|
|
1044
|
-
const events = (await readFile(eventsPath, 'utf-8')).trim().split('\n').map(line => JSON.parse(line));
|
|
1045
|
-
const nudgeEvent = events.find((e) => e.type === 'team_leader_nudge');
|
|
1046
|
-
assert.equal(nudgeEvent?.reason, 'new_mailbox_message');
|
|
1047
|
-
});
|
|
1048
|
-
});
|
|
1049
|
-
it('does not re-nudge for the same fresh mailbox message on repeated notify-hook runs', async () => {
|
|
1050
|
-
await withTempWorkingDir(async (cwd) => {
|
|
1051
|
-
const rcsDir = join(cwd, '.rcs');
|
|
1052
|
-
const stateDir = join(rcsDir, 'state');
|
|
1053
|
-
const logsDir = join(rcsDir, 'logs');
|
|
1054
|
-
const teamName = 'fresh-mailbox-bounded';
|
|
1055
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
1056
|
-
const mailboxDir = join(teamDir, 'mailbox');
|
|
1057
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
1058
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
1059
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
1060
|
-
await mkdir(logsDir, { recursive: true });
|
|
1061
|
-
await mkdir(mailboxDir, { recursive: true });
|
|
1062
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
1063
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
1064
|
-
active: true,
|
|
1065
|
-
team_name: teamName,
|
|
1066
|
-
current_phase: 'team-exec',
|
|
1067
|
-
});
|
|
1068
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
1069
|
-
name: teamName,
|
|
1070
|
-
tmux_session: 'fresh-mailbox-bounded:0',
|
|
1071
|
-
leader_pane_id: '%97',
|
|
1072
|
-
workers: [
|
|
1073
|
-
{ name: 'worker-1', index: 1, role: 'executor', assigned_tasks: ['1'] },
|
|
1074
|
-
],
|
|
1075
|
-
});
|
|
1076
|
-
await writeJson(join(stateDir, 'hud-state.json'), {
|
|
1077
|
-
last_turn_at: new Date().toISOString(),
|
|
1078
|
-
turn_count: 1,
|
|
1079
|
-
});
|
|
1080
|
-
await writeJson(join(mailboxDir, 'leader-fixed.json'), {
|
|
1081
|
-
worker: 'leader-fixed',
|
|
1082
|
-
messages: [
|
|
1083
|
-
{
|
|
1084
|
-
message_id: 'same-msg-1',
|
|
1085
|
-
from_worker: 'worker-1',
|
|
1086
|
-
to_worker: 'leader-fixed',
|
|
1087
|
-
body: 'please review',
|
|
1088
|
-
created_at: '2026-02-14T00:00:00.000Z',
|
|
1089
|
-
},
|
|
1090
|
-
],
|
|
1091
|
-
});
|
|
1092
|
-
await writeFile(fakeTmuxPath, buildFakeTmux(tmuxLogPath));
|
|
1093
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
1094
|
-
const first = runNotifyHook(cwd, fakeBinDir, { RCS_TEAM_LEADER_NUDGE_MS: '600000' });
|
|
1095
|
-
assert.equal(first.status, 0, `notify-hook failed: ${first.stderr || first.stdout}`);
|
|
1096
|
-
const second = runNotifyHook(cwd, fakeBinDir, { RCS_TEAM_LEADER_NUDGE_MS: '600000' });
|
|
1097
|
-
assert.equal(second.status, 0, `notify-hook failed: ${second.stderr || second.stdout}`);
|
|
1098
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
1099
|
-
const sends = tmuxLog.match(/send-keys -t %97 -l Team fresh-mailbox-bounded: 1 msg\(s\) for leader\./g) || [];
|
|
1100
|
-
assert.equal(sends.length, 1, 'same mailbox message should not trigger repeated non-stale nudges');
|
|
1101
|
-
});
|
|
1102
|
-
});
|
|
1103
|
-
it('does not inject leader nudge into a shell pane', async () => {
|
|
1104
|
-
await withTempWorkingDir(async (cwd) => {
|
|
1105
|
-
const rcsDir = join(cwd, '.rcs');
|
|
1106
|
-
const stateDir = join(rcsDir, 'state');
|
|
1107
|
-
const logsDir = join(rcsDir, 'logs');
|
|
1108
|
-
const teamName = 'shell-guard';
|
|
1109
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
1110
|
-
const mailboxDir = join(teamDir, 'mailbox');
|
|
1111
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
1112
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
1113
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
1114
|
-
await mkdir(logsDir, { recursive: true });
|
|
1115
|
-
await mkdir(mailboxDir, { recursive: true });
|
|
1116
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
1117
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
1118
|
-
active: true,
|
|
1119
|
-
team_name: teamName,
|
|
1120
|
-
current_phase: 'team-exec',
|
|
1121
|
-
});
|
|
1122
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
1123
|
-
name: teamName,
|
|
1124
|
-
tmux_session: 'shell-guard:0',
|
|
1125
|
-
leader_pane_id: '%71',
|
|
1126
|
-
});
|
|
1127
|
-
await writeJson(join(mailboxDir, 'leader-fixed.json'), {
|
|
1128
|
-
worker: 'leader-fixed',
|
|
1129
|
-
messages: [
|
|
1130
|
-
{
|
|
1131
|
-
message_id: 'm1',
|
|
1132
|
-
from_worker: 'worker-1',
|
|
1133
|
-
to_worker: 'leader-fixed',
|
|
1134
|
-
body: 'ACK',
|
|
1135
|
-
created_at: '2026-02-14T00:00:00.000Z',
|
|
1136
|
-
},
|
|
1137
|
-
],
|
|
1138
|
-
});
|
|
1139
|
-
const fakeTmux = `#!/usr/bin/env bash
|
|
1140
|
-
set -eu
|
|
1141
|
-
echo "$@" >> "${tmuxLogPath}"
|
|
1142
|
-
cmd="$1"
|
|
1143
|
-
shift || true
|
|
1144
|
-
if [[ "$cmd" == "display-message" ]]; then
|
|
1145
|
-
target=""
|
|
1146
|
-
format=""
|
|
1147
|
-
while (($#)); do
|
|
1148
|
-
case "$1" in
|
|
1149
|
-
-p) shift ;;
|
|
1150
|
-
-t) target="$2"; shift 2 ;;
|
|
1151
|
-
*) format="$1"; shift ;;
|
|
1152
|
-
esac
|
|
1153
|
-
done
|
|
1154
|
-
if [[ "$format" == "#{pane_current_command}" && "$target" == "%71" ]]; then
|
|
1155
|
-
echo "zsh"
|
|
1156
|
-
fi
|
|
1157
|
-
exit 0
|
|
1158
|
-
fi
|
|
1159
|
-
if [[ "$cmd" == "send-keys" ]]; then
|
|
1160
|
-
exit 0
|
|
1161
|
-
fi
|
|
1162
|
-
if [[ "$cmd" == "list-panes" ]]; then
|
|
1163
|
-
echo "%1 12345"
|
|
1164
|
-
exit 0
|
|
1165
|
-
fi
|
|
1166
|
-
exit 0
|
|
1167
|
-
`;
|
|
1168
|
-
await writeFile(fakeTmuxPath, fakeTmux);
|
|
1169
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
1170
|
-
const result = runNotifyHook(cwd, fakeBinDir);
|
|
1171
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
1172
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
1173
|
-
assert.match(tmuxLog, /display-message -p -t %71 #\{pane_current_command\}/);
|
|
1174
|
-
assert.doesNotMatch(tmuxLog, /send-keys -t %71/, 'should not inject into a shell pane');
|
|
1175
|
-
const eventsPath = join(teamDir, 'events', 'events.ndjson');
|
|
1176
|
-
const events = (await readFile(eventsPath, 'utf-8')).trim().split('\n').filter(Boolean).map((line) => JSON.parse(line));
|
|
1177
|
-
const deferred = events.find((entry) => entry.type === 'leader_notification_deferred' && entry.reason === 'leader_pane_shell_no_injection');
|
|
1178
|
-
assert.ok(deferred, 'should emit deferred event for shell-pane leader');
|
|
1179
|
-
assert.equal(deferred.pane_current_command, 'zsh');
|
|
1180
|
-
});
|
|
1181
|
-
});
|
|
1182
|
-
it('injects leader nudge even while the leader pane has an active task', async () => {
|
|
1183
|
-
await withTempWorkingDir(async (cwd) => {
|
|
1184
|
-
const rcsDir = join(cwd, '.rcs');
|
|
1185
|
-
const stateDir = join(rcsDir, 'state');
|
|
1186
|
-
const logsDir = join(rcsDir, 'logs');
|
|
1187
|
-
const teamName = 'busy-leader-queue';
|
|
1188
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
1189
|
-
const mailboxDir = join(teamDir, 'mailbox');
|
|
1190
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
1191
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
1192
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
1193
|
-
await mkdir(logsDir, { recursive: true });
|
|
1194
|
-
await mkdir(mailboxDir, { recursive: true });
|
|
1195
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
1196
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
1197
|
-
active: true,
|
|
1198
|
-
team_name: teamName,
|
|
1199
|
-
current_phase: 'team-exec',
|
|
1200
|
-
});
|
|
1201
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
1202
|
-
name: teamName,
|
|
1203
|
-
tmux_session: 'busy-leader-queue:0',
|
|
1204
|
-
leader_pane_id: '%73',
|
|
1205
|
-
});
|
|
1206
|
-
await writeJson(join(mailboxDir, 'leader-fixed.json'), {
|
|
1207
|
-
worker: 'leader-fixed',
|
|
1208
|
-
messages: [
|
|
1209
|
-
{
|
|
1210
|
-
message_id: 'm1',
|
|
1211
|
-
from_worker: 'worker-1',
|
|
1212
|
-
to_worker: 'leader-fixed',
|
|
1213
|
-
body: 'please review queued message',
|
|
1214
|
-
created_at: '2026-03-12T00:00:00.000Z',
|
|
1215
|
-
},
|
|
1216
|
-
],
|
|
1217
|
-
});
|
|
1218
|
-
const fakeTmux = `#!/usr/bin/env bash
|
|
1219
|
-
set -eu
|
|
1220
|
-
echo "$@" >> "${tmuxLogPath}"
|
|
1221
|
-
cmd="$1"
|
|
1222
|
-
shift || true
|
|
1223
|
-
if [[ "$cmd" == "display-message" ]]; then
|
|
1224
|
-
target=""
|
|
1225
|
-
format=""
|
|
1226
|
-
while (($#)); do
|
|
1227
|
-
case "$1" in
|
|
1228
|
-
-p) shift ;;
|
|
1229
|
-
-t) target="$2"; shift 2 ;;
|
|
1230
|
-
*) format="$1"; shift ;;
|
|
1231
|
-
esac
|
|
1232
|
-
done
|
|
1233
|
-
if [[ "$format" == "#{pane_in_mode}" && "$target" == "%73" ]]; then
|
|
1234
|
-
echo "0"
|
|
1235
|
-
exit 0
|
|
1236
|
-
fi
|
|
1237
|
-
if [[ "$format" == "#{pane_current_command}" && "$target" == "%73" ]]; then
|
|
1238
|
-
echo "codex"
|
|
1239
|
-
exit 0
|
|
1240
|
-
fi
|
|
1241
|
-
exit 0
|
|
1242
|
-
fi
|
|
1243
|
-
if [[ "$cmd" == "capture-pane" ]]; then
|
|
1244
|
-
printf "• Running tests (3m 12s • esc to interrupt)\\n"
|
|
1245
|
-
exit 0
|
|
1246
|
-
fi
|
|
1247
|
-
if [[ "$cmd" == "send-keys" ]]; then
|
|
1248
|
-
exit 0
|
|
1249
|
-
fi
|
|
1250
|
-
if [[ "$cmd" == "list-panes" ]]; then
|
|
1251
|
-
echo "%1 12345"
|
|
1252
|
-
exit 0
|
|
1253
|
-
fi
|
|
1254
|
-
exit 0
|
|
1255
|
-
`;
|
|
1256
|
-
await writeFile(fakeTmuxPath, fakeTmux);
|
|
1257
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
1258
|
-
const result = runNotifyHook(cwd, fakeBinDir);
|
|
1259
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
1260
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
1261
|
-
assert.match(tmuxLog, /capture-pane/);
|
|
1262
|
-
assert.match(tmuxLog, /send-keys -t %73/, 'should inject into a busy leader pane so Codex can queue the message');
|
|
1263
|
-
const eventsPath = join(teamDir, 'events', 'events.ndjson');
|
|
1264
|
-
if (existsSync(eventsPath)) {
|
|
1265
|
-
const events = (await readFile(eventsPath, 'utf-8')).trim().split('\n').filter(Boolean).map((line) => JSON.parse(line));
|
|
1266
|
-
const deferred = events.find((entry) => entry.type === 'leader_notification_deferred' && entry.reason === 'pane_has_active_task');
|
|
1267
|
-
assert.equal(deferred, undefined, 'busy leader pane should no longer defer notifications');
|
|
1268
|
-
}
|
|
1269
|
-
});
|
|
1270
|
-
});
|
|
1271
|
-
it('injects leader nudge when capture-pane fails but the leader pane is a live codex pane', async () => {
|
|
1272
|
-
await withTempWorkingDir(async (cwd) => {
|
|
1273
|
-
const rcsDir = join(cwd, '.rcs');
|
|
1274
|
-
const stateDir = join(rcsDir, 'state');
|
|
1275
|
-
const logsDir = join(rcsDir, 'logs');
|
|
1276
|
-
const teamName = 'capture-failure-live-leader';
|
|
1277
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
1278
|
-
const mailboxDir = join(teamDir, 'mailbox');
|
|
1279
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
1280
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
1281
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
1282
|
-
await mkdir(logsDir, { recursive: true });
|
|
1283
|
-
await mkdir(mailboxDir, { recursive: true });
|
|
1284
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
1285
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
1286
|
-
active: true,
|
|
1287
|
-
team_name: teamName,
|
|
1288
|
-
current_phase: 'team-exec',
|
|
1289
|
-
});
|
|
1290
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
1291
|
-
name: teamName,
|
|
1292
|
-
tmux_session: 'capture-failure-live-leader:0',
|
|
1293
|
-
leader_pane_id: '%74',
|
|
1294
|
-
});
|
|
1295
|
-
await writeJson(join(mailboxDir, 'leader-fixed.json'), {
|
|
1296
|
-
worker: 'leader-fixed',
|
|
1297
|
-
messages: [
|
|
1298
|
-
{
|
|
1299
|
-
message_id: 'm1',
|
|
1300
|
-
from_worker: 'worker-1',
|
|
1301
|
-
to_worker: 'leader-fixed',
|
|
1302
|
-
body: 'please review capture failure path',
|
|
1303
|
-
created_at: '2026-03-12T00:00:00.000Z',
|
|
1304
|
-
},
|
|
1305
|
-
],
|
|
1306
|
-
});
|
|
1307
|
-
const fakeTmux = `#!/usr/bin/env bash
|
|
1308
|
-
set -eu
|
|
1309
|
-
echo "$@" >> "${tmuxLogPath}"
|
|
1310
|
-
cmd="$1"
|
|
1311
|
-
shift || true
|
|
1312
|
-
if [[ "$cmd" == "display-message" ]]; then
|
|
1313
|
-
target=""
|
|
1314
|
-
format=""
|
|
1315
|
-
while (($#)); do
|
|
1316
|
-
case "$1" in
|
|
1317
|
-
-p) shift ;;
|
|
1318
|
-
-t) target="$2"; shift 2 ;;
|
|
1319
|
-
*) format="$1"; shift ;;
|
|
1320
|
-
esac
|
|
1321
|
-
done
|
|
1322
|
-
if [[ "$format" == "#{pane_in_mode}" && "$target" == "%74" ]]; then
|
|
1323
|
-
echo "0"
|
|
1324
|
-
exit 0
|
|
1325
|
-
fi
|
|
1326
|
-
if [[ "$format" == "#{pane_current_command}" && "$target" == "%74" ]]; then
|
|
1327
|
-
echo "codex"
|
|
1328
|
-
exit 0
|
|
1329
|
-
fi
|
|
1330
|
-
exit 0
|
|
1331
|
-
fi
|
|
1332
|
-
if [[ "$cmd" == "capture-pane" ]]; then
|
|
1333
|
-
echo "capture failed" >&2
|
|
1334
|
-
exit 1
|
|
1335
|
-
fi
|
|
1336
|
-
if [[ "$cmd" == "send-keys" ]]; then
|
|
1337
|
-
exit 0
|
|
1338
|
-
fi
|
|
1339
|
-
if [[ "$cmd" == "list-panes" ]]; then
|
|
1340
|
-
echo "%1 12345"
|
|
1341
|
-
exit 0
|
|
1342
|
-
fi
|
|
1343
|
-
exit 0
|
|
1344
|
-
`;
|
|
1345
|
-
await writeFile(fakeTmuxPath, fakeTmux);
|
|
1346
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
1347
|
-
const result = runNotifyHook(cwd, fakeBinDir);
|
|
1348
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
1349
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
1350
|
-
assert.match(tmuxLog, /capture-pane -t %74 -p -S -80/);
|
|
1351
|
-
assert.match(tmuxLog, /send-keys -t %74/, 'capture failures should not suppress leader injection into a live codex pane');
|
|
1352
|
-
const eventsPath = join(teamDir, 'events', 'events.ndjson');
|
|
1353
|
-
if (existsSync(eventsPath)) {
|
|
1354
|
-
const events = (await readFile(eventsPath, 'utf-8')).trim().split('\n').filter(Boolean).map((line) => JSON.parse(line));
|
|
1355
|
-
const deferred = events.find((entry) => entry.type === 'leader_notification_deferred');
|
|
1356
|
-
assert.equal(deferred, undefined, 'capture failure alone should not defer a live codex leader pane');
|
|
1357
|
-
}
|
|
1358
|
-
});
|
|
1359
|
-
});
|
|
1360
|
-
it('suppresses duplicate visible leader injection when the pane already shows the same classified state', async () => {
|
|
1361
|
-
await withTempWorkingDir(async (cwd) => {
|
|
1362
|
-
const rcsDir = join(cwd, '.rcs');
|
|
1363
|
-
const stateDir = join(rcsDir, 'state');
|
|
1364
|
-
const logsDir = join(rcsDir, 'logs');
|
|
1365
|
-
const teamName = 'same-classified-state';
|
|
1366
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
1367
|
-
const mailboxDir = join(teamDir, 'mailbox');
|
|
1368
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
1369
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
1370
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
1371
|
-
await mkdir(logsDir, { recursive: true });
|
|
1372
|
-
await mkdir(mailboxDir, { recursive: true });
|
|
1373
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
1374
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
1375
|
-
active: true,
|
|
1376
|
-
team_name: teamName,
|
|
1377
|
-
current_phase: 'team-exec',
|
|
1378
|
-
});
|
|
1379
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
1380
|
-
name: teamName,
|
|
1381
|
-
tmux_session: 'same-classified-state:0',
|
|
1382
|
-
leader_pane_id: '%75',
|
|
1383
|
-
});
|
|
1384
|
-
await writeJson(join(mailboxDir, 'leader-fixed.json'), {
|
|
1385
|
-
worker: 'leader-fixed',
|
|
1386
|
-
messages: [
|
|
1387
|
-
{
|
|
1388
|
-
message_id: 'same-classified-msg',
|
|
1389
|
-
from_worker: 'worker-1',
|
|
1390
|
-
to_worker: 'leader-fixed',
|
|
1391
|
-
body: 'please review latest output',
|
|
1392
|
-
created_at: '2026-03-12T00:00:00.000Z',
|
|
1393
|
-
},
|
|
1394
|
-
],
|
|
1395
|
-
});
|
|
1396
|
-
const fakeTmux = `#!/usr/bin/env bash
|
|
1397
|
-
set -eu
|
|
1398
|
-
echo "$@" >> "${tmuxLogPath}"
|
|
1399
|
-
cmd="$1"
|
|
1400
|
-
shift || true
|
|
1401
|
-
if [[ "$cmd" == "display-message" ]]; then
|
|
1402
|
-
target=""
|
|
1403
|
-
format=""
|
|
1404
|
-
while (($#)); do
|
|
1405
|
-
case "$1" in
|
|
1406
|
-
-p) shift ;;
|
|
1407
|
-
-t) target="$2"; shift 2 ;;
|
|
1408
|
-
*) format="$1"; shift ;;
|
|
1409
|
-
esac
|
|
1410
|
-
done
|
|
1411
|
-
if [[ "$format" == "#{pane_in_mode}" && "$target" == "%75" ]]; then
|
|
1412
|
-
echo "0"
|
|
1413
|
-
exit 0
|
|
1414
|
-
fi
|
|
1415
|
-
if [[ "$format" == "#{pane_current_command}" && "$target" == "%75" ]]; then
|
|
1416
|
-
echo "codex"
|
|
1417
|
-
exit 0
|
|
1418
|
-
fi
|
|
1419
|
-
exit 0
|
|
1420
|
-
fi
|
|
1421
|
-
if [[ "$cmd" == "capture-pane" ]]; then
|
|
1422
|
-
cat <<'EOF'
|
|
1423
|
-
Team same-classified-state: 1 msg(s) for leader. Next: read messages; keep orchestrating; if done, gracefully shut down: rcs team shutdown same-classified-state.
|
|
1424
|
-
EOF
|
|
1425
|
-
exit 0
|
|
1426
|
-
fi
|
|
1427
|
-
if [[ "$cmd" == "send-keys" ]]; then
|
|
1428
|
-
exit 0
|
|
1429
|
-
fi
|
|
1430
|
-
if [[ "$cmd" == "list-panes" ]]; then
|
|
1431
|
-
echo "%1 12345"
|
|
1432
|
-
exit 0
|
|
1433
|
-
fi
|
|
1434
|
-
exit 0
|
|
1435
|
-
`;
|
|
1436
|
-
await writeFile(fakeTmuxPath, fakeTmux);
|
|
1437
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
1438
|
-
const result = runNotifyHook(cwd, fakeBinDir);
|
|
1439
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
1440
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
1441
|
-
assert.match(tmuxLog, /capture-pane -t %75 -p -S -80/);
|
|
1442
|
-
assert.doesNotMatch(tmuxLog, /send-keys -t %75 -l Team same-classified-state: 1 msg\(s\) for leader\./, 'same visible classified state should not be reinjected');
|
|
1443
|
-
const eventsPath = join(teamDir, 'events', 'events.ndjson');
|
|
1444
|
-
const events = (await readFile(eventsPath, 'utf-8')).trim().split('\n').filter(Boolean).map((line) => JSON.parse(line));
|
|
1445
|
-
const nudgeEvent = events.find((entry) => entry.type === 'team_leader_nudge' && entry.reason === 'new_mailbox_message');
|
|
1446
|
-
assert.ok(nudgeEvent, 'suppressed visible sends should still emit leader nudge events');
|
|
1447
|
-
assert.equal(nudgeEvent.orchestration_intent, 'pending-mailbox-review');
|
|
1448
|
-
const deliveryLog = await readTeamDeliveryLog(cwd);
|
|
1449
|
-
assert.ok(deliveryLog.some((entry) => entry.event === 'nudge_triggered'
|
|
1450
|
-
&& entry.team === teamName
|
|
1451
|
-
&& entry.to_worker === 'leader-fixed'
|
|
1452
|
-
&& entry.result === 'suppressed'
|
|
1453
|
-
&& entry.reason === 'new_mailbox_message'
|
|
1454
|
-
&& entry.suppression_reason === 'pane_already_shows_same_classified_state'));
|
|
1455
|
-
});
|
|
1456
|
-
});
|
|
1457
|
-
it('does not inject leader nudge while leader pane is in copy-mode', async () => {
|
|
1458
|
-
await withTempWorkingDir(async (cwd) => {
|
|
1459
|
-
const rcsDir = join(cwd, '.rcs');
|
|
1460
|
-
const stateDir = join(rcsDir, 'state');
|
|
1461
|
-
const logsDir = join(rcsDir, 'logs');
|
|
1462
|
-
const teamName = 'scroll-guard';
|
|
1463
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
1464
|
-
const mailboxDir = join(teamDir, 'mailbox');
|
|
1465
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
1466
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
1467
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
1468
|
-
await mkdir(logsDir, { recursive: true });
|
|
1469
|
-
await mkdir(mailboxDir, { recursive: true });
|
|
1470
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
1471
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
1472
|
-
active: true,
|
|
1473
|
-
team_name: teamName,
|
|
1474
|
-
current_phase: 'team-exec',
|
|
1475
|
-
});
|
|
1476
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
1477
|
-
name: teamName,
|
|
1478
|
-
tmux_session: 'scroll-guard:0',
|
|
1479
|
-
leader_pane_id: '%72',
|
|
1480
|
-
});
|
|
1481
|
-
await writeJson(join(mailboxDir, 'leader-fixed.json'), {
|
|
1482
|
-
worker: 'leader-fixed',
|
|
1483
|
-
messages: [
|
|
1484
|
-
{
|
|
1485
|
-
message_id: 'm1',
|
|
1486
|
-
from_worker: 'worker-1',
|
|
1487
|
-
to_worker: 'leader-fixed',
|
|
1488
|
-
body: 'follow up',
|
|
1489
|
-
created_at: '2026-03-12T00:00:00.000Z',
|
|
1490
|
-
},
|
|
1491
|
-
],
|
|
1492
|
-
});
|
|
1493
|
-
const fakeTmux = `#!/usr/bin/env bash
|
|
1494
|
-
set -eu
|
|
1495
|
-
echo "$@" >> "${tmuxLogPath}"
|
|
1496
|
-
cmd="$1"
|
|
1497
|
-
shift || true
|
|
1498
|
-
if [[ "$cmd" == "display-message" ]]; then
|
|
1499
|
-
target=""
|
|
1500
|
-
format=""
|
|
1501
|
-
while (($#)); do
|
|
1502
|
-
case "$1" in
|
|
1503
|
-
-p) shift ;;
|
|
1504
|
-
-t) target="$2"; shift 2 ;;
|
|
1505
|
-
*) format="$1"; shift ;;
|
|
1506
|
-
esac
|
|
1507
|
-
done
|
|
1508
|
-
if [[ "$format" == "#{pane_in_mode}" && "$target" == "%72" ]]; then
|
|
1509
|
-
echo "1"
|
|
1510
|
-
exit 0
|
|
1511
|
-
fi
|
|
1512
|
-
if [[ "$format" == "#{pane_current_command}" && "$target" == "%72" ]]; then
|
|
1513
|
-
echo "codex"
|
|
1514
|
-
exit 0
|
|
1515
|
-
fi
|
|
1516
|
-
exit 0
|
|
1517
|
-
fi
|
|
1518
|
-
if [[ "$cmd" == "send-keys" ]]; then
|
|
1519
|
-
exit 0
|
|
1520
|
-
fi
|
|
1521
|
-
if [[ "$cmd" == "list-panes" ]]; then
|
|
1522
|
-
echo "%1 12345"
|
|
1523
|
-
exit 0
|
|
1524
|
-
fi
|
|
1525
|
-
exit 0
|
|
1526
|
-
`;
|
|
1527
|
-
await writeFile(fakeTmuxPath, fakeTmux);
|
|
1528
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
1529
|
-
const result = runNotifyHook(cwd, fakeBinDir);
|
|
1530
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
1531
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
1532
|
-
assert.match(tmuxLog, /display-message -p -t %72 #\{pane_in_mode\}/);
|
|
1533
|
-
assert.doesNotMatch(tmuxLog, /send-keys -t %72/, 'should not inject into a scrolling leader pane');
|
|
1534
|
-
const eventsPath = join(teamDir, 'events', 'events.ndjson');
|
|
1535
|
-
const events = (await readFile(eventsPath, 'utf-8')).trim().split('\n').filter(Boolean).map((line) => JSON.parse(line));
|
|
1536
|
-
const deferred = events.find((entry) => entry.type === 'leader_notification_deferred' && entry.reason === 'scroll_active');
|
|
1537
|
-
assert.ok(deferred, 'should emit deferred event for scrolling leader pane');
|
|
1538
|
-
});
|
|
1539
|
-
});
|
|
1540
|
-
it('syncs stale root team-state to inactive when team-local phase is already terminal', async () => {
|
|
1541
|
-
await withTempWorkingDir(async (cwd) => {
|
|
1542
|
-
const rcsDir = join(cwd, '.rcs');
|
|
1543
|
-
const stateDir = join(rcsDir, 'state');
|
|
1544
|
-
const logsDir = join(rcsDir, 'logs');
|
|
1545
|
-
const teamName = 'terminal-sync';
|
|
1546
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
1547
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
1548
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
1549
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
1550
|
-
await mkdir(logsDir, { recursive: true });
|
|
1551
|
-
await mkdir(teamDir, { recursive: true });
|
|
1552
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
1553
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
1554
|
-
active: true,
|
|
1555
|
-
team_name: teamName,
|
|
1556
|
-
current_phase: 'team-exec',
|
|
1557
|
-
});
|
|
1558
|
-
await writeJson(join(teamDir, 'phase.json'), {
|
|
1559
|
-
current_phase: 'complete',
|
|
1560
|
-
transitions: [
|
|
1561
|
-
{ from: 'team-verify', to: 'complete', at: '2026-03-09T19:20:19.088Z' },
|
|
1562
|
-
],
|
|
1563
|
-
updated_at: '2026-03-09T19:20:19.088Z',
|
|
1564
|
-
});
|
|
1565
|
-
await writeFile(fakeTmuxPath, buildFakeTmux(tmuxLogPath));
|
|
1566
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
1567
|
-
const result = runNotifyHook(cwd, fakeBinDir);
|
|
1568
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
1569
|
-
const syncedState = JSON.parse(await readFile(join(stateDir, 'team-state.json'), 'utf-8'));
|
|
1570
|
-
assert.equal(syncedState.active, false);
|
|
1571
|
-
assert.equal(syncedState.current_phase, 'complete');
|
|
1572
|
-
assert.equal(syncedState.completed_at, '2026-03-09T19:20:19.088Z');
|
|
1573
|
-
if (existsSync(tmuxLogPath)) {
|
|
1574
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
1575
|
-
assert.doesNotMatch(tmuxLog, /send-keys/, 'must not nudge a terminal team');
|
|
1576
|
-
}
|
|
1577
|
-
});
|
|
1578
|
-
});
|
|
1579
|
-
it('does not nudge completed teams on reopen even when config and idle worker state still exist', async () => {
|
|
1580
|
-
await withTempWorkingDir(async (cwd) => {
|
|
1581
|
-
const rcsDir = join(cwd, '.rcs');
|
|
1582
|
-
const stateDir = join(rcsDir, 'state');
|
|
1583
|
-
const logsDir = join(rcsDir, 'logs');
|
|
1584
|
-
const teamName = 'completed-reopen';
|
|
1585
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
1586
|
-
const workersDir = join(teamDir, 'workers');
|
|
1587
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
1588
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
1589
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
1590
|
-
const completedAt = '2026-03-21T08:40:35.471Z';
|
|
1591
|
-
await mkdir(logsDir, { recursive: true });
|
|
1592
|
-
await mkdir(workersDir, { recursive: true });
|
|
1593
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
1594
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
1595
|
-
active: false,
|
|
1596
|
-
team_name: teamName,
|
|
1597
|
-
current_phase: 'complete',
|
|
1598
|
-
completed_at: completedAt,
|
|
1599
|
-
});
|
|
1600
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
1601
|
-
name: teamName,
|
|
1602
|
-
tmux_session: 'completed-reopen:0',
|
|
1603
|
-
leader_pane_id: '%91',
|
|
1604
|
-
workers: [
|
|
1605
|
-
{ name: 'worker-1', index: 1, pane_id: '%10', role: 'executor' },
|
|
1606
|
-
{ name: 'worker-2', index: 2, pane_id: '%11', role: 'executor' },
|
|
1607
|
-
],
|
|
1608
|
-
});
|
|
1609
|
-
await writeJson(join(teamDir, 'phase.json'), {
|
|
1610
|
-
current_phase: 'complete',
|
|
1611
|
-
transitions: [{ from: 'team-verify', to: 'complete', at: completedAt }],
|
|
1612
|
-
updated_at: completedAt,
|
|
1613
|
-
});
|
|
1614
|
-
for (const worker of ['worker-1', 'worker-2']) {
|
|
1615
|
-
await mkdir(join(workersDir, worker), { recursive: true });
|
|
1616
|
-
await writeJson(join(workersDir, worker, 'status.json'), {
|
|
1617
|
-
state: 'idle',
|
|
1618
|
-
updated_at: new Date().toISOString(),
|
|
1619
|
-
});
|
|
1620
|
-
}
|
|
1621
|
-
await writeFile(fakeTmuxPath, buildFakeTmux(tmuxLogPath));
|
|
1622
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
1623
|
-
const result = runNotifyHook(cwd, fakeBinDir);
|
|
1624
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
1625
|
-
if (existsSync(tmuxLogPath)) {
|
|
1626
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
1627
|
-
assert.doesNotMatch(tmuxLog, /send-keys/, 'completed teams must not re-nudge on reopen');
|
|
1628
|
-
}
|
|
1629
|
-
});
|
|
1630
|
-
});
|
|
1631
|
-
it('does not nudge a team owned by another session', async () => {
|
|
1632
|
-
await withTempWorkingDir(async (cwd) => {
|
|
1633
|
-
const rcsDir = join(cwd, '.rcs');
|
|
1634
|
-
const stateDir = join(rcsDir, 'state');
|
|
1635
|
-
const logsDir = join(rcsDir, 'logs');
|
|
1636
|
-
const teamName = 'other-session-team';
|
|
1637
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
1638
|
-
const workersDir = join(teamDir, 'workers');
|
|
1639
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
1640
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
1641
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
1642
|
-
const nowIso = new Date().toISOString();
|
|
1643
|
-
await mkdir(logsDir, { recursive: true });
|
|
1644
|
-
await mkdir(workersDir, { recursive: true });
|
|
1645
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
1646
|
-
await writeJson(join(stateDir, 'session.json'), { session_id: 'sess-current' });
|
|
1647
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
1648
|
-
active: true,
|
|
1649
|
-
team_name: teamName,
|
|
1650
|
-
current_phase: 'team-exec',
|
|
1651
|
-
});
|
|
1652
|
-
await writeJson(join(teamDir, 'manifest.v2.json'), {
|
|
1653
|
-
schema_version: 2,
|
|
1654
|
-
name: teamName,
|
|
1655
|
-
task: 'session ownership repro',
|
|
1656
|
-
leader: {
|
|
1657
|
-
session_id: 'sess-other',
|
|
1658
|
-
worker_id: 'leader-fixed',
|
|
1659
|
-
role: 'coordinator',
|
|
1660
|
-
},
|
|
1661
|
-
policy: {
|
|
1662
|
-
worker_launch_mode: 'interactive',
|
|
1663
|
-
display_mode: 'split_pane',
|
|
1664
|
-
dispatch_mode: 'hook_preferred_with_fallback',
|
|
1665
|
-
dispatch_ack_timeout_ms: 2000,
|
|
1666
|
-
},
|
|
1667
|
-
tmux_session: 'other-session-team:0',
|
|
1668
|
-
leader_pane_id: '%94',
|
|
1669
|
-
worker_count: 2,
|
|
1670
|
-
workers: [
|
|
1671
|
-
{ name: 'worker-1', index: 1, pane_id: '%10', role: 'executor' },
|
|
1672
|
-
{ name: 'worker-2', index: 2, pane_id: '%11', role: 'executor' },
|
|
1673
|
-
],
|
|
1674
|
-
created_at: nowIso,
|
|
1675
|
-
});
|
|
1676
|
-
for (const worker of ['worker-1', 'worker-2']) {
|
|
1677
|
-
await mkdir(join(workersDir, worker), { recursive: true });
|
|
1678
|
-
await writeJson(join(workersDir, worker, 'status.json'), {
|
|
1679
|
-
state: 'idle',
|
|
1680
|
-
updated_at: nowIso,
|
|
1681
|
-
});
|
|
1682
|
-
}
|
|
1683
|
-
await writeFile(fakeTmuxPath, buildFakeTmux(tmuxLogPath));
|
|
1684
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
1685
|
-
const result = runNotifyHook(cwd, fakeBinDir);
|
|
1686
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
1687
|
-
if (existsSync(tmuxLogPath)) {
|
|
1688
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
1689
|
-
assert.doesNotMatch(tmuxLog, /send-keys/, 'must not nudge teams owned by another session');
|
|
1690
|
-
}
|
|
1691
|
-
});
|
|
1692
|
-
});
|
|
1693
|
-
it('does not nudge a canonical-only team when owner session is blank', async () => {
|
|
1694
|
-
await withTempWorkingDir(async (cwd) => {
|
|
1695
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
1696
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
1697
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
1698
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
1699
|
-
await writeCanonicalTeamFixture(cwd, {
|
|
1700
|
-
teamName: 'ownerless-team',
|
|
1701
|
-
sessionId: 'sess-current',
|
|
1702
|
-
ownerSessionId: '',
|
|
1703
|
-
});
|
|
1704
|
-
await writeFile(fakeTmuxPath, buildFakeTmux(tmuxLogPath));
|
|
1705
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
1706
|
-
const result = runNotifyHook(cwd, fakeBinDir);
|
|
1707
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
1708
|
-
if (existsSync(tmuxLogPath)) {
|
|
1709
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
1710
|
-
assert.doesNotMatch(tmuxLog, /send-keys/, 'ownerless canonical teams must not nudge');
|
|
1711
|
-
}
|
|
1712
|
-
});
|
|
1713
|
-
});
|
|
1714
|
-
it('nudges when worker panes are alive and leader is stale (no recent HUD turn)', async () => {
|
|
1715
|
-
await withTempWorkingDir(async (cwd) => {
|
|
1716
|
-
const rcsDir = join(cwd, '.rcs');
|
|
1717
|
-
const stateDir = join(rcsDir, 'state');
|
|
1718
|
-
const logsDir = join(rcsDir, 'logs');
|
|
1719
|
-
const teamName = 'beta';
|
|
1720
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
1721
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
1722
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
1723
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
1724
|
-
await mkdir(logsDir, { recursive: true });
|
|
1725
|
-
await mkdir(join(teamDir, 'mailbox'), { recursive: true });
|
|
1726
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
1727
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
1728
|
-
active: true,
|
|
1729
|
-
team_name: teamName,
|
|
1730
|
-
current_phase: 'team-exec',
|
|
1731
|
-
});
|
|
1732
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
1733
|
-
name: teamName,
|
|
1734
|
-
tmux_session: 'rcs-team-beta',
|
|
1735
|
-
leader_pane_id: '%92',
|
|
1736
|
-
});
|
|
1737
|
-
// Leader HUD state is stale (last turn 5 minutes ago)
|
|
1738
|
-
await writeJson(join(stateDir, 'hud-state.json'), {
|
|
1739
|
-
last_turn_at: new Date(Date.now() - 300_000).toISOString(),
|
|
1740
|
-
turn_count: 5,
|
|
1741
|
-
});
|
|
1742
|
-
// No mailbox messages — but worker panes alive should trigger nudge
|
|
1743
|
-
await writeFile(fakeTmuxPath, buildFakeTmux(tmuxLogPath));
|
|
1744
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
1745
|
-
const result = runNotifyHook(cwd, fakeBinDir);
|
|
1746
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
1747
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
1748
|
-
assert.match(tmuxLog, /send-keys/);
|
|
1749
|
-
assert.match(tmuxLog, /Team beta:/);
|
|
1750
|
-
assert.match(tmuxLog, /leader stale, \d+ worker pane\(s\) still active\./);
|
|
1751
|
-
assert.match(tmuxLog, /Next: check messages; keep orchestrating; if done, gracefully shut down: rcs team shutdown beta\./);
|
|
1752
|
-
assert.doesNotMatch(tmuxLog, /keep polling/);
|
|
1753
|
-
assert.match(tmuxLog, /\[RCS_TMUX_INJECT\]/, 'should include injection marker');
|
|
1754
|
-
});
|
|
1755
|
-
});
|
|
1756
|
-
it('nudges when team progress is stalled even if timing signals are fresh or missing (fallback threshold)', async () => {
|
|
1757
|
-
await withTempWorkingDir(async (cwd) => {
|
|
1758
|
-
const rcsDir = join(cwd, '.rcs');
|
|
1759
|
-
const stateDir = join(rcsDir, 'state');
|
|
1760
|
-
const logsDir = join(rcsDir, 'logs');
|
|
1761
|
-
const teamName = 'stalled-progress';
|
|
1762
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
1763
|
-
const workersDir = join(teamDir, 'workers');
|
|
1764
|
-
const tasksDir = join(teamDir, 'tasks');
|
|
1765
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
1766
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
1767
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
1768
|
-
const nowIso = new Date().toISOString();
|
|
1769
|
-
await mkdir(logsDir, { recursive: true });
|
|
1770
|
-
await mkdir(tasksDir, { recursive: true });
|
|
1771
|
-
await mkdir(join(workersDir, 'worker-1'), { recursive: true });
|
|
1772
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
1773
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
1774
|
-
active: true,
|
|
1775
|
-
team_name: teamName,
|
|
1776
|
-
current_phase: 'team-exec',
|
|
1777
|
-
});
|
|
1778
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
1779
|
-
name: teamName,
|
|
1780
|
-
tmux_session: 'rcs-team-stalled-progress',
|
|
1781
|
-
leader_pane_id: '%90',
|
|
1782
|
-
workers: [
|
|
1783
|
-
{ name: 'worker-1', index: 1, pane_id: '%10' },
|
|
1784
|
-
{ name: 'worker-2', index: 2, pane_id: '%11' },
|
|
1785
|
-
],
|
|
1786
|
-
});
|
|
1787
|
-
await writeJson(join(stateDir, 'hud-state.json'), {
|
|
1788
|
-
last_turn_at: new Date(Date.now() - 300_000).toISOString(),
|
|
1789
|
-
turn_count: 4,
|
|
1790
|
-
});
|
|
1791
|
-
await writeJson(join(tasksDir, 'task-1.json'), {
|
|
1792
|
-
id: '1',
|
|
1793
|
-
subject: 'Investigate stall',
|
|
1794
|
-
description: 'worker-1 owns the active task',
|
|
1795
|
-
status: 'in_progress',
|
|
1796
|
-
owner: 'worker-1',
|
|
1797
|
-
created_at: nowIso,
|
|
1798
|
-
});
|
|
1799
|
-
await writeJson(join(tasksDir, 'task-2.json'), {
|
|
1800
|
-
id: '2',
|
|
1801
|
-
subject: 'Follow-up',
|
|
1802
|
-
description: 'still pending',
|
|
1803
|
-
status: 'pending',
|
|
1804
|
-
created_at: nowIso,
|
|
1805
|
-
});
|
|
1806
|
-
await writeJson(join(workersDir, 'worker-1', 'status.json'), {
|
|
1807
|
-
state: 'working',
|
|
1808
|
-
current_task_id: '1',
|
|
1809
|
-
updated_at: nowIso,
|
|
1810
|
-
});
|
|
1811
|
-
await writeJson(join(workersDir, 'worker-1', 'heartbeat.json'), {
|
|
1812
|
-
last_turn_at: nowIso,
|
|
1813
|
-
turn_count: 2,
|
|
1814
|
-
alive: true,
|
|
1815
|
-
});
|
|
1816
|
-
const stalledSignature = JSON.stringify({
|
|
1817
|
-
tasks: [
|
|
1818
|
-
{ id: '1', owner: 'worker-1', status: 'in_progress' },
|
|
1819
|
-
{ id: '2', owner: '', status: 'pending' },
|
|
1820
|
-
],
|
|
1821
|
-
workers: [
|
|
1822
|
-
{
|
|
1823
|
-
worker: 'worker-1',
|
|
1824
|
-
state: 'working',
|
|
1825
|
-
current_task_id: '1',
|
|
1826
|
-
status_missing: false,
|
|
1827
|
-
turn_count: 2,
|
|
1828
|
-
heartbeat_missing: false,
|
|
1829
|
-
},
|
|
1830
|
-
{
|
|
1831
|
-
worker: 'worker-2',
|
|
1832
|
-
state: 'unknown',
|
|
1833
|
-
current_task_id: '',
|
|
1834
|
-
status_missing: true,
|
|
1835
|
-
turn_count: null,
|
|
1836
|
-
heartbeat_missing: true,
|
|
1837
|
-
},
|
|
1838
|
-
],
|
|
1839
|
-
});
|
|
1840
|
-
await writeJson(join(stateDir, 'team-leader-nudge.json'), {
|
|
1841
|
-
last_nudged_by_team: {
|
|
1842
|
-
[teamName]: {
|
|
1843
|
-
at: new Date(Date.now() - 5_000).toISOString(),
|
|
1844
|
-
last_message_id: '',
|
|
1845
|
-
reason: 'new_mailbox_message',
|
|
1846
|
-
},
|
|
1847
|
-
},
|
|
1848
|
-
progress_by_team: {
|
|
1849
|
-
[teamName]: {
|
|
1850
|
-
signature: stalledSignature,
|
|
1851
|
-
last_progress_at: new Date(Date.now() - 180_000).toISOString(),
|
|
1852
|
-
},
|
|
1853
|
-
},
|
|
1854
|
-
});
|
|
1855
|
-
await writeFile(fakeTmuxPath, buildFakeTmuxWithListPanes(tmuxLogPath, ['%10 12345', '%11 12346']));
|
|
1856
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
1857
|
-
const result = runNotifyHook(cwd, fakeBinDir, {
|
|
1858
|
-
RCS_TEAM_PROGRESS_STALL_MS: '60000',
|
|
1859
|
-
RCS_TEAM_LEADER_NUDGE_MS: '30000',
|
|
1860
|
-
});
|
|
1861
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
1862
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
1863
|
-
assert.match(tmuxLog, /Team stalled-progress: leader stale, no progress 3m(?:\d+s)?\./);
|
|
1864
|
-
assert.match(tmuxLog, /Next: rcs team status stalled-progress; read worker messages; unblock\/reassign or shutdown\./);
|
|
1865
|
-
assert.doesNotMatch(tmuxLog, /keep polling/);
|
|
1866
|
-
assert.doesNotMatch(tmuxLog, /\[RCS_INTENT:/);
|
|
1867
|
-
assert.match(tmuxLog, /\[RCS_TMUX_INJECT\]/);
|
|
1868
|
-
const eventsPath = join(teamDir, 'events', 'events.ndjson');
|
|
1869
|
-
const events = (await readFile(eventsPath, 'utf-8')).trim().split('\n').map(line => JSON.parse(line));
|
|
1870
|
-
const nudgeEvent = events.find((e) => e.type === 'team_leader_nudge');
|
|
1871
|
-
assert.equal(nudgeEvent?.reason, 'stuck_waiting_on_leader');
|
|
1872
|
-
assert.equal(nudgeEvent?.orchestration_intent, 'stalled-unblock');
|
|
1873
|
-
});
|
|
1874
|
-
});
|
|
1875
|
-
it('nudges when team progress is stalled before the leader becomes stale (fallback threshold)', async () => {
|
|
1876
|
-
await withTempWorkingDir(async (cwd) => {
|
|
1877
|
-
const rcsDir = join(cwd, '.rcs');
|
|
1878
|
-
const stateDir = join(rcsDir, 'state');
|
|
1879
|
-
const logsDir = join(rcsDir, 'logs');
|
|
1880
|
-
const teamName = 'stalled-before-stale';
|
|
1881
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
1882
|
-
const workersDir = join(teamDir, 'workers');
|
|
1883
|
-
const tasksDir = join(teamDir, 'tasks');
|
|
1884
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
1885
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
1886
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
1887
|
-
const nowIso = new Date().toISOString();
|
|
1888
|
-
await mkdir(logsDir, { recursive: true });
|
|
1889
|
-
await mkdir(tasksDir, { recursive: true });
|
|
1890
|
-
await mkdir(join(workersDir, 'worker-1'), { recursive: true });
|
|
1891
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
1892
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
1893
|
-
active: true,
|
|
1894
|
-
team_name: teamName,
|
|
1895
|
-
current_phase: 'team-exec',
|
|
1896
|
-
});
|
|
1897
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
1898
|
-
name: teamName,
|
|
1899
|
-
tmux_session: 'rcs-team-stalled-before-stale',
|
|
1900
|
-
leader_pane_id: '%89',
|
|
1901
|
-
workers: [
|
|
1902
|
-
{ name: 'worker-1', index: 1, pane_id: '%10' },
|
|
1903
|
-
{ name: 'worker-2', index: 2, pane_id: '%11' },
|
|
1904
|
-
],
|
|
1905
|
-
});
|
|
1906
|
-
await writeJson(join(stateDir, 'hud-state.json'), {
|
|
1907
|
-
last_turn_at: nowIso,
|
|
1908
|
-
turn_count: 4,
|
|
1909
|
-
});
|
|
1910
|
-
await writeJson(join(tasksDir, 'task-1.json'), {
|
|
1911
|
-
id: '1',
|
|
1912
|
-
subject: 'Investigate stall',
|
|
1913
|
-
description: 'worker-1 owns the active task',
|
|
1914
|
-
status: 'in_progress',
|
|
1915
|
-
owner: 'worker-1',
|
|
1916
|
-
created_at: nowIso,
|
|
1917
|
-
});
|
|
1918
|
-
await writeJson(join(tasksDir, 'task-2.json'), {
|
|
1919
|
-
id: '2',
|
|
1920
|
-
subject: 'Follow-up',
|
|
1921
|
-
description: 'still pending',
|
|
1922
|
-
status: 'pending',
|
|
1923
|
-
created_at: nowIso,
|
|
1924
|
-
});
|
|
1925
|
-
await writeJson(join(workersDir, 'worker-1', 'status.json'), {
|
|
1926
|
-
state: 'working',
|
|
1927
|
-
current_task_id: '1',
|
|
1928
|
-
updated_at: nowIso,
|
|
1929
|
-
});
|
|
1930
|
-
await writeJson(join(workersDir, 'worker-1', 'heartbeat.json'), {
|
|
1931
|
-
last_turn_at: nowIso,
|
|
1932
|
-
turn_count: 2,
|
|
1933
|
-
alive: true,
|
|
1934
|
-
});
|
|
1935
|
-
const stalledSignature = JSON.stringify({
|
|
1936
|
-
tasks: [
|
|
1937
|
-
{ id: '1', owner: 'worker-1', status: 'in_progress' },
|
|
1938
|
-
{ id: '2', owner: '', status: 'pending' },
|
|
1939
|
-
],
|
|
1940
|
-
workers: [
|
|
1941
|
-
{
|
|
1942
|
-
worker: 'worker-1',
|
|
1943
|
-
state: 'working',
|
|
1944
|
-
current_task_id: '1',
|
|
1945
|
-
status_missing: false,
|
|
1946
|
-
turn_count: 2,
|
|
1947
|
-
heartbeat_missing: false,
|
|
1948
|
-
},
|
|
1949
|
-
{
|
|
1950
|
-
worker: 'worker-2',
|
|
1951
|
-
state: 'unknown',
|
|
1952
|
-
current_task_id: '',
|
|
1953
|
-
status_missing: true,
|
|
1954
|
-
turn_count: null,
|
|
1955
|
-
heartbeat_missing: true,
|
|
1956
|
-
},
|
|
1957
|
-
],
|
|
1958
|
-
});
|
|
1959
|
-
await writeJson(join(stateDir, 'team-leader-nudge.json'), {
|
|
1960
|
-
last_nudged_by_team: {
|
|
1961
|
-
[teamName]: {
|
|
1962
|
-
at: new Date(Date.now() - 5_000).toISOString(),
|
|
1963
|
-
last_message_id: '',
|
|
1964
|
-
reason: 'new_mailbox_message',
|
|
1965
|
-
},
|
|
1966
|
-
},
|
|
1967
|
-
progress_by_team: {
|
|
1968
|
-
[teamName]: {
|
|
1969
|
-
signature: stalledSignature,
|
|
1970
|
-
last_progress_at: new Date(Date.now() - 180_000).toISOString(),
|
|
1971
|
-
},
|
|
1972
|
-
},
|
|
1973
|
-
});
|
|
1974
|
-
await writeFile(fakeTmuxPath, buildFakeTmuxWithListPanes(tmuxLogPath, ['%10 12345', '%11 12346']));
|
|
1975
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
1976
|
-
const result = runNotifyHook(cwd, fakeBinDir, {
|
|
1977
|
-
RCS_TEAM_PROGRESS_STALL_MS: '60000',
|
|
1978
|
-
RCS_TEAM_LEADER_NUDGE_MS: '30000',
|
|
1979
|
-
RCS_TEAM_LEADER_STALE_MS: '60000',
|
|
1980
|
-
});
|
|
1981
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
1982
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
1983
|
-
assert.match(tmuxLog, /Team stalled-before-stale: worker panes stalled, no progress 3m(?:\d+s)?\./);
|
|
1984
|
-
assert.match(tmuxLog, /Next: rcs team status stalled-before-stale; read worker messages; unblock\/reassign or shutdown\./);
|
|
1985
|
-
assert.doesNotMatch(tmuxLog, /keep polling/);
|
|
1986
|
-
assert.doesNotMatch(tmuxLog, /leader stale/);
|
|
1987
|
-
const eventsPath = join(teamDir, 'events', 'events.ndjson');
|
|
1988
|
-
const events = (await readFile(eventsPath, 'utf-8')).trim().split('\n').map(line => JSON.parse(line));
|
|
1989
|
-
const nudgeEvent = events.find((e) => e.type === 'team_leader_nudge');
|
|
1990
|
-
assert.equal(nudgeEvent?.reason, 'stuck_waiting_on_leader');
|
|
1991
|
-
});
|
|
1992
|
-
});
|
|
1993
|
-
it('nudges stalled team after configurable worker turn stall window elapses (primary threshold)', async () => {
|
|
1994
|
-
await withTempWorkingDir(async (cwd) => {
|
|
1995
|
-
const rcsDir = join(cwd, '.rcs');
|
|
1996
|
-
const stateDir = join(rcsDir, 'state');
|
|
1997
|
-
const logsDir = join(rcsDir, 'logs');
|
|
1998
|
-
const teamName = 'worker-turn-stall-threshold';
|
|
1999
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
2000
|
-
const workersDir = join(teamDir, 'workers');
|
|
2001
|
-
const tasksDir = join(teamDir, 'tasks');
|
|
2002
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
2003
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
2004
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
2005
|
-
const nowIso = new Date().toISOString();
|
|
2006
|
-
await mkdir(logsDir, { recursive: true });
|
|
2007
|
-
await mkdir(tasksDir, { recursive: true });
|
|
2008
|
-
await mkdir(join(workersDir, 'worker-1'), { recursive: true });
|
|
2009
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
2010
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
2011
|
-
active: true,
|
|
2012
|
-
team_name: teamName,
|
|
2013
|
-
current_phase: 'team-exec',
|
|
2014
|
-
});
|
|
2015
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
2016
|
-
name: teamName,
|
|
2017
|
-
tmux_session: 'rcs-team-worker-turn-stall-threshold',
|
|
2018
|
-
leader_pane_id: '%86',
|
|
2019
|
-
workers: [
|
|
2020
|
-
{ name: 'worker-1', index: 1, pane_id: '%10' },
|
|
2021
|
-
],
|
|
2022
|
-
});
|
|
2023
|
-
await writeJson(join(stateDir, 'hud-state.json'), {
|
|
2024
|
-
last_turn_at: nowIso,
|
|
2025
|
-
turn_count: 4,
|
|
2026
|
-
});
|
|
2027
|
-
await writeJson(join(tasksDir, 'task-1.json'), {
|
|
2028
|
-
id: '1',
|
|
2029
|
-
subject: 'Investigate stall',
|
|
2030
|
-
description: 'worker-1 owns the active task',
|
|
2031
|
-
status: 'in_progress',
|
|
2032
|
-
owner: 'worker-1',
|
|
2033
|
-
created_at: nowIso,
|
|
2034
|
-
});
|
|
2035
|
-
await writeJson(join(workersDir, 'worker-1', 'status.json'), {
|
|
2036
|
-
state: 'working',
|
|
2037
|
-
current_task_id: '1',
|
|
2038
|
-
updated_at: nowIso,
|
|
2039
|
-
});
|
|
2040
|
-
await writeJson(join(workersDir, 'worker-1', 'heartbeat.json'), {
|
|
2041
|
-
last_turn_at: nowIso,
|
|
2042
|
-
turn_count: 5,
|
|
2043
|
-
alive: true,
|
|
2044
|
-
});
|
|
2045
|
-
const previousSignature = JSON.stringify({
|
|
2046
|
-
tasks: [
|
|
2047
|
-
{ id: '1', owner: 'worker-1', status: 'in_progress' },
|
|
2048
|
-
],
|
|
2049
|
-
workers: [
|
|
2050
|
-
{
|
|
2051
|
-
worker: 'worker-1',
|
|
2052
|
-
state: 'working',
|
|
2053
|
-
current_task_id: '1',
|
|
2054
|
-
status_missing: false,
|
|
2055
|
-
turn_count: 5,
|
|
2056
|
-
heartbeat_missing: false,
|
|
2057
|
-
},
|
|
2058
|
-
],
|
|
2059
|
-
});
|
|
2060
|
-
await writeJson(join(stateDir, 'team-leader-nudge.json'), {
|
|
2061
|
-
last_nudged_by_team: {
|
|
2062
|
-
[teamName]: {
|
|
2063
|
-
at: new Date(Date.now() - 60_000).toISOString(),
|
|
2064
|
-
last_message_id: '',
|
|
2065
|
-
reason: 'new_mailbox_message',
|
|
2066
|
-
},
|
|
2067
|
-
},
|
|
2068
|
-
progress_by_team: {
|
|
2069
|
-
[teamName]: {
|
|
2070
|
-
signature: previousSignature,
|
|
2071
|
-
last_progress_at: new Date(Date.now() - 45_000).toISOString(),
|
|
2072
|
-
},
|
|
2073
|
-
},
|
|
2074
|
-
});
|
|
2075
|
-
await writeFile(fakeTmuxPath, buildFakeTmuxWithListPanes(tmuxLogPath, ['%10 12345']));
|
|
2076
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
2077
|
-
const result = runNotifyHook(cwd, fakeBinDir, {
|
|
2078
|
-
RCS_TEAM_PROGRESS_STALL_MS: '120000',
|
|
2079
|
-
RCS_TEAM_WORKER_TURN_STALL_MS: '30000',
|
|
2080
|
-
RCS_TEAM_LEADER_NUDGE_MS: '30000',
|
|
2081
|
-
RCS_TEAM_LEADER_STALE_MS: '60000',
|
|
2082
|
-
});
|
|
2083
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
2084
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
2085
|
-
assert.match(tmuxLog, /Team worker-turn-stall-threshold: worker panes stalled, no progress 45s\./);
|
|
2086
|
-
});
|
|
2087
|
-
});
|
|
2088
|
-
it('does not nudge stalled team when an in-progress worker is still advancing heartbeat turns', async () => {
|
|
2089
|
-
await withTempWorkingDir(async (cwd) => {
|
|
2090
|
-
const rcsDir = join(cwd, '.rcs');
|
|
2091
|
-
const stateDir = join(rcsDir, 'state');
|
|
2092
|
-
const logsDir = join(rcsDir, 'logs');
|
|
2093
|
-
const teamName = 'active-turns-no-stall';
|
|
2094
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
2095
|
-
const workersDir = join(teamDir, 'workers');
|
|
2096
|
-
const tasksDir = join(teamDir, 'tasks');
|
|
2097
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
2098
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
2099
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
2100
|
-
const nowIso = new Date().toISOString();
|
|
2101
|
-
await mkdir(logsDir, { recursive: true });
|
|
2102
|
-
await mkdir(tasksDir, { recursive: true });
|
|
2103
|
-
await mkdir(join(workersDir, 'worker-1'), { recursive: true });
|
|
2104
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
2105
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
2106
|
-
active: true,
|
|
2107
|
-
team_name: teamName,
|
|
2108
|
-
current_phase: 'team-exec',
|
|
2109
|
-
});
|
|
2110
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
2111
|
-
name: teamName,
|
|
2112
|
-
tmux_session: 'rcs-team-active-turns-no-stall',
|
|
2113
|
-
leader_pane_id: '%87',
|
|
2114
|
-
workers: [
|
|
2115
|
-
{ name: 'worker-1', index: 1, pane_id: '%10' },
|
|
2116
|
-
{ name: 'worker-2', index: 2, pane_id: '%11' },
|
|
2117
|
-
],
|
|
2118
|
-
});
|
|
2119
|
-
await writeJson(join(stateDir, 'hud-state.json'), {
|
|
2120
|
-
last_turn_at: nowIso,
|
|
2121
|
-
turn_count: 4,
|
|
2122
|
-
});
|
|
2123
|
-
await writeJson(join(tasksDir, 'task-1.json'), {
|
|
2124
|
-
id: '1',
|
|
2125
|
-
subject: 'Investigate stall',
|
|
2126
|
-
description: 'worker-1 owns the active task',
|
|
2127
|
-
status: 'in_progress',
|
|
2128
|
-
owner: 'worker-1',
|
|
2129
|
-
created_at: nowIso,
|
|
2130
|
-
});
|
|
2131
|
-
await writeJson(join(tasksDir, 'task-2.json'), {
|
|
2132
|
-
id: '2',
|
|
2133
|
-
subject: 'Follow-up',
|
|
2134
|
-
description: 'still pending',
|
|
2135
|
-
status: 'pending',
|
|
2136
|
-
created_at: nowIso,
|
|
2137
|
-
});
|
|
2138
|
-
await writeJson(join(workersDir, 'worker-1', 'status.json'), {
|
|
2139
|
-
state: 'working',
|
|
2140
|
-
current_task_id: '1',
|
|
2141
|
-
updated_at: nowIso,
|
|
2142
|
-
});
|
|
2143
|
-
await writeJson(join(workersDir, 'worker-1', 'heartbeat.json'), {
|
|
2144
|
-
last_turn_at: nowIso,
|
|
2145
|
-
turn_count: 8,
|
|
2146
|
-
alive: true,
|
|
2147
|
-
});
|
|
2148
|
-
const previousSignature = JSON.stringify({
|
|
2149
|
-
tasks: [
|
|
2150
|
-
{ id: '1', owner: 'worker-1', status: 'in_progress' },
|
|
2151
|
-
{ id: '2', owner: '', status: 'pending' },
|
|
2152
|
-
],
|
|
2153
|
-
workers: [
|
|
2154
|
-
{
|
|
2155
|
-
worker: 'worker-1',
|
|
2156
|
-
state: 'working',
|
|
2157
|
-
current_task_id: '1',
|
|
2158
|
-
status_missing: false,
|
|
2159
|
-
turn_count: 2,
|
|
2160
|
-
heartbeat_missing: false,
|
|
2161
|
-
},
|
|
2162
|
-
{
|
|
2163
|
-
worker: 'worker-2',
|
|
2164
|
-
state: 'unknown',
|
|
2165
|
-
current_task_id: '',
|
|
2166
|
-
status_missing: true,
|
|
2167
|
-
turn_count: null,
|
|
2168
|
-
heartbeat_missing: true,
|
|
2169
|
-
},
|
|
2170
|
-
]
|
|
2171
|
-
});
|
|
2172
|
-
await writeJson(join(stateDir, 'team-leader-nudge.json'), {
|
|
2173
|
-
last_nudged_by_team: {
|
|
2174
|
-
[teamName]: {
|
|
2175
|
-
at: new Date(Date.now() - 5_000).toISOString(),
|
|
2176
|
-
last_message_id: '',
|
|
2177
|
-
reason: 'new_mailbox_message',
|
|
2178
|
-
},
|
|
2179
|
-
},
|
|
2180
|
-
progress_by_team: {
|
|
2181
|
-
[teamName]: {
|
|
2182
|
-
signature: previousSignature,
|
|
2183
|
-
last_progress_at: new Date(Date.now() - 180_000).toISOString(),
|
|
2184
|
-
},
|
|
2185
|
-
},
|
|
2186
|
-
});
|
|
2187
|
-
await writeFile(fakeTmuxPath, buildFakeTmuxWithListPanes(tmuxLogPath, ['%10 12345', '%11 12346']));
|
|
2188
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
2189
|
-
const result = runNotifyHook(cwd, fakeBinDir, {
|
|
2190
|
-
RCS_TEAM_PROGRESS_STALL_MS: '60000',
|
|
2191
|
-
RCS_TEAM_LEADER_NUDGE_MS: '30000',
|
|
2192
|
-
RCS_TEAM_LEADER_STALE_MS: '60000',
|
|
2193
|
-
});
|
|
2194
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
2195
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
2196
|
-
assert.doesNotMatch(tmuxLog, /active-turns-no-stall: worker panes stalled, no progress/);
|
|
2197
|
-
assert.doesNotMatch(tmuxLog, /active-turns-no-stall: leader stale, no progress/);
|
|
2198
|
-
});
|
|
2199
|
-
});
|
|
2200
|
-
it('bounds repeated stalled-team nudges before leader stale by cooldown', async () => {
|
|
2201
|
-
await withTempWorkingDir(async (cwd) => {
|
|
2202
|
-
const rcsDir = join(cwd, '.rcs');
|
|
2203
|
-
const stateDir = join(rcsDir, 'state');
|
|
2204
|
-
const logsDir = join(rcsDir, 'logs');
|
|
2205
|
-
const teamName = 'stalled-before-stale-bounded';
|
|
2206
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
2207
|
-
const workersDir = join(teamDir, 'workers');
|
|
2208
|
-
const tasksDir = join(teamDir, 'tasks');
|
|
2209
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
2210
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
2211
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
2212
|
-
const nowIso = new Date().toISOString();
|
|
2213
|
-
await mkdir(logsDir, { recursive: true });
|
|
2214
|
-
await mkdir(tasksDir, { recursive: true });
|
|
2215
|
-
await mkdir(join(workersDir, 'worker-1'), { recursive: true });
|
|
2216
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
2217
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
2218
|
-
active: true,
|
|
2219
|
-
team_name: teamName,
|
|
2220
|
-
current_phase: 'team-exec',
|
|
2221
|
-
});
|
|
2222
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
2223
|
-
name: teamName,
|
|
2224
|
-
tmux_session: 'rcs-team-stalled-before-stale-bounded',
|
|
2225
|
-
leader_pane_id: '%88',
|
|
2226
|
-
workers: [
|
|
2227
|
-
{ name: 'worker-1', index: 1, pane_id: '%10' },
|
|
2228
|
-
{ name: 'worker-2', index: 2, pane_id: '%11' },
|
|
2229
|
-
],
|
|
2230
|
-
});
|
|
2231
|
-
await writeJson(join(stateDir, 'hud-state.json'), {
|
|
2232
|
-
last_turn_at: nowIso,
|
|
2233
|
-
turn_count: 4,
|
|
2234
|
-
});
|
|
2235
|
-
await writeJson(join(tasksDir, 'task-1.json'), {
|
|
2236
|
-
id: '1',
|
|
2237
|
-
subject: 'Investigate stall',
|
|
2238
|
-
description: 'worker-1 owns the active task',
|
|
2239
|
-
status: 'in_progress',
|
|
2240
|
-
owner: 'worker-1',
|
|
2241
|
-
created_at: nowIso,
|
|
2242
|
-
});
|
|
2243
|
-
await writeJson(join(tasksDir, 'task-2.json'), {
|
|
2244
|
-
id: '2',
|
|
2245
|
-
subject: 'Follow-up',
|
|
2246
|
-
description: 'still pending',
|
|
2247
|
-
status: 'pending',
|
|
2248
|
-
created_at: nowIso,
|
|
2249
|
-
});
|
|
2250
|
-
await writeJson(join(workersDir, 'worker-1', 'status.json'), {
|
|
2251
|
-
state: 'working',
|
|
2252
|
-
current_task_id: '1',
|
|
2253
|
-
updated_at: nowIso,
|
|
2254
|
-
});
|
|
2255
|
-
await writeJson(join(workersDir, 'worker-1', 'heartbeat.json'), {
|
|
2256
|
-
last_turn_at: nowIso,
|
|
2257
|
-
turn_count: 2,
|
|
2258
|
-
alive: true,
|
|
2259
|
-
});
|
|
2260
|
-
const stalledSignature = JSON.stringify({
|
|
2261
|
-
tasks: [
|
|
2262
|
-
{ id: '1', owner: 'worker-1', status: 'in_progress' },
|
|
2263
|
-
{ id: '2', owner: '', status: 'pending' },
|
|
2264
|
-
],
|
|
2265
|
-
workers: [
|
|
2266
|
-
{
|
|
2267
|
-
worker: 'worker-1',
|
|
2268
|
-
state: 'working',
|
|
2269
|
-
current_task_id: '1',
|
|
2270
|
-
status_missing: false,
|
|
2271
|
-
turn_count: 2,
|
|
2272
|
-
heartbeat_missing: false,
|
|
2273
|
-
},
|
|
2274
|
-
{
|
|
2275
|
-
worker: 'worker-2',
|
|
2276
|
-
state: 'unknown',
|
|
2277
|
-
current_task_id: '',
|
|
2278
|
-
status_missing: true,
|
|
2279
|
-
turn_count: null,
|
|
2280
|
-
heartbeat_missing: true,
|
|
2281
|
-
},
|
|
2282
|
-
],
|
|
2283
|
-
});
|
|
2284
|
-
await writeJson(join(stateDir, 'team-leader-nudge.json'), {
|
|
2285
|
-
progress_by_team: {
|
|
2286
|
-
[teamName]: {
|
|
2287
|
-
signature: stalledSignature,
|
|
2288
|
-
last_progress_at: new Date(Date.now() - 180_000).toISOString(),
|
|
2289
|
-
},
|
|
2290
|
-
},
|
|
2291
|
-
});
|
|
2292
|
-
await writeFile(fakeTmuxPath, buildFakeTmuxWithListPanes(tmuxLogPath, ['%10 12345', '%11 12346']));
|
|
2293
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
2294
|
-
const first = runNotifyHook(cwd, fakeBinDir, {
|
|
2295
|
-
RCS_TEAM_PROGRESS_STALL_MS: '60000',
|
|
2296
|
-
RCS_TEAM_LEADER_NUDGE_MS: '30000',
|
|
2297
|
-
RCS_TEAM_LEADER_STALE_MS: '60000',
|
|
2298
|
-
});
|
|
2299
|
-
assert.equal(first.status, 0, `notify-hook failed: ${first.stderr || first.stdout}`);
|
|
2300
|
-
const second = runNotifyHook(cwd, fakeBinDir, {
|
|
2301
|
-
RCS_TEAM_PROGRESS_STALL_MS: '60000',
|
|
2302
|
-
RCS_TEAM_LEADER_NUDGE_MS: '30000',
|
|
2303
|
-
RCS_TEAM_LEADER_STALE_MS: '60000',
|
|
2304
|
-
});
|
|
2305
|
-
assert.equal(second.status, 0, `notify-hook failed: ${second.stderr || second.stdout}`);
|
|
2306
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
2307
|
-
const sends = tmuxLog.match(/send-keys -t %88 -l Team stalled-before-stale-bounded: worker panes stalled, no progress/g) || [];
|
|
2308
|
-
assert.equal(sends.length, 1, 'cooldown should keep repeated stalled-team nudges bounded');
|
|
2309
|
-
assert.match(tmuxLog, /Next: rcs team status stalled-before-stale-bounded; read worker messages; unblock\/reassign or shutdown\./);
|
|
2310
|
-
});
|
|
2311
|
-
});
|
|
2312
|
-
it('does not treat leader and HUD panes as active worker panes when worker pane ids are known', async () => {
|
|
2313
|
-
await withTempWorkingDir(async (cwd) => {
|
|
2314
|
-
const rcsDir = join(cwd, '.rcs');
|
|
2315
|
-
const stateDir = join(rcsDir, 'state');
|
|
2316
|
-
const logsDir = join(rcsDir, 'logs');
|
|
2317
|
-
const teamName = 'stale-no-workers';
|
|
2318
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
2319
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
2320
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
2321
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
2322
|
-
await mkdir(logsDir, { recursive: true });
|
|
2323
|
-
await mkdir(join(teamDir, 'mailbox'), { recursive: true });
|
|
2324
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
2325
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
2326
|
-
active: true,
|
|
2327
|
-
team_name: teamName,
|
|
2328
|
-
current_phase: 'team-exec',
|
|
2329
|
-
});
|
|
2330
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
2331
|
-
name: teamName,
|
|
2332
|
-
tmux_session: 'rcs-team-stale-no-workers',
|
|
2333
|
-
leader_pane_id: '%92',
|
|
2334
|
-
hud_pane_id: '%93',
|
|
2335
|
-
workers: [
|
|
2336
|
-
{ name: 'worker-1', index: 1, pane_id: '%10' },
|
|
2337
|
-
{ name: 'worker-2', index: 2, pane_id: '%11' },
|
|
2338
|
-
],
|
|
2339
|
-
});
|
|
2340
|
-
await writeJson(join(stateDir, 'hud-state.json'), {
|
|
2341
|
-
last_turn_at: new Date(Date.now() - 300_000).toISOString(),
|
|
2342
|
-
turn_count: 5,
|
|
2343
|
-
});
|
|
2344
|
-
await writeFile(fakeTmuxPath, buildFakeTmuxWithListPanes(tmuxLogPath, ['%92 12345', '%93 12346']));
|
|
2345
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
2346
|
-
const result = runNotifyHook(cwd, fakeBinDir);
|
|
2347
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
2348
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
2349
|
-
assert.doesNotMatch(tmuxLog, /send-keys -t %92 -l Team stale-no-workers: leader stale,/);
|
|
2350
|
-
});
|
|
2351
|
-
});
|
|
2352
|
-
it('does not send a generic periodic leader nudge when the leader is not stale', async () => {
|
|
2353
|
-
await withTempWorkingDir(async (cwd) => {
|
|
2354
|
-
const rcsDir = join(cwd, '.rcs');
|
|
2355
|
-
const stateDir = join(rcsDir, 'state');
|
|
2356
|
-
const logsDir = join(rcsDir, 'logs');
|
|
2357
|
-
const teamName = 'fresh-leader';
|
|
2358
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
2359
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
2360
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
2361
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
2362
|
-
await mkdir(logsDir, { recursive: true });
|
|
2363
|
-
await mkdir(join(teamDir, 'mailbox'), { recursive: true });
|
|
2364
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
2365
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
2366
|
-
active: true,
|
|
2367
|
-
team_name: teamName,
|
|
2368
|
-
current_phase: 'team-exec',
|
|
2369
|
-
});
|
|
2370
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
2371
|
-
name: teamName,
|
|
2372
|
-
tmux_session: 'rcs-team-fresh',
|
|
2373
|
-
leader_pane_id: '%95',
|
|
2374
|
-
});
|
|
2375
|
-
await writeJson(join(stateDir, 'hud-state.json'), {
|
|
2376
|
-
last_turn_at: new Date().toISOString(),
|
|
2377
|
-
turn_count: 2,
|
|
2378
|
-
});
|
|
2379
|
-
await writeFile(fakeTmuxPath, buildFakeTmux(tmuxLogPath));
|
|
2380
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
2381
|
-
const result = runNotifyHook(cwd, fakeBinDir, { RCS_TEAM_LEADER_NUDGE_MS: '30000' });
|
|
2382
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
2383
|
-
if (existsSync(tmuxLogPath)) {
|
|
2384
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
2385
|
-
assert.doesNotMatch(tmuxLog, /Team fresh-leader/, 'non-stale leader should not receive generic periodic follow-up');
|
|
2386
|
-
}
|
|
2387
|
-
});
|
|
2388
|
-
});
|
|
2389
|
-
it('uses a 30s cadence for stale leader follow-up nudges', async () => {
|
|
2390
|
-
await withTempWorkingDir(async (cwd) => {
|
|
2391
|
-
const rcsDir = join(cwd, '.rcs');
|
|
2392
|
-
const stateDir = join(rcsDir, 'state');
|
|
2393
|
-
const logsDir = join(rcsDir, 'logs');
|
|
2394
|
-
const teamName = 'stale-cadence';
|
|
2395
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
2396
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
2397
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
2398
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
2399
|
-
const now = Date.now();
|
|
2400
|
-
await mkdir(logsDir, { recursive: true });
|
|
2401
|
-
await mkdir(join(teamDir, 'mailbox'), { recursive: true });
|
|
2402
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
2403
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
2404
|
-
active: true,
|
|
2405
|
-
team_name: teamName,
|
|
2406
|
-
current_phase: 'team-exec',
|
|
2407
|
-
});
|
|
2408
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
2409
|
-
name: teamName,
|
|
2410
|
-
tmux_session: 'rcs-team-stale-cadence',
|
|
2411
|
-
leader_pane_id: '%96',
|
|
2412
|
-
});
|
|
2413
|
-
const staleHud = {
|
|
2414
|
-
last_turn_at: new Date(now - 300_000).toISOString(),
|
|
2415
|
-
turn_count: 5,
|
|
2416
|
-
};
|
|
2417
|
-
await writeJson(join(stateDir, 'hud-state.json'), staleHud);
|
|
2418
|
-
await writeJson(join(stateDir, 'team-leader-nudge.json'), {
|
|
2419
|
-
last_nudged_by_team: {
|
|
2420
|
-
[teamName]: {
|
|
2421
|
-
at: new Date(now - 20_000).toISOString(),
|
|
2422
|
-
last_message_id: '',
|
|
2423
|
-
},
|
|
2424
|
-
},
|
|
2425
|
-
});
|
|
2426
|
-
await writeFile(fakeTmuxPath, buildFakeTmux(tmuxLogPath));
|
|
2427
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
2428
|
-
const blocked = runNotifyHook(cwd, fakeBinDir, { RCS_TEAM_LEADER_NUDGE_MS: '30000' });
|
|
2429
|
-
assert.equal(blocked.status, 0, `notify-hook failed: ${blocked.stderr || blocked.stdout}`);
|
|
2430
|
-
if (existsSync(tmuxLogPath)) {
|
|
2431
|
-
const firstLog = await readFile(tmuxLogPath, 'utf-8');
|
|
2432
|
-
assert.doesNotMatch(firstLog, /Team stale-cadence:/, 'stale follow-up should be blocked inside the 30s window');
|
|
2433
|
-
}
|
|
2434
|
-
await writeJson(join(stateDir, 'hud-state.json'), staleHud);
|
|
2435
|
-
await writeJson(join(stateDir, 'team-leader-nudge.json'), {
|
|
2436
|
-
last_nudged_by_team: {
|
|
2437
|
-
[teamName]: {
|
|
2438
|
-
at: new Date(now - 31_000).toISOString(),
|
|
2439
|
-
last_message_id: '',
|
|
2440
|
-
},
|
|
2441
|
-
},
|
|
2442
|
-
});
|
|
2443
|
-
const allowed = runNotifyHook(cwd, fakeBinDir, { RCS_TEAM_LEADER_NUDGE_MS: '30000' });
|
|
2444
|
-
assert.equal(allowed.status, 0, `notify-hook failed: ${allowed.stderr || allowed.stdout}`);
|
|
2445
|
-
const finalLog = await readFile(tmuxLogPath, 'utf-8');
|
|
2446
|
-
assert.match(finalLog, /Team stale-cadence:/);
|
|
2447
|
-
assert.match(finalLog, /Team stale-cadence: leader stale, \d+ worker pane\(s\) still active\./);
|
|
2448
|
-
});
|
|
2449
|
-
});
|
|
2450
|
-
it('suppresses stale leader follow-up when detached worktree progress is still fresh', async () => {
|
|
2451
|
-
await withTempWorkingDir(async (cwd) => {
|
|
2452
|
-
const rcsDir = join(cwd, '.rcs');
|
|
2453
|
-
const stateDir = join(rcsDir, 'state');
|
|
2454
|
-
const logsDir = join(rcsDir, 'logs');
|
|
2455
|
-
const teamName = 'fresh-detached-progress';
|
|
2456
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
2457
|
-
const workersDir = join(teamDir, 'workers');
|
|
2458
|
-
const tasksDir = join(teamDir, 'tasks');
|
|
2459
|
-
const workerWorktree = join(cwd, 'worktrees', 'worker-1');
|
|
2460
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
2461
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
2462
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
2463
|
-
await mkdir(logsDir, { recursive: true });
|
|
2464
|
-
await mkdir(tasksDir, { recursive: true });
|
|
2465
|
-
await mkdir(join(workersDir, 'worker-1'), { recursive: true });
|
|
2466
|
-
await mkdir(join(workerWorktree, '.rcs', 'state'), { recursive: true });
|
|
2467
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
2468
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
2469
|
-
active: true,
|
|
2470
|
-
team_name: teamName,
|
|
2471
|
-
current_phase: 'team-exec',
|
|
2472
|
-
});
|
|
2473
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
2474
|
-
name: teamName,
|
|
2475
|
-
tmux_session: 'rcs-team-fresh-detached-progress',
|
|
2476
|
-
leader_pane_id: '%99',
|
|
2477
|
-
workers: [
|
|
2478
|
-
{ name: 'worker-1', index: 1, pane_id: '%10', worktree_path: workerWorktree },
|
|
2479
|
-
],
|
|
2480
|
-
});
|
|
2481
|
-
await writeJson(join(stateDir, 'hud-state.json'), {
|
|
2482
|
-
last_turn_at: new Date(Date.now() - 300_000).toISOString(),
|
|
2483
|
-
turn_count: 5,
|
|
2484
|
-
});
|
|
2485
|
-
await writeJson(join(tasksDir, 'task-1.json'), {
|
|
2486
|
-
id: '1',
|
|
2487
|
-
subject: 'Apply detached fix',
|
|
2488
|
-
description: 'keep going',
|
|
2489
|
-
status: 'in_progress',
|
|
2490
|
-
owner: 'worker-1',
|
|
2491
|
-
created_at: new Date(Date.now() - 300_000).toISOString(),
|
|
2492
|
-
});
|
|
2493
|
-
await writeJson(join(workersDir, 'worker-1', 'status.json'), {
|
|
2494
|
-
state: 'working',
|
|
2495
|
-
current_task_id: '1',
|
|
2496
|
-
updated_at: new Date(Date.now() - 300_000).toISOString(),
|
|
2497
|
-
});
|
|
2498
|
-
await writeJson(join(workersDir, 'worker-1', 'heartbeat.json'), {
|
|
2499
|
-
last_turn_at: new Date(Date.now() - 300_000).toISOString(),
|
|
2500
|
-
turn_count: 2,
|
|
2501
|
-
alive: true,
|
|
2502
|
-
});
|
|
2503
|
-
await writeJson(join(stateDir, 'team-leader-nudge.json'), {
|
|
2504
|
-
last_nudged_by_team: {
|
|
2505
|
-
[teamName]: {
|
|
2506
|
-
at: new Date(Date.now() - 60_000).toISOString(),
|
|
2507
|
-
last_message_id: '',
|
|
2508
|
-
reason: 'stale_leader_panes_alive',
|
|
2509
|
-
},
|
|
2510
|
-
},
|
|
2511
|
-
progress_by_team: {
|
|
2512
|
-
[teamName]: {
|
|
2513
|
-
signature: JSON.stringify({
|
|
2514
|
-
tasks: [{ id: '1', owner: 'worker-1', status: 'in_progress' }],
|
|
2515
|
-
workers: [{
|
|
2516
|
-
worker: 'worker-1',
|
|
2517
|
-
state: 'working',
|
|
2518
|
-
current_task_id: '1',
|
|
2519
|
-
status_missing: false,
|
|
2520
|
-
turn_count: 2,
|
|
2521
|
-
heartbeat_missing: false,
|
|
2522
|
-
}],
|
|
2523
|
-
}),
|
|
2524
|
-
last_progress_at: new Date(Date.now() - 300_000).toISOString(),
|
|
2525
|
-
},
|
|
2526
|
-
},
|
|
2527
|
-
});
|
|
2528
|
-
await writeJson(join(workerWorktree, '.rcs', 'state', 'current-task-baseline.json'), {
|
|
2529
|
-
version: 1,
|
|
2530
|
-
tasks: [],
|
|
2531
|
-
});
|
|
2532
|
-
await writeFile(fakeTmuxPath, buildFakeTmuxWithListPanes(tmuxLogPath, ['%10 12345']));
|
|
2533
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
2534
|
-
const result = runNotifyHook(cwd, fakeBinDir, {
|
|
2535
|
-
RCS_TEAM_PROGRESS_STALL_MS: '60000',
|
|
2536
|
-
RCS_TEAM_LEADER_NUDGE_MS: '30000',
|
|
2537
|
-
RCS_TEAM_LEADER_STALE_MS: '60000',
|
|
2538
|
-
});
|
|
2539
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
2540
|
-
if (existsSync(tmuxLogPath)) {
|
|
2541
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
2542
|
-
assert.doesNotMatch(tmuxLog, /Team fresh-detached-progress: leader stale,/);
|
|
2543
|
-
}
|
|
2544
|
-
});
|
|
2545
|
-
});
|
|
2546
|
-
it('emits team_leader_nudge event to events.ndjson when nudge fires', async () => {
|
|
2547
|
-
await withTempWorkingDir(async (cwd) => {
|
|
2548
|
-
const rcsDir = join(cwd, '.rcs');
|
|
2549
|
-
const stateDir = join(rcsDir, 'state');
|
|
2550
|
-
const logsDir = join(rcsDir, 'logs');
|
|
2551
|
-
const teamName = 'gamma';
|
|
2552
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
2553
|
-
const eventsDir = join(teamDir, 'events');
|
|
2554
|
-
const mailboxDir = join(teamDir, 'mailbox');
|
|
2555
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
2556
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
2557
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
2558
|
-
await mkdir(logsDir, { recursive: true });
|
|
2559
|
-
await mkdir(eventsDir, { recursive: true });
|
|
2560
|
-
await mkdir(mailboxDir, { recursive: true });
|
|
2561
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
2562
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
2563
|
-
active: true,
|
|
2564
|
-
team_name: teamName,
|
|
2565
|
-
current_phase: 'team-exec',
|
|
2566
|
-
});
|
|
2567
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
2568
|
-
name: teamName,
|
|
2569
|
-
tmux_session: 'rcs-team-gamma',
|
|
2570
|
-
leader_pane_id: '%93',
|
|
2571
|
-
});
|
|
2572
|
-
await writeJson(join(mailboxDir, 'leader-fixed.json'), {
|
|
2573
|
-
worker: 'leader-fixed',
|
|
2574
|
-
messages: [
|
|
2575
|
-
{
|
|
2576
|
-
message_id: 'msg-99',
|
|
2577
|
-
from_worker: 'worker-1',
|
|
2578
|
-
to_worker: 'leader-fixed',
|
|
2579
|
-
body: 'Task complete',
|
|
2580
|
-
created_at: '2026-02-14T00:00:00.000Z',
|
|
2581
|
-
},
|
|
2582
|
-
],
|
|
2583
|
-
});
|
|
2584
|
-
await writeFile(fakeTmuxPath, buildFakeTmux(tmuxLogPath));
|
|
2585
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
2586
|
-
const result = runNotifyHook(cwd, fakeBinDir);
|
|
2587
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
2588
|
-
// Verify event was written
|
|
2589
|
-
const eventsPath = join(eventsDir, 'events.ndjson');
|
|
2590
|
-
assert.ok(existsSync(eventsPath), 'events.ndjson should exist after nudge');
|
|
2591
|
-
const eventsContent = await readFile(eventsPath, 'utf-8');
|
|
2592
|
-
const events = eventsContent.trim().split('\n').map(line => JSON.parse(line));
|
|
2593
|
-
const nudgeEvent = events.find((e) => e.type === 'team_leader_nudge');
|
|
2594
|
-
assert.ok(nudgeEvent, 'should have a team_leader_nudge event');
|
|
2595
|
-
assert.equal(nudgeEvent.team, teamName);
|
|
2596
|
-
assert.equal(nudgeEvent.worker, 'leader-fixed');
|
|
2597
|
-
assert.ok(nudgeEvent.reason, 'event should have a reason');
|
|
2598
|
-
assert.notEqual(nudgeEvent.reason, 'leader_pane_missing_no_injection');
|
|
2599
|
-
assert.ok(nudgeEvent.orchestration_intent, 'event should record an orchestration intent');
|
|
2600
|
-
});
|
|
2601
|
-
});
|
|
2602
|
-
it('defers leader nudge when leader_pane_id is missing', async () => {
|
|
2603
|
-
await withTempWorkingDir(async (cwd) => {
|
|
2604
|
-
const rcsDir = join(cwd, '.rcs');
|
|
2605
|
-
const stateDir = join(rcsDir, 'state');
|
|
2606
|
-
const logsDir = join(rcsDir, 'logs');
|
|
2607
|
-
const teamName = 'gamma-missing-pane';
|
|
2608
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
2609
|
-
const eventsDir = join(teamDir, 'events');
|
|
2610
|
-
const mailboxDir = join(teamDir, 'mailbox');
|
|
2611
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
2612
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
2613
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
2614
|
-
await mkdir(logsDir, { recursive: true });
|
|
2615
|
-
await mkdir(eventsDir, { recursive: true });
|
|
2616
|
-
await mkdir(mailboxDir, { recursive: true });
|
|
2617
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
2618
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
2619
|
-
active: true,
|
|
2620
|
-
team_name: teamName,
|
|
2621
|
-
current_phase: 'team-exec',
|
|
2622
|
-
});
|
|
2623
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
2624
|
-
name: teamName,
|
|
2625
|
-
tmux_session: 'devsess:0',
|
|
2626
|
-
});
|
|
2627
|
-
await writeJson(join(mailboxDir, 'leader-fixed.json'), {
|
|
2628
|
-
worker: 'leader-fixed',
|
|
2629
|
-
messages: [
|
|
2630
|
-
{
|
|
2631
|
-
message_id: 'msg-missing-pane',
|
|
2632
|
-
from_worker: 'worker-1',
|
|
2633
|
-
to_worker: 'leader-fixed',
|
|
2634
|
-
body: 'Task complete',
|
|
2635
|
-
created_at: '2026-02-14T00:00:00.000Z',
|
|
2636
|
-
},
|
|
2637
|
-
],
|
|
2638
|
-
});
|
|
2639
|
-
await writeFile(fakeTmuxPath, buildFakeTmux(tmuxLogPath));
|
|
2640
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
2641
|
-
const result = runNotifyHook(cwd, fakeBinDir);
|
|
2642
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
2643
|
-
if (existsSync(tmuxLogPath)) {
|
|
2644
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
2645
|
-
assert.doesNotMatch(tmuxLog, /send-keys -t .*devsess/, 'must not fall back to session target');
|
|
2646
|
-
}
|
|
2647
|
-
const eventsPath = join(eventsDir, 'events.ndjson');
|
|
2648
|
-
assert.ok(existsSync(eventsPath), 'events.ndjson should exist');
|
|
2649
|
-
const eventsContent = await readFile(eventsPath, 'utf-8');
|
|
2650
|
-
const events = eventsContent.trim().split('\n').map(line => JSON.parse(line));
|
|
2651
|
-
const deferred = events.find((e) => e.type === 'leader_notification_deferred' && e.reason === 'leader_pane_missing_no_injection');
|
|
2652
|
-
assert.ok(deferred);
|
|
2653
|
-
assert.equal(deferred.type, 'leader_notification_deferred');
|
|
2654
|
-
assert.equal(deferred.worker, 'leader-fixed');
|
|
2655
|
-
assert.equal(deferred.to_worker, 'leader-fixed');
|
|
2656
|
-
assert.equal(deferred.source_type, 'leader_nudge');
|
|
2657
|
-
assert.equal(deferred.tmux_session, 'devsess:0');
|
|
2658
|
-
assert.equal(deferred.leader_pane_id, null);
|
|
2659
|
-
assert.equal(deferred.orchestration_intent, 'pending-mailbox-review');
|
|
2660
|
-
assert.equal(deferred.tmux_injection_attempted, false);
|
|
2661
|
-
const nudgeStatePath = join(stateDir, 'team-leader-nudge.json');
|
|
2662
|
-
assert.ok(existsSync(nudgeStatePath), 'nudge state should still advance on deferred leader visibility');
|
|
2663
|
-
const nudgeState = JSON.parse(await readFile(nudgeStatePath, 'utf-8'));
|
|
2664
|
-
assert.ok(nudgeState.last_nudged_by_team?.[teamName]?.at);
|
|
2665
|
-
assert.equal(nudgeState.last_nudged_by_team?.[teamName]?.orchestration_intent, 'pending-mailbox-review');
|
|
2666
|
-
const leaderAttentionPath = join(stateDir, 'team', teamName, 'leader-attention.json');
|
|
2667
|
-
assert.ok(existsSync(leaderAttentionPath), 'leader attention state should be written from notify-hook');
|
|
2668
|
-
const leaderAttention = JSON.parse(await readFile(leaderAttentionPath, 'utf-8'));
|
|
2669
|
-
assert.equal(leaderAttention.source, 'notify_hook');
|
|
2670
|
-
assert.equal(leaderAttention.team_name, teamName);
|
|
2671
|
-
assert.equal(leaderAttention.leader_decision_state, 'still_actionable');
|
|
2672
|
-
assert.equal(leaderAttention.leader_attention_pending, true);
|
|
2673
|
-
assert.equal(leaderAttention.leader_attention_reason, 'new_mailbox_message');
|
|
2674
|
-
assert.deepEqual(leaderAttention.attention_reasons, ['new_mailbox_message']);
|
|
2675
|
-
assert.equal(leaderAttention.leader_session_active, true);
|
|
2676
|
-
assert.equal(leaderAttention.leader_session_stopped_at, null);
|
|
2677
|
-
});
|
|
2678
|
-
});
|
|
2679
|
-
it('bounds repeated all-workers-idle nudges by cooldown', async () => {
|
|
2680
|
-
await withTempWorkingDir(async (cwd) => {
|
|
2681
|
-
const rcsDir = join(cwd, '.rcs');
|
|
2682
|
-
const stateDir = join(rcsDir, 'state');
|
|
2683
|
-
const logsDir = join(rcsDir, 'logs');
|
|
2684
|
-
const teamName = 'idle-bounded';
|
|
2685
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
2686
|
-
const workersDir = join(teamDir, 'workers');
|
|
2687
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
2688
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
2689
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
2690
|
-
await mkdir(logsDir, { recursive: true });
|
|
2691
|
-
await mkdir(workersDir, { recursive: true });
|
|
2692
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
2693
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
2694
|
-
active: true,
|
|
2695
|
-
team_name: teamName,
|
|
2696
|
-
current_phase: 'team-exec',
|
|
2697
|
-
});
|
|
2698
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
2699
|
-
name: teamName,
|
|
2700
|
-
tmux_session: 'idle-bounded:0',
|
|
2701
|
-
leader_pane_id: '%98',
|
|
2702
|
-
workers: [
|
|
2703
|
-
{ name: 'worker-1', index: 1, role: 'executor', assigned_tasks: [] },
|
|
2704
|
-
{ name: 'worker-2', index: 2, role: 'executor', assigned_tasks: [] },
|
|
2705
|
-
],
|
|
2706
|
-
});
|
|
2707
|
-
await writeJson(join(stateDir, 'hud-state.json'), {
|
|
2708
|
-
last_turn_at: new Date().toISOString(),
|
|
2709
|
-
turn_count: 1,
|
|
2710
|
-
});
|
|
2711
|
-
for (const worker of ['worker-1', 'worker-2']) {
|
|
2712
|
-
await mkdir(join(workersDir, worker), { recursive: true });
|
|
2713
|
-
await writeJson(join(workersDir, worker, 'status.json'), {
|
|
2714
|
-
state: 'idle',
|
|
2715
|
-
updated_at: new Date().toISOString(),
|
|
2716
|
-
});
|
|
2717
|
-
}
|
|
2718
|
-
await writeFile(fakeTmuxPath, buildFakeTmux(tmuxLogPath));
|
|
2719
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
2720
|
-
const first = runNotifyHook(cwd, fakeBinDir, { RCS_TEAM_LEADER_ALL_IDLE_COOLDOWN_MS: '600000' });
|
|
2721
|
-
assert.equal(first.status, 0, `notify-hook failed: ${first.stderr || first.stdout}`);
|
|
2722
|
-
const second = runNotifyHook(cwd, fakeBinDir, { RCS_TEAM_LEADER_ALL_IDLE_COOLDOWN_MS: '600000' });
|
|
2723
|
-
assert.equal(second.status, 0, `notify-hook failed: ${second.stderr || second.stdout}`);
|
|
2724
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
2725
|
-
const sends = tmuxLog.match(/send-keys -t %98 -l \[RCS\] All 2 workers idle/g) || [];
|
|
2726
|
-
assert.equal(sends.length, 1, 'cooldown should keep repeated all-workers-idle leader nudges bounded');
|
|
2727
|
-
assert.doesNotMatch(tmuxLog, /\[RCS_INTENT:/);
|
|
2728
|
-
});
|
|
2729
|
-
});
|
|
2730
|
-
it('does not nudge when no active team state exists', async () => {
|
|
2731
|
-
await withTempWorkingDir(async (cwd) => {
|
|
2732
|
-
const rcsDir = join(cwd, '.rcs');
|
|
2733
|
-
const stateDir = join(rcsDir, 'state');
|
|
2734
|
-
const logsDir = join(rcsDir, 'logs');
|
|
2735
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
2736
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
2737
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
2738
|
-
await mkdir(logsDir, { recursive: true });
|
|
2739
|
-
await mkdir(stateDir, { recursive: true });
|
|
2740
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
2741
|
-
// No team-state.json — no active team
|
|
2742
|
-
await writeFile(fakeTmuxPath, buildFakeTmux(tmuxLogPath));
|
|
2743
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
2744
|
-
const result = runNotifyHook(cwd, fakeBinDir);
|
|
2745
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
2746
|
-
// tmux log should not contain display-message for any team nudge
|
|
2747
|
-
const hasLog = existsSync(tmuxLogPath);
|
|
2748
|
-
if (hasLog) {
|
|
2749
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
2750
|
-
assert.doesNotMatch(tmuxLog, /Team .+: leader stale/);
|
|
2751
|
-
}
|
|
2752
|
-
});
|
|
2753
|
-
});
|
|
2754
|
-
it('includes stale_leader_with_messages reason when both conditions met', async () => {
|
|
2755
|
-
await withTempWorkingDir(async (cwd) => {
|
|
2756
|
-
const rcsDir = join(cwd, '.rcs');
|
|
2757
|
-
const stateDir = join(rcsDir, 'state');
|
|
2758
|
-
const logsDir = join(rcsDir, 'logs');
|
|
2759
|
-
const teamName = 'delta';
|
|
2760
|
-
const teamDir = join(stateDir, 'team', teamName);
|
|
2761
|
-
const eventsDir = join(teamDir, 'events');
|
|
2762
|
-
const mailboxDir = join(teamDir, 'mailbox');
|
|
2763
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
2764
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
2765
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
2766
|
-
await mkdir(logsDir, { recursive: true });
|
|
2767
|
-
await mkdir(eventsDir, { recursive: true });
|
|
2768
|
-
await mkdir(mailboxDir, { recursive: true });
|
|
2769
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
2770
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
2771
|
-
active: true,
|
|
2772
|
-
team_name: teamName,
|
|
2773
|
-
current_phase: 'team-exec',
|
|
2774
|
-
});
|
|
2775
|
-
await writeJson(join(teamDir, 'config.json'), {
|
|
2776
|
-
name: teamName,
|
|
2777
|
-
tmux_session: 'rcs-team-delta',
|
|
2778
|
-
leader_pane_id: '%94',
|
|
2779
|
-
});
|
|
2780
|
-
// Leader stale
|
|
2781
|
-
await writeJson(join(stateDir, 'hud-state.json'), {
|
|
2782
|
-
last_turn_at: new Date(Date.now() - 300_000).toISOString(),
|
|
2783
|
-
turn_count: 3,
|
|
2784
|
-
});
|
|
2785
|
-
// Mailbox has messages
|
|
2786
|
-
await writeJson(join(mailboxDir, 'leader-fixed.json'), {
|
|
2787
|
-
worker: 'leader-fixed',
|
|
2788
|
-
messages: [
|
|
2789
|
-
{
|
|
2790
|
-
message_id: 'combo-msg',
|
|
2791
|
-
from_worker: 'worker-2',
|
|
2792
|
-
to_worker: 'leader-fixed',
|
|
2793
|
-
body: 'done',
|
|
2794
|
-
created_at: '2026-02-14T00:00:00.000Z',
|
|
2795
|
-
},
|
|
2796
|
-
],
|
|
2797
|
-
});
|
|
2798
|
-
await writeFile(fakeTmuxPath, buildFakeTmux(tmuxLogPath));
|
|
2799
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
2800
|
-
const result = runNotifyHook(cwd, fakeBinDir);
|
|
2801
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
2802
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
2803
|
-
assert.match(tmuxLog, /Team delta: leader stale, \d+ pane\(s\) active, 1 msg\(s\) pending\./);
|
|
2804
|
-
assert.match(tmuxLog, /Next: read messages; keep orchestrating; if done, gracefully shut down: rcs team shutdown delta\./);
|
|
2805
|
-
assert.doesNotMatch(tmuxLog, /keep polling/);
|
|
2806
|
-
assert.match(tmuxLog, /\[RCS_TMUX_INJECT\]/, 'should include injection marker');
|
|
2807
|
-
// Verify event reason
|
|
2808
|
-
const eventsPath = join(eventsDir, 'events.ndjson');
|
|
2809
|
-
assert.ok(existsSync(eventsPath), 'events.ndjson should exist');
|
|
2810
|
-
const eventsContent = await readFile(eventsPath, 'utf-8');
|
|
2811
|
-
const events = eventsContent.trim().split('\n').map(line => JSON.parse(line));
|
|
2812
|
-
const nudgeEvent = events.find((e) => e.type === 'team_leader_nudge');
|
|
2813
|
-
assert.ok(nudgeEvent);
|
|
2814
|
-
assert.equal(nudgeEvent.reason, 'stale_leader_with_messages');
|
|
2815
|
-
});
|
|
2816
|
-
});
|
|
2817
|
-
it('rejects invalid team_name before leader follow-up team path joins', async () => {
|
|
2818
|
-
await withTempWorkingDir(async (cwd) => {
|
|
2819
|
-
const rcsDir = join(cwd, '.rcs');
|
|
2820
|
-
const stateDir = join(rcsDir, 'state');
|
|
2821
|
-
const logsDir = join(rcsDir, 'logs');
|
|
2822
|
-
const fakeBinDir = join(cwd, 'fake-bin');
|
|
2823
|
-
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
2824
|
-
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
2825
|
-
const validTeamName = 'valid-team';
|
|
2826
|
-
const validTeamDir = join(stateDir, 'team', validTeamName);
|
|
2827
|
-
const workersDir = join(validTeamDir, 'workers');
|
|
2828
|
-
const nowIso = new Date().toISOString();
|
|
2829
|
-
await mkdir(logsDir, { recursive: true });
|
|
2830
|
-
await mkdir(workersDir, { recursive: true });
|
|
2831
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
2832
|
-
await writeJson(join(stateDir, 'session.json'), { session_id: 'sess-current' });
|
|
2833
|
-
await writeJson(join(stateDir, 'team-state.json'), {
|
|
2834
|
-
active: true,
|
|
2835
|
-
team_name: '../team/valid-team',
|
|
2836
|
-
current_phase: 'team-exec',
|
|
2837
|
-
});
|
|
2838
|
-
await writeJson(join(validTeamDir, 'manifest.v2.json'), {
|
|
2839
|
-
schema_version: 2,
|
|
2840
|
-
name: validTeamName,
|
|
2841
|
-
task: 'invalid team path repro',
|
|
2842
|
-
leader: {
|
|
2843
|
-
session_id: 'sess-current',
|
|
2844
|
-
worker_id: 'leader-fixed',
|
|
2845
|
-
role: 'coordinator',
|
|
2846
|
-
},
|
|
2847
|
-
policy: {
|
|
2848
|
-
worker_launch_mode: 'interactive',
|
|
2849
|
-
display_mode: 'split_pane',
|
|
2850
|
-
dispatch_mode: 'hook_preferred_with_fallback',
|
|
2851
|
-
dispatch_ack_timeout_ms: 2000,
|
|
2852
|
-
},
|
|
2853
|
-
tmux_session: 'valid-team:0',
|
|
2854
|
-
leader_pane_id: '%94',
|
|
2855
|
-
worker_count: 1,
|
|
2856
|
-
workers: [
|
|
2857
|
-
{ name: 'worker-1', index: 1, pane_id: '%10', role: 'executor' },
|
|
2858
|
-
],
|
|
2859
|
-
created_at: nowIso,
|
|
2860
|
-
});
|
|
2861
|
-
await mkdir(join(workersDir, 'worker-1'), { recursive: true });
|
|
2862
|
-
await writeJson(join(workersDir, 'worker-1', 'status.json'), {
|
|
2863
|
-
state: 'idle',
|
|
2864
|
-
updated_at: nowIso,
|
|
2865
|
-
});
|
|
2866
|
-
await writeFile(fakeTmuxPath, buildFakeTmux(tmuxLogPath));
|
|
2867
|
-
await chmod(fakeTmuxPath, 0o755);
|
|
2868
|
-
const result = runNotifyHook(cwd, fakeBinDir, {
|
|
2869
|
-
RCS_SESSION_ID: 'sess-current',
|
|
2870
|
-
});
|
|
2871
|
-
assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
|
|
2872
|
-
if (existsSync(tmuxLogPath)) {
|
|
2873
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
2874
|
-
assert.doesNotMatch(tmuxLog, /send-keys/, 'invalid team_name must not be used for leader follow-up path resolution');
|
|
2875
|
-
}
|
|
2876
|
-
});
|
|
2877
|
-
});
|
|
2878
|
-
});
|
|
2879
|
-
//# sourceMappingURL=notify-hook-team-leader-nudge.test.js.map
|