dsh-compound-engineering 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/README.md +160 -2
- package/README.zh.md +161 -0
- package/cordis.patch.yml +6 -0
- package/lib/frontmatter.js +167 -0
- package/lib/index.js +46 -0
- package/lib/provider.js +201 -0
- package/package.json +62 -4
- package/skills/CODING_STANDARDS.md +68 -0
- package/skills/ce-babysit-pr/SKILL.md +59 -0
- package/skills/ce-babysit-pr/references/branch-currency.md +22 -0
- package/skills/ce-babysit-pr/references/envelope.md +44 -0
- package/skills/ce-babysit-pr/references/pipeline.md +15 -0
- package/skills/ce-babysit-pr/references/report.md +20 -0
- package/skills/ce-babysit-pr/references/settle.md +37 -0
- package/skills/ce-babysit-pr/references/setup.md +33 -0
- package/skills/ce-babysit-pr/references/stack-commands.md +39 -0
- package/skills/ce-babysit-pr/references/stack.md +43 -0
- package/skills/ce-babysit-pr/references/tick.md +102 -0
- package/skills/ce-babysit-pr/references/watch-loop.md +222 -0
- package/skills/ce-babysit-pr/scripts/pr-snapshot +3656 -0
- package/skills/ce-bakeoff/SKILL.md +48 -0
- package/skills/ce-bakeoff/references/candidates.md +37 -0
- package/skills/ce-bakeoff/references/judging.md +19 -0
- package/skills/ce-bakeoff/references/output.md +11 -0
- package/skills/ce-bakeoff/references/verification.md +15 -0
- package/skills/ce-brainstorm/SKILL.md +69 -0
- package/skills/ce-brainstorm/references/agents/slack-researcher.md +127 -0
- package/skills/ce-brainstorm/references/approaches.md +57 -0
- package/skills/ce-brainstorm/references/bakeoff.md +9 -0
- package/skills/ce-brainstorm/references/blindspot-pass.md +70 -0
- package/skills/ce-brainstorm/references/brainstorm-sections.md +458 -0
- package/skills/ce-brainstorm/references/dialogue.md +89 -0
- package/skills/ce-brainstorm/references/handoff.md +187 -0
- package/skills/ce-brainstorm/references/html-rendering.md +638 -0
- package/skills/ce-brainstorm/references/interaction-rules.md +33 -0
- package/skills/ce-brainstorm/references/markdown-rendering.md +242 -0
- package/skills/ce-brainstorm/references/model-tiers.md +13 -0
- package/skills/ce-brainstorm/references/output-mode.md +26 -0
- package/skills/ce-brainstorm/references/phase-0.md +104 -0
- package/skills/ce-brainstorm/references/plan-write.md +32 -0
- package/skills/ce-brainstorm/references/product-pressure-test.md +42 -0
- package/skills/ce-brainstorm/references/reasoning-elevation.md +124 -0
- package/skills/ce-brainstorm/references/settled-decisions.md +45 -0
- package/skills/ce-brainstorm/references/synthesis-summary.md +149 -0
- package/skills/ce-brainstorm/references/universal-brainstorming.md +72 -0
- package/skills/ce-brainstorm/references/verdict-routing.md +26 -0
- package/skills/ce-brainstorm/references/visual-probes.md +164 -0
- package/skills/ce-brainstorm/scripts/elevation-dispatch.sh +288 -0
- package/skills/ce-brainstorm/scripts/light-webserver.js +1275 -0
- package/skills/ce-brainstorm/scripts/packs-resolve.py +737 -0
- package/skills/ce-brainstorm/scripts/peer-job-runner.py +2594 -0
- package/skills/ce-code-review/SKILL.md +40 -0
- package/skills/ce-code-review/references/action-class-rubric.md +67 -0
- package/skills/ce-code-review/references/cross-model-recovery.md +38 -0
- package/skills/ce-code-review/references/cross-model-review.md +191 -0
- package/skills/ce-code-review/references/depth-paths.md +32 -0
- package/skills/ce-code-review/references/diff-scope.md +59 -0
- package/skills/ce-code-review/references/dispatch-reviewers.md +135 -0
- package/skills/ce-code-review/references/findings-schema.json +125 -0
- package/skills/ce-code-review/references/finish-input.md +118 -0
- package/skills/ce-code-review/references/finish-review.md +202 -0
- package/skills/ce-code-review/references/intent-and-plan.md +73 -0
- package/skills/ce-code-review/references/modes-and-output.md +156 -0
- package/skills/ce-code-review/references/persona-catalog.md +66 -0
- package/skills/ce-code-review/references/personas/adversarial-reviewer.md +111 -0
- package/skills/ce-code-review/references/personas/agent-native-reviewer.md +173 -0
- package/skills/ce-code-review/references/personas/api-contract-reviewer.md +44 -0
- package/skills/ce-code-review/references/personas/correctness-reviewer.md +48 -0
- package/skills/ce-code-review/references/personas/data-migration-reviewer.md +111 -0
- package/skills/ce-code-review/references/personas/deployment-verification-agent.md +157 -0
- package/skills/ce-code-review/references/personas/julik-frontend-races-reviewer.md +45 -0
- package/skills/ce-code-review/references/personas/learnings-researcher.md +260 -0
- package/skills/ce-code-review/references/personas/maintainability-reviewer.md +85 -0
- package/skills/ce-code-review/references/personas/performance-reviewer.md +45 -0
- package/skills/ce-code-review/references/personas/previous-comments-reviewer.md +59 -0
- package/skills/ce-code-review/references/personas/project-standards-reviewer.md +77 -0
- package/skills/ce-code-review/references/personas/reliability-reviewer.md +49 -0
- package/skills/ce-code-review/references/personas/security-reviewer.md +50 -0
- package/skills/ce-code-review/references/personas/swift-ios-reviewer.md +99 -0
- package/skills/ce-code-review/references/personas/testing-reviewer.md +51 -0
- package/skills/ce-code-review/references/review-output-template.md +172 -0
- package/skills/ce-code-review/references/scope.md +176 -0
- package/skills/ce-code-review/references/select-and-route.md +93 -0
- package/skills/ce-code-review/references/subagent-template.md +206 -0
- package/skills/ce-code-review/references/validator-batch-template.md +67 -0
- package/skills/ce-code-review/scripts/cross-model-adversarial-review.sh +1275 -0
- package/skills/ce-code-review/scripts/findings-mechanics.py +274 -0
- package/skills/ce-code-review/scripts/packs-resolve.py +737 -0
- package/skills/ce-code-review/scripts/peer-job-runner.py +2594 -0
- package/skills/ce-code-review/scripts/review-scope.py +419 -0
- package/skills/ce-code-review/scripts/run-log.py +280 -0
- package/skills/ce-commit/SKILL.md +60 -0
- package/skills/ce-commit-push-pr/SKILL.md +69 -0
- package/skills/ce-commit-push-pr/references/apply-and-handoff.md +79 -0
- package/skills/ce-commit-push-pr/references/branch-creation.md +47 -0
- package/skills/ce-commit-push-pr/references/commit-and-push.md +26 -0
- package/skills/ce-commit-push-pr/references/compose.md +29 -0
- package/skills/ce-commit-push-pr/references/context.md +42 -0
- package/skills/ce-commit-push-pr/references/gh-stack-cli.md +83 -0
- package/skills/ce-commit-push-pr/references/pr-description-writing.md +208 -0
- package/skills/ce-commit-push-pr/references/stack-submit.md +81 -0
- package/skills/ce-compound/SKILL.md +79 -0
- package/skills/ce-compound/assets/resolution-template.md +96 -0
- package/skills/ce-compound/references/agents/best-practices-researcher.md +115 -0
- package/skills/ce-compound/references/agents/data-integrity-guardian.md +68 -0
- package/skills/ce-compound/references/agents/framework-docs-researcher.md +93 -0
- package/skills/ce-compound/references/agents/pattern-recognition-specialist.md +55 -0
- package/skills/ce-compound/references/agents/performance-oracle.md +108 -0
- package/skills/ce-compound/references/agents/security-sentinel.md +91 -0
- package/skills/ce-compound/references/agents/session-historian.md +82 -0
- package/skills/ce-compound/references/assembly.md +127 -0
- package/skills/ce-compound/references/concepts-vocabulary.md +98 -0
- package/skills/ce-compound/references/enhancement.md +42 -0
- package/skills/ce-compound/references/grounding-validation.md +89 -0
- package/skills/ce-compound/references/lightweight.md +56 -0
- package/skills/ce-compound/references/modes.md +13 -0
- package/skills/ce-compound/references/refresh-and-discoverability.md +96 -0
- package/skills/ce-compound/references/report.md +115 -0
- package/skills/ce-compound/references/research.md +144 -0
- package/skills/ce-compound/references/schema.yaml +236 -0
- package/skills/ce-compound/references/session-history.md +73 -0
- package/skills/ce-compound/references/yaml-schema.md +130 -0
- package/skills/ce-compound/scripts/packs-resolve.py +737 -0
- package/skills/ce-compound/scripts/session-history/__pycache__/dsh_session.cpython-314.pyc +0 -0
- package/skills/ce-compound/scripts/session-history/discover-sessions.sh +290 -0
- package/skills/ce-compound/scripts/session-history/discover_dsh.py +123 -0
- package/skills/ce-compound/scripts/session-history/dsh_session.py +225 -0
- package/skills/ce-compound/scripts/session-history/extract-errors.py +178 -0
- package/skills/ce-compound/scripts/session-history/extract-metadata.py +301 -0
- package/skills/ce-compound/scripts/session-history/extract-skeleton.py +346 -0
- package/skills/ce-compound/scripts/validate-doc-claims.py +497 -0
- package/skills/ce-compound/scripts/validate-frontmatter.py +137 -0
- package/skills/ce-compound-refresh/SKILL.md +88 -0
- package/skills/ce-compound-refresh/assets/resolution-template.md +96 -0
- package/skills/ce-compound-refresh/references/classify.md +42 -0
- package/skills/ce-compound-refresh/references/commit.md +7 -0
- package/skills/ce-compound-refresh/references/concepts-vocabulary.md +116 -0
- package/skills/ce-compound-refresh/references/discoverability.md +15 -0
- package/skills/ce-compound-refresh/references/investigate.md +23 -0
- package/skills/ce-compound-refresh/references/modes.md +27 -0
- package/skills/ce-compound-refresh/references/per-action-flows.md +142 -0
- package/skills/ce-compound-refresh/references/report.md +23 -0
- package/skills/ce-compound-refresh/references/schema.yaml +236 -0
- package/skills/ce-compound-refresh/references/scope.md +18 -0
- package/skills/ce-compound-refresh/references/worth-audit.md +35 -0
- package/skills/ce-compound-refresh/references/yaml-schema.md +130 -0
- package/skills/ce-compound-refresh/scripts/validate-doc-claims.py +497 -0
- package/skills/ce-compound-refresh/scripts/validate-frontmatter.py +137 -0
- package/skills/ce-debug/SKILL.md +122 -0
- package/skills/ce-debug/references/anti-patterns.md +91 -0
- package/skills/ce-debug/references/defense-in-depth.md +39 -0
- package/skills/ce-debug/references/fix.md +21 -0
- package/skills/ce-debug/references/investigate.md +114 -0
- package/skills/ce-debug/references/investigation-techniques.md +384 -0
- package/skills/ce-debug/references/pipeline-mode.md +78 -0
- package/skills/ce-debug/references/post-fix-handoff.md +60 -0
- package/skills/ce-debug/references/return-to-caller.md +57 -0
- package/skills/ce-doc-review/SKILL.md +75 -0
- package/skills/ce-doc-review/references/bulk-preview.md +146 -0
- package/skills/ce-doc-review/references/cross-model-review.md +207 -0
- package/skills/ce-doc-review/references/decision-primer.md +47 -0
- package/skills/ce-doc-review/references/dispatch.md +56 -0
- package/skills/ce-doc-review/references/document-intake.md +44 -0
- package/skills/ce-doc-review/references/findings-schema.json +85 -0
- package/skills/ce-doc-review/references/modes.md +20 -0
- package/skills/ce-doc-review/references/open-questions-defer.md +158 -0
- package/skills/ce-doc-review/references/persona-selection.md +41 -0
- package/skills/ce-doc-review/references/personas/adversarial-document-reviewer.md +109 -0
- package/skills/ce-doc-review/references/personas/coherence-reviewer.md +69 -0
- package/skills/ce-doc-review/references/personas/design-lens-reviewer.md +48 -0
- package/skills/ce-doc-review/references/personas/feasibility-reviewer.md +50 -0
- package/skills/ce-doc-review/references/personas/product-lens-reviewer.md +85 -0
- package/skills/ce-doc-review/references/personas/scope-guardian-reviewer.md +83 -0
- package/skills/ce-doc-review/references/personas/security-lens-reviewer.md +41 -0
- package/skills/ce-doc-review/references/personas/whole-doc-reviewer.md +25 -0
- package/skills/ce-doc-review/references/rendering-floor.md +175 -0
- package/skills/ce-doc-review/references/review-output-template.md +123 -0
- package/skills/ce-doc-review/references/subagent-template.md +188 -0
- package/skills/ce-doc-review/references/synthesis-and-presentation.md +350 -0
- package/skills/ce-doc-review/references/walkthrough.md +347 -0
- package/skills/ce-doc-review/scripts/cross-model-doc-review.sh +1270 -0
- package/skills/ce-doc-review/scripts/packs-resolve.py +737 -0
- package/skills/ce-doc-review/scripts/peer-job-runner.py +2594 -0
- package/skills/ce-dogfood/SKILL.md +66 -0
- package/skills/ce-dogfood/agents/openai.yaml +2 -0
- package/skills/ce-dogfood/references/dogfood-report-template.md +97 -0
- package/skills/ce-dogfood/references/phases.md +167 -0
- package/skills/ce-dogfood/references/test-matrix-taxonomy.md +62 -0
- package/skills/ce-dogfood/scripts/packs-resolve.py +737 -0
- package/skills/ce-explain/SKILL.md +81 -0
- package/skills/ce-explain/references/agents/behavior-trace-scout.md +19 -0
- package/skills/ce-explain/references/agents/work-recap-scout.md +23 -0
- package/skills/ce-explain/references/check-in.md +18 -0
- package/skills/ce-explain/references/destinations.md +35 -0
- package/skills/ce-explain/references/explainer-html.md +29 -0
- package/skills/ce-explain/references/explainer-markdown.md +24 -0
- package/skills/ce-explain/references/intake.md +50 -0
- package/skills/ce-explain/references/orchestration.md +46 -0
- package/skills/ce-handoff/SKILL.md +54 -0
- package/skills/ce-handoff/references/create.md +88 -0
- package/skills/ce-handoff/references/resume.md +46 -0
- package/skills/ce-ideate/SKILL.md +75 -0
- package/skills/ce-ideate/references/agents/issue-intelligence-analyst.md +166 -0
- package/skills/ce-ideate/references/agents/learnings-researcher.md +247 -0
- package/skills/ce-ideate/references/agents/slack-researcher.md +127 -0
- package/skills/ce-ideate/references/agents/web-researcher.md +121 -0
- package/skills/ce-ideate/references/decomposition.md +39 -0
- package/skills/ce-ideate/references/divergent-ideation.md +112 -0
- package/skills/ce-ideate/references/grounding.md +138 -0
- package/skills/ce-ideate/references/html-rendering.md +638 -0
- package/skills/ce-ideate/references/ideation-sections.md +191 -0
- package/skills/ce-ideate/references/issue-intelligence.md +44 -0
- package/skills/ce-ideate/references/markdown-rendering.md +242 -0
- package/skills/ce-ideate/references/output-mode.md +34 -0
- package/skills/ce-ideate/references/post-ideation-workflow.md +169 -0
- package/skills/ce-ideate/references/scope-gates.md +148 -0
- package/skills/ce-ideate/references/universal-ideation.md +119 -0
- package/skills/ce-ideate/references/user-research-artifacts.md +28 -0
- package/skills/ce-ideate/references/web-research-cache.md +56 -0
- package/skills/ce-noslop/SKILL.md +45 -0
- package/skills/ce-noslop/references/patterns.md +61 -0
- package/skills/ce-noslop/references/terminology.md +3 -0
- package/skills/ce-optimize/SKILL.md +64 -0
- package/skills/ce-optimize/references/agents/learnings-researcher.md +247 -0
- package/skills/ce-optimize/references/agents/repo-research-analyst.md +258 -0
- package/skills/ce-optimize/references/example-expensive-benchmark-spec.yaml +88 -0
- package/skills/ce-optimize/references/example-hard-spec.yaml +64 -0
- package/skills/ce-optimize/references/example-judge-spec.yaml +83 -0
- package/skills/ce-optimize/references/example-text-target-spec.yaml +80 -0
- package/skills/ce-optimize/references/experiment-log-schema.yaml +563 -0
- package/skills/ce-optimize/references/experiment-prompt-template.md +143 -0
- package/skills/ce-optimize/references/judge-prompt-template.md +117 -0
- package/skills/ce-optimize/references/loop.md +286 -0
- package/skills/ce-optimize/references/measurement.md +168 -0
- package/skills/ce-optimize/references/optimize-spec-schema.yaml +570 -0
- package/skills/ce-optimize/references/persistence.md +93 -0
- package/skills/ce-optimize/references/spec.md +107 -0
- package/skills/ce-optimize/references/text-targets.md +51 -0
- package/skills/ce-optimize/references/usage-guide.md +147 -0
- package/skills/ce-optimize/references/wrap-up.md +54 -0
- package/skills/ce-optimize/scripts/decide.mjs +676 -0
- package/skills/ce-optimize/scripts/experiment-worktree.sh +330 -0
- package/skills/ce-optimize/scripts/measure.sh +140 -0
- package/skills/ce-optimize/scripts/parallel-probe.sh +134 -0
- package/skills/ce-plan/SKILL.md +60 -0
- package/skills/ce-plan/references/agents/agent-native-planning-strategist.md +66 -0
- package/skills/ce-plan/references/agents/architecture-strategist.md +46 -0
- package/skills/ce-plan/references/agents/best-practices-researcher.md +118 -0
- package/skills/ce-plan/references/agents/data-integrity-guardian.md +68 -0
- package/skills/ce-plan/references/agents/framework-docs-researcher.md +97 -0
- package/skills/ce-plan/references/agents/git-history-analyzer.md +40 -0
- package/skills/ce-plan/references/agents/learnings-researcher.md +268 -0
- package/skills/ce-plan/references/agents/pattern-recognition-specialist.md +55 -0
- package/skills/ce-plan/references/agents/performance-oracle.md +108 -0
- package/skills/ce-plan/references/agents/repo-research-analyst.md +262 -0
- package/skills/ce-plan/references/agents/security-sentinel.md +91 -0
- package/skills/ce-plan/references/agents/slack-researcher.md +131 -0
- package/skills/ce-plan/references/agents/spec-flow-analyzer.md +84 -0
- package/skills/ce-plan/references/agents/web-researcher.md +125 -0
- package/skills/ce-plan/references/approach-altitude.md +55 -0
- package/skills/ce-plan/references/bakeoff.md +9 -0
- package/skills/ce-plan/references/deepening-workflow.md +263 -0
- package/skills/ce-plan/references/final-review.md +134 -0
- package/skills/ce-plan/references/html-rendering.md +638 -0
- package/skills/ce-plan/references/intake.md +158 -0
- package/skills/ce-plan/references/markdown-rendering.md +242 -0
- package/skills/ce-plan/references/output-contracts.md +29 -0
- package/skills/ce-plan/references/output-mode.md +45 -0
- package/skills/ce-plan/references/plan-handoff.md +116 -0
- package/skills/ce-plan/references/plan-sections.md +489 -0
- package/skills/ce-plan/references/reasoning-elevation.md +124 -0
- package/skills/ce-plan/references/research.md +211 -0
- package/skills/ce-plan/references/resume.md +61 -0
- package/skills/ce-plan/references/settled-decisions.md +45 -0
- package/skills/ce-plan/references/structure.md +207 -0
- package/skills/ce-plan/references/synthesis-summary.md +260 -0
- package/skills/ce-plan/references/universal-planning.md +170 -0
- package/skills/ce-plan/scripts/elevation-dispatch.sh +288 -0
- package/skills/ce-plan/scripts/packs-resolve.py +737 -0
- package/skills/ce-plan/scripts/peer-job-runner.py +2594 -0
- package/skills/ce-polish/SKILL.md +22 -0
- package/skills/ce-polish/agents/openai.yaml +2 -0
- package/skills/ce-polish/references/dev-server-astro.md +58 -0
- package/skills/ce-polish/references/dev-server-detection.md +40 -0
- package/skills/ce-polish/references/dev-server-next.md +61 -0
- package/skills/ce-polish/references/dev-server-nuxt.md +58 -0
- package/skills/ce-polish/references/dev-server-procfile.md +59 -0
- package/skills/ce-polish/references/dev-server-rails.md +50 -0
- package/skills/ce-polish/references/dev-server-remix.md +58 -0
- package/skills/ce-polish/references/dev-server-sveltekit.md +58 -0
- package/skills/ce-polish/references/dev-server-vite.md +48 -0
- package/skills/ce-polish/references/install-riffrec.md +35 -0
- package/skills/ce-polish/references/launch-json-schema.md +177 -0
- package/skills/ce-polish/references/live-loop.md +98 -0
- package/skills/ce-polish/references/live-remote.md +35 -0
- package/skills/ce-polish/references/live-start.md +93 -0
- package/skills/ce-polish/references/live-stream-contract.md +253 -0
- package/skills/ce-polish/references/run.md +67 -0
- package/skills/ce-polish/scripts/detect-project-type.sh +277 -0
- package/skills/ce-polish/scripts/detect-riffrec.sh +225 -0
- package/skills/ce-polish/scripts/live-endpoint.js +2841 -0
- package/skills/ce-polish/scripts/read-launch-json.sh +117 -0
- package/skills/ce-polish/scripts/resolve-package-manager.sh +95 -0
- package/skills/ce-polish/scripts/resolve-port.sh +307 -0
- package/skills/ce-pov/SKILL.md +64 -0
- package/skills/ce-pov/references/agents/external-evidence-researcher.md +26 -0
- package/skills/ce-pov/references/agents/pov-peer.md +43 -0
- package/skills/ce-pov/references/agents/precedent-activity-scout.md +21 -0
- package/skills/ce-pov/references/agents/project-grounding-scout.md +32 -0
- package/skills/ce-pov/references/boundaries.md +36 -0
- package/skills/ce-pov/references/cross-model-panel.md +596 -0
- package/skills/ce-pov/references/followup.md +9 -0
- package/skills/ce-pov/references/grounding.md +47 -0
- package/skills/ce-pov/references/intake.md +61 -0
- package/skills/ce-pov/references/invocation.md +47 -0
- package/skills/ce-pov/references/method.md +68 -0
- package/skills/ce-pov/references/pov-schema.json +56 -0
- package/skills/ce-pov/references/report.md +7 -0
- package/skills/ce-pov/scripts/cross-model-pov.sh +1110 -0
- package/skills/ce-pov/scripts/peer-job-runner.py +2594 -0
- package/skills/ce-product-pulse/SKILL.md +81 -0
- package/skills/ce-product-pulse/agents/openai.yaml +2 -0
- package/skills/ce-product-pulse/references/config.md +20 -0
- package/skills/ce-product-pulse/references/interview.md +260 -0
- package/skills/ce-product-pulse/references/report-template.md +89 -0
- package/skills/ce-product-pulse/references/run.md +46 -0
- package/skills/ce-product-pulse/references/setup.md +43 -0
- package/skills/ce-promote/SKILL.md +90 -0
- package/skills/ce-promote/agents/openai.yaml +2 -0
- package/skills/ce-promote/references/spiral-cli.md +149 -0
- package/skills/ce-proof/SKILL.md +65 -0
- package/skills/ce-proof/references/api.md +176 -0
- package/skills/ce-proof/references/workflows.md +124 -0
- package/skills/ce-prototype/SKILL.md +59 -0
- package/skills/ce-prototype/assets/annotate.css +206 -0
- package/skills/ce-prototype/assets/annotate.js +837 -0
- package/skills/ce-prototype/references/annotation-loop.md +32 -0
- package/skills/ce-prototype/references/build.md +39 -0
- package/skills/ce-prototype/references/craft-floor.md +43 -0
- package/skills/ce-prototype/references/preview.md +118 -0
- package/skills/ce-prototype/references/scoping.md +38 -0
- package/skills/ce-prototype/references/write-back.md +37 -0
- package/skills/ce-prototype/scripts/light-webserver.js +1275 -0
- package/skills/ce-resolve-pr-feedback/SKILL.md +60 -0
- package/skills/ce-resolve-pr-feedback/references/agents/pr-comment-resolver.md +59 -0
- package/skills/ce-resolve-pr-feedback/references/evaluation-rubric.md +141 -0
- package/skills/ce-resolve-pr-feedback/references/full-mode.md +336 -0
- package/skills/ce-resolve-pr-feedback/references/pipeline-mode.md +23 -0
- package/skills/ce-resolve-pr-feedback/references/resume.md +60 -0
- package/skills/ce-resolve-pr-feedback/references/return-to-caller.md +111 -0
- package/skills/ce-resolve-pr-feedback/references/targeted-mode.md +48 -0
- package/skills/ce-resolve-pr-feedback/scripts/get-pr-comments +176 -0
- package/skills/ce-resolve-pr-feedback/scripts/get-thread-for-comment +84 -0
- package/skills/ce-resolve-pr-feedback/scripts/pending-feedback.py +287 -0
- package/skills/ce-resolve-pr-feedback/scripts/reply-to-pr-thread +57 -0
- package/skills/ce-resolve-pr-feedback/scripts/resolve-pr-thread +23 -0
- package/skills/ce-retune/SKILL.md +42 -0
- package/skills/ce-retune/agents/openai.yaml +2 -0
- package/skills/ce-retune/references/baseline-mining.md +122 -0
- package/skills/ce-retune/references/corpus-audit.md +128 -0
- package/skills/ce-retune/references/cut-passes.md +129 -0
- package/skills/ce-retune/references/halt-taxonomy.md +123 -0
- package/skills/ce-retune/references/noise-floor.md +131 -0
- package/skills/ce-retune/references/workflow-shapes.md +73 -0
- package/skills/ce-riffrec-feedback-analysis/SKILL.md +33 -0
- package/skills/ce-riffrec-feedback-analysis/references/analyzer.md +18 -0
- package/skills/ce-riffrec-feedback-analysis/references/compound-engineering-feedback-format.md +116 -0
- package/skills/ce-riffrec-feedback-analysis/references/extensive-analysis.md +98 -0
- package/skills/ce-riffrec-feedback-analysis/references/install-riffrec.md +27 -0
- package/skills/ce-riffrec-feedback-analysis/references/quick-bug-report.md +38 -0
- package/skills/ce-riffrec-feedback-analysis/scripts/analyze_riffrec_zip.py +1306 -0
- package/skills/ce-setup/SKILL.md +103 -0
- package/skills/ce-setup/agents/openai.yaml +2 -0
- package/skills/ce-setup/assets/compounding-directive.md +11 -0
- package/skills/ce-setup/assets/noslop-directive.md +7 -0
- package/skills/ce-setup/assets/pack-rule-template.md +11 -0
- package/skills/ce-setup/references/acpx-cache-warm.md +14 -0
- package/skills/ce-setup/references/config-template.yaml +210 -0
- package/skills/ce-setup/references/legacy-codex-tool-map.md +15 -0
- package/skills/ce-setup/references/pack-scaffold.md +45 -0
- package/skills/ce-setup/references/repo-fixes.md +121 -0
- package/skills/ce-setup/scripts/check-health +929 -0
- package/skills/ce-setup/scripts/packs-resolve.py +737 -0
- package/skills/ce-simplify-code/SKILL.md +76 -0
- package/skills/ce-simplify-code/references/personas/code-quality-reviewer.md +17 -0
- package/skills/ce-simplify-code/references/personas/code-reuse-reviewer.md +7 -0
- package/skills/ce-simplify-code/references/personas/comment-reviewer.md +24 -0
- package/skills/ce-simplify-code/references/personas/efficiency-reviewer.md +11 -0
- package/skills/ce-simplify-code/references/personas/test-value-reviewer.md +23 -0
- package/skills/ce-simplify-code/references/reviewer-dispatch.md +11 -0
- package/skills/ce-simplify-code/references/standalone-report.md +25 -0
- package/skills/ce-strategy/SKILL.md +61 -0
- package/skills/ce-strategy/references/grounding.md +22 -0
- package/skills/ce-strategy/references/interview.md +179 -0
- package/skills/ce-strategy/references/strategy-template.md +94 -0
- package/skills/ce-strategy/references/update-run.md +33 -0
- package/skills/ce-sweep/SKILL.md +93 -0
- package/skills/ce-sweep/agents/openai.yaml +2 -0
- package/skills/ce-sweep/references/agents/media-analyzer.md +54 -0
- package/skills/ce-sweep/references/interview.md +208 -0
- package/skills/ce-sweep/references/model-tiers.md +13 -0
- package/skills/ce-sweep/references/plan-template.md +62 -0
- package/skills/ce-sweep/references/run.md +126 -0
- package/skills/ce-sweep/references/sources/email.md +57 -0
- package/skills/ce-sweep/references/sources/github-issues.md +59 -0
- package/skills/ce-sweep/references/sources/slack.md +61 -0
- package/skills/ce-sweep/references/state-schema.md +157 -0
- package/skills/ce-sweep/references/subagent-template.md +60 -0
- package/skills/ce-sweep/scripts/analyze_riffrec_zip.py +1306 -0
- package/skills/ce-sweep/scripts/sweep-state.py +793 -0
- package/skills/ce-test-browser/SKILL.md +50 -0
- package/skills/ce-test-browser/references/agent-browser-driver.md +47 -0
- package/skills/ce-test-browser/references/pipeline-orchestration.md +45 -0
- package/skills/ce-test-browser/references/route-and-report.md +129 -0
- package/skills/ce-test-browser/scripts/resolve-port.sh +41 -0
- package/skills/ce-test-xcode/SKILL.md +26 -0
- package/skills/ce-test-xcode/agents/openai.yaml +2 -0
- package/skills/ce-test-xcode/references/setup-and-build.md +32 -0
- package/skills/ce-test-xcode/references/test-and-report.md +64 -0
- package/skills/ce-work/SKILL.md +62 -0
- package/skills/ce-work/references/agents/figma-design-sync.md +27 -0
- package/skills/ce-work/references/agents/implementation-worker.md +17 -0
- package/skills/ce-work/references/cross-model-execution.md +191 -0
- package/skills/ce-work/references/execution-engines.md +64 -0
- package/skills/ce-work/references/execution-strategy.md +73 -0
- package/skills/ce-work/references/implementation-loop.md +107 -0
- package/skills/ce-work/references/implementation-result-schema.json +52 -0
- package/skills/ce-work/references/input-triage.md +50 -0
- package/skills/ce-work/references/non-code-execution.md +23 -0
- package/skills/ce-work/references/return-to-caller.md +33 -0
- package/skills/ce-work/references/review-findings-followup.md +82 -0
- package/skills/ce-work/references/shipping-workflow.md +89 -0
- package/skills/ce-work/references/tracker-defer.md +150 -0
- package/skills/ce-work/references/work-intake.md +51 -0
- package/skills/ce-work/references/workspace-setup.md +27 -0
- package/skills/ce-work/scripts/__pycache__/unit_workspace_jobs.cpython-314.pyc +0 -0
- package/skills/ce-work/scripts/__pycache__/unit_workspace_state.cpython-314.pyc +0 -0
- package/skills/ce-work/scripts/cross-model-work.sh +1385 -0
- package/skills/ce-work/scripts/peer-job-runner.py +2594 -0
- package/skills/ce-work/scripts/unit-workspace.py +215 -0
- package/skills/ce-work/scripts/unit_workspace_ignored.py +93 -0
- package/skills/ce-work/scripts/unit_workspace_integration.py +677 -0
- package/skills/ce-work/scripts/unit_workspace_jobs.py +1216 -0
- package/skills/ce-work/scripts/unit_workspace_lifecycle.py +981 -0
- package/skills/ce-work/scripts/unit_workspace_state.py +1379 -0
- package/skills/ce-work/scripts/unit_workspace_transaction.py +612 -0
- package/skills/ce-worktree/SKILL.md +54 -0
- package/skills/lfg/SKILL.md +59 -0
- package/skills/lfg/references/debug-return.md +25 -0
- package/skills/lfg/references/intake.md +30 -0
- package/skills/lfg/references/next-work-handoff.md +96 -0
- package/skills/lfg/references/plan-brief.md +36 -0
- package/skills/lfg/references/review-followup.md +78 -0
- package/skills/lfg/references/shipping.md +65 -0
- package/skills/lfg/references/stage-routing.md +49 -0
- package/skills/lfg/references/task-visibility.md +11 -0
- package/skills/lfg/references/tracker-defer.md +150 -0
- package/skills/lfg/references/work-return.md +27 -0
- package/skills/wtf/SKILL.md +33 -0
- package/skills/wtf/agents/openai.yaml +2 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Step 1 detail: forge check, PR resolution, chain classification, checkout, watch mode
|
|
2
|
+
|
|
3
|
+
## Prerequisites
|
|
4
|
+
|
|
5
|
+
The loop runs `gh`, `git`, and a bundled Python helper against a local checkout with filesystem access. A harness without those (some sandboxed GUI environments) cannot run this skill — say so and stop rather than half-running.
|
|
6
|
+
|
|
7
|
+
## Step 1: Confirm GitHub, resolve the PR, pick an execution mode
|
|
8
|
+
|
|
9
|
+
**GitHub only.** This skill and everything it delegates to speak GitHub's API (`gh`, review threads, Actions). First confirm the repo is on GitHub: `gh repo view` succeeding is the positive signal (it also covers GitHub Enterprise that `gh` is configured for). If it fails, inspect the remote — `git remote get-url origin` pointing at a `gitlab.*` host means GitLab, `bitbucket.*` means Bitbucket. On any non-GitHub forge (git hosting service), or if `gh` cannot resolve the repo at all, **stop and tell the user ce-babysit-pr is GitHub-only** and that GitLab/other forges are not yet supported. Do not proceed into `gh` calls that will produce confusing errors.
|
|
10
|
+
|
|
11
|
+
Then resolve the target PR from the argument (number/URL) or the current branch. If no open PR exists, report and stop. Resolve draft state with the PR. For an automatic calling-skill handoff without an explicit user watch-mode token, this check must be the plain read `gh pr view --json isDraft`, run before any bootstrap — never `snapshot --start-invocation`, which creates a new invocation and would supersede a watch a user explicitly authorized on that draft. A draft target reports its draft status and stops here, before any bootstrap or watcher, per the "Draft PRs are opt-in" boundary. On user-invoked runs the first snapshot's emitted `pr_is_draft` serves as the ongoing signal.
|
|
12
|
+
|
|
13
|
+
**Check that auto-merge can arm before watching CI.** When the PR expects auto-merge — a check on it arms auto-merge, or the user or calling skill said auto-merge should land it — read the base repository's setting with `gh api repos/<base-owner>/<base-repo> --hostname <host> --jq .allow_auto_merge`. If it is `false`, no CI outcome can merge this PR: report that the repository setting Settings > General > Pull Requests > "Allow auto-merge" is off, and stop before any bootstrap or watcher. In `mode:pipeline` that report is the structured return `{ status: "needs-human" }` from `references/pipeline.md`, with one residual whose `decision_context` names the setting. Changing the setting is the user's call, not this skill's. Once it is on, an auto-merge check that already failed stays red until its job is re-run; Step 5 of the tick handles that.
|
|
14
|
+
|
|
15
|
+
**Automatically classify the target's PR chain; never rely on the user to announce a stack.** The first snapshot and every later poll probe the read-only local manager with `gh stack view --json`, accepting it only when its branch list contains the target PR. If that cannot prove membership, the helper uses a read-only GraphQL fallback. A successful null stack means `pr_chain.manager_status == "absent"`. The specific stack-field schema-unavailable response also means `"absent"` only when a separate read-only lookup resolves the repository's default branch; auth, transport, rate-limit, malformed, other GraphQL, or failed default-branch probes mean `"probe-error"`. When no manager is confirmed, ordinary open-PR base/head relationships distinguish an independent PR from a manual dependency chain. Discovery never runs `gh stack checkout`, imports a stack, switches branches, or changes remote state.
|
|
16
|
+
|
|
17
|
+
**Only when the fresh snapshot has `manager_status == "confirmed"` may stack-wide continuation activate; no other classification authorizes it.** A manual dependency chain never activates stack-wide continuation: keep it target-local even when its base/head topology resembles the manager's ordered branches. `probe-error` also stays target-local and mutation-conservative until a later snapshot positively confirms the manager. Discovery still runs for every babysit — posture does not disable confirmed-manager detection or Step 7 upstack maintenance.
|
|
18
|
+
|
|
19
|
+
For a confirmed managed stack, inspect the manager's ordered entries once before choosing the active layer; this is a read-only look at the order, not multi-PR monitoring. Resolve **posture** per the table in `references/stack.md` before any review/CI work. If posture is still `target` and the requested middle PR has an unsettled downstack layer, offer once to begin at the lowest unsettled non-draft layer and proceed upward (`stack-ready`), with target-only as the alternative; do not silently redirect review/CI work to another PR. If posture is already `stack-ready` or `stack-land` and the requested PR has an unsettled downstack layer, begin at the lowest unsettled non-draft layer without asking (downstack-to-upstack). If all downstack layers are settled, begin on the requested PR. When the requested PR already looks ready or later settles under `target`, offer once to continue to the immediate open non-draft upstack layer if it needs work (accepting selects `stack-ready` for the rest of the run). That one-time offer expands the babysit scope on an already confirmed managed stack — it is **not** a proactive suggestion to create or adopt PR stacks. An explicit request to babysit the managed stack counts as `stack-ready` acceptance, so do not ask redundantly. In `mode:pipeline`, which cannot ask, continue beyond the requested PR only when the invocation already supplied `posture:stack-ready`, `posture:stack-land`, or equivalent stack-wide scope; otherwise return the next candidate as a residual.
|
|
20
|
+
|
|
21
|
+
Once `stack-ready` or `stack-land` is in effect, that posture authorizes babysitting each layer of the confirmed managed stack in turn without asking again at each layer. Keep one active PR target and one watcher. At each transition: revalidate manager membership and ordered state, stop the old watcher, switch/check out the next immediate layer, then initialize its own snapshot state with `--continue-invocation` and the same three recorded values on the flags the first snapshot used — `--invocation-id "$RUN_INVOCATION_ID" --session-started-at "$RUN_STARTED_AT" --invocation-budget-seconds "$RUN_BUDGET_SECONDS"` (the anchor flag is `--session-started-at`, not `--invocation-started-at`) — plus `--continue-dead-time-seconds <prior layer's `invocation_dead_time_seconds`>` so the shared active-time budget carries the suspended time already excluded on earlier layers (each layer's state dir accumulates its own dead time, so without this the new layer would count that prior suspend as active) — **and re-state the same `posture:` value on the continue invocation**. The invocation budget is not renewed per layer. Never skip past a draft or enter it unless the user explicitly included that draft; never advance past a layer with a `needs-human` blocker. Stop at the first draft outside scope, human-blocked layer, end of the stack, budget, or user stop. Reconfirm `manager_status == "confirmed"` before every cross-PR transition — loss of positive confirmation ends stack-wide continuation rather than degrading into manual-chain behavior.
|
|
22
|
+
|
|
23
|
+
**Verify the local checkout is the PR's head *branch* before any delegated mutation.** `ce-resolve-pr-feedback` and `ce-debug` commit and push the **currently checked-out branch** — so a checkout that isn't the PR's head branch makes their fixes fail to push or land on the wrong branch. A matching `HEAD` **SHA is not sufficient**: a detached HEAD or a *different* local branch that happens to point at the PR head SHA passes a SHA check yet still can't push the PR's branch. So verify the checkout is actually on the PR's head **ref with a matching upstream**: resolve `gh pr view <ref> --json headRefName,headRefOid,isCrossRepository`, and confirm `git branch --show-current` equals `headRefName` (and the upstream tracks the PR head repo). The robust default is to **just run `gh pr checkout <ref>` before mutating** (it checks out the head branch and sets tracking, and handles fork heads it can push to). If you cannot — **no push access to the PR's head ref** (you have it when the head repo is yours, when you have write access to it, or on someone else's fork when `maintainerCanModify` is true) or a dirty checkout — **stop and tell the user to checkout the PR's branch** rather than mutating the wrong one. Switching a *clean* checkout to the PR's branch is not a reason to ask; do it. Babysitting the current branch's own PR (the common case) already satisfies this.
|
|
24
|
+
|
|
25
|
+
Then decide **how the watch sustains itself** — a skill can't be re-invoked by magic once its turn ends, so *you* set up the loop. **The default is a self-sustaining, in-session watch: you do not do one tick and hand back a resume command.** Read `references/watch-loop.md` once here for the mechanics; later steps cite its sections rather than re-reading it. Then:
|
|
26
|
+
|
|
27
|
+
**User-runnable resume syntax.** Whenever this skill prints or copies a resume invocation, default to `/ce-babysit-pr <url>` and, when the run posture is not `target`, append the same `posture:stack-ready` or `posture:stack-land` token so checkpoint / durable / session re-entry keeps stack scope. Use `$ce-babysit-pr <url> [posture:…]` only when the active host is Codex or explicitly documents dollar-prefixed skill invocation. Render only the invocation as inline code and output one form only.
|
|
28
|
+
|
|
29
|
+
- **Self-sustaining in-session watch (default).** Keep monitoring in the current session until a stop condition is met. Run `pr-snapshot watch`, the deterministic change detector, and wait for its `BABYSIT_WAKE` output using the harness's tools. The detector polls without agent tokens; each wake returns control to this agent for one tick of judgment and sub-skill calls, followed by re-arming the detector. Never collapse those judgments into a shell script. `references/watch-loop.md` defines scheduling and wake handling.
|
|
30
|
+
- **Checkpoint.** Use checkpoint mode only when the user requests it or the harness cannot keep the session active while waiting for the detector’s output. Run exactly one tick, persist, report, and print the resume invocation. Say monitoring is paused. Never fake a loop with a foreground `sleep` or by ending the turn and promising to continue.
|
|
31
|
+
- **Pipeline** (`mode:pipeline`, set by an orchestrator like `lfg`) — run **bounded synchronous ticks in-line**: the orchestrator is the scheduler, so loop ticks yourself (snapshot → act → re-snapshot) until the **pipeline stop** (Step 3), then return. Fully non-interactive. See `references/pipeline.md` for what differs — a different stop condition, residuals returned in the structured result, and a structured return — and read `references/watch-loop.md` for its bound.
|
|
32
|
+
|
|
33
|
+
**Durability.** The in-session watch is session-bound; if the session closes, re-invoking with the host-rendered resume syntax resumes cleanly (state is fully persisted on disk). For an unattended watch that must outlive the session (days), move to a durable scheduler where one exists — Grok `scheduler_create --durable`, or a cron running `<harness-cli> exec '<host-rendered resume invocation>'` — accepting that a fresh headless run reconstructs from disk and loses this conversation's context (persist consequential decisions so it does not re-litigate). If the user passed a mode, honor it; otherwise pick per harness capability, state it in one line, and proceed.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Managed-stack CLI recipes
|
|
2
|
+
|
|
3
|
+
Load this file when the active run uses a confirmed managed stack (`manager_status == "confirmed"`) and needs `gh stack` command recipes. Soft-depend on the CLI: if `gh stack` is missing or exits unavailable (e.g. code 9), surface a clear residual — do not invent managed membership from topology.
|
|
4
|
+
|
|
5
|
+
Always non-interactive. Prefer JSON/view probes and explicit branch names; never rely on interactive prompts. Substitute `<tracking-remote>` with the stack branches' actual tracking remote (often `origin`, but may be `upstream` or a fork remote) — never hard-code `origin` when SKILL.md already resolved a different tracking remote.
|
|
6
|
+
|
|
7
|
+
## After an owned push on the active layer (dependents exist)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
gh stack rebase "<first-open-dependent-branch>" --upstack --no-trunk --remote <tracking-remote>
|
|
11
|
+
gh stack push --remote <tracking-remote>
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Starting at the first dependent excludes the active target from the cascading rebase. Quote the branch name — git branch names may contain shell metacharacters. On conflict: `gh stack rebase --abort`, then surface a needs-human / stack-sync residual.
|
|
15
|
+
|
|
16
|
+
## Discover order / next open layer
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
gh stack view --json
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Land one prefix (only under `posture:stack-land`)
|
|
23
|
+
|
|
24
|
+
Merge the **bottom-most open settled** PR — `gh stack merge <PR>` merges the full stack prefix through that PR atomically. Never merge an upstack active PR while downstack PRs remain open when single-prefix landing is intended.
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
gh stack merge <BOTTOM_MOST_OPEN_SETTLED_PR> --yes --squash
|
|
28
|
+
gh stack sync --remote <tracking-remote>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Re-probe the landed PR before advancing: on merge-queue bases the CLI may succeed after enqueue while the PR stays OPEN — keep watching or return a queued residual until `pr_state` is `MERGED`. Only then treat the just-merged PR as a **layer transition** (stop watcher, re-probe, continue next open non-draft needing work with posture restated) — not a run-level Terminal stop for this babysit invocation.
|
|
32
|
+
|
|
33
|
+
## Forbidden on managed stack members
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
gh pr merge …
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Use `gh stack merge` only. Under `posture:target` and `posture:stack-ready`, print the exact merge command when reporting ready-as-next; do not execute it.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Managed stacks: posture, discovery, transitions, upstack maintenance, landing
|
|
2
|
+
|
|
3
|
+
## Posture (full text)
|
|
4
|
+
|
|
5
|
+
Hold exactly one posture for the run. It is written as `posture:target|stack-ready|stack-land` — distinct from `watch` / `checkpoint` / `mode:pipeline` / duration. Re-state the same posture on every managed-stack layer `--continue-invocation` transition alongside the existing budget flags.
|
|
6
|
+
|
|
7
|
+
| Posture | Behavior |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| `target` | Only the named PR. Stop at looks-ready. May offer stack-wide once when a confirmed multi-layer managed stack needs work; decline keeps the target-local stop. Never merges. |
|
|
10
|
+
| `stack-ready` | Once the active layer is **quiescent** — zero actionable backlog (`counts.threads/comments/ci == 0`), **no standing residual** (no open `needs-human`, no `blocked-failing` — `has_failing_checks` with nothing left to dispatch — no `stack-blocked`, no open or claimed currency item), and no delegate work in flight — automatically continue to the next open non-draft upstack layer that needs work, even while the quiescent layer's CI is still running or its settle window has not elapsed. Lower layers stay under the watcher's downstack probe; the lowest one that re-opens pulls the walk back. Never merges. Persist for the run; do not re-ask each layer. |
|
|
11
|
+
| `stack-land` | Like `stack-ready` for traversal. Selecting or handing off `posture:stack-land` **is** run-level land authorization. Landing still requires **settled**, not merely quiescent: once the bottom-most open layer looks ready (full Step 3 gate), merge it via `gh stack merge` + `gh stack sync`, then continue. |
|
|
12
|
+
|
|
13
|
+
**Selection:** the user named one PR with no stack language → default `target`, but if it is a confirmed multi-layer managed stack, ask once (only this PR vs whole stack to ready). Intent to own/finish the stack → `stack-ready`. Intent to land/merge when green → `stack-land`. Prefer intent over keyword matching. An explicit "babysit the managed stack" request selects `stack-ready` (or `stack-land` when land intent is also clear). In `mode:pipeline`, use only the posture/scope already supplied on the invocation — never ask.
|
|
14
|
+
|
|
15
|
+
When a confirmed managed stack is in play and you need CLI recipes, read `references/stack-commands.md`.
|
|
16
|
+
|
|
17
|
+
## Discovery and continuation
|
|
18
|
+
|
|
19
|
+
**Automatically classify the target's PR chain; never rely on the user to announce a stack.** The first snapshot and every later poll probe the read-only local manager with `gh stack view --json`, accepting it only when its branch list contains the target PR. If that cannot prove membership, the helper uses a read-only GraphQL fallback. A successful null stack means `pr_chain.manager_status == "absent"`. The specific stack-field schema-unavailable response also means `"absent"` only when a separate read-only lookup resolves the repository's default branch; auth, transport, rate-limit, malformed, other GraphQL, or failed default-branch probes mean `"probe-error"`. When no manager is confirmed, ordinary open-PR base/head relationships distinguish an independent PR from a manual dependency chain. Discovery never runs `gh stack checkout`, imports a stack, switches branches, or changes remote state.
|
|
20
|
+
|
|
21
|
+
**Only when the fresh snapshot has `manager_status == "confirmed"` may stack-wide continuation activate; no other classification authorizes it.** A manual dependency chain never activates stack-wide continuation: keep it target-local even when its base/head topology resembles the manager's ordered branches. `probe-error` also stays target-local and mutation-conservative until a later snapshot positively confirms the manager. Discovery still runs for every babysit — posture does not disable confirmed-manager detection or Step 7 upstack maintenance.
|
|
22
|
+
|
|
23
|
+
For a confirmed managed stack, inspect the manager's ordered entries once before choosing the active layer; this is a read-only look at the order, not multi-PR monitoring. Resolve **posture** per the table above before any review/CI work. If posture is still `target` and the requested middle PR has an unsettled downstack layer, offer once to begin at the lowest unsettled non-draft layer and proceed upward (`stack-ready`), with target-only as the alternative; do not silently redirect review/CI work to another PR. If posture is already `stack-ready` or `stack-land` and the requested PR has an unsettled downstack layer, begin at the lowest unsettled non-draft layer without asking (downstack-to-upstack). If all downstack layers are settled, begin on the requested PR. When the requested PR already looks ready or later settles under `target`, offer once to continue to the immediate open non-draft upstack layer if it needs work (accepting selects `stack-ready` for the rest of the run). That one-time offer expands the babysit scope on an already confirmed managed stack — it is **not** a proactive suggestion to create or adopt PR stacks. An explicit request to babysit the managed stack counts as `stack-ready` acceptance, so do not ask redundantly. In `mode:pipeline`, which cannot ask, continue beyond the requested PR only when the invocation already supplied `posture:stack-ready`, `posture:stack-land`, or equivalent stack-wide scope; otherwise return the next candidate as a residual.
|
|
24
|
+
|
|
25
|
+
Once `stack-ready` or `stack-land` is in effect, that posture authorizes babysitting each layer of the confirmed managed stack in turn without asking again at each layer. Keep one active PR target and one watcher. At each transition: revalidate manager membership and ordered state, stop the old watcher, switch/check out the next immediate layer, then initialize its own snapshot state with `--continue-invocation` and the same three recorded values on the flags the first snapshot used — `--invocation-id "$RUN_INVOCATION_ID" --session-started-at "$RUN_STARTED_AT" --invocation-budget-seconds "$RUN_BUDGET_SECONDS"` (the anchor flag is `--session-started-at`, not `--invocation-started-at`) — plus `--continue-dead-time-seconds <prior layer's `invocation_dead_time_seconds`>` so the shared active-time budget carries the suspended time already excluded on earlier layers (each layer's state dir accumulates its own dead time, so without this the new layer would count that prior suspend as active) — **and re-state the same `posture:` value on the continue invocation**. The invocation budget is not renewed per layer. Never skip past a draft or enter it unless the user explicitly included that draft; never advance past a layer with a `needs-human` blocker. Stop at the first draft outside scope, human-blocked layer, end of the stack, budget, or user stop. Reconfirm `manager_status == "confirmed"` before every cross-PR transition — loss of positive confirmation ends stack-wide continuation rather than degrading into manual-chain behavior.
|
|
26
|
+
|
|
27
|
+
## Pre-push baseline
|
|
28
|
+
|
|
29
|
+
**Managed-stack pre-push baseline.** Before invoking a delegate that may push the active target in a confirmed managed stack, record a recoverable baseline from a fresh `gh stack view --json`: the manager-ordered open branches at or above the target (target plus open dependents) and each branch's current remote-tracking OID on the tracking remote. Require a clean worktree and still-confirmed manager membership for the target/current branch. If either precondition fails, this is a true stop for the active invocation in every mode: do not invoke a delegate, run another tick, or arm/re-arm a watcher; state the residual and give the host-rendered resume invocation. Do not stop for missing atomic multi-ref push proof — current `gh stack push` may update branches non-atomically (`github/gh-stack#216`); prefer all-or-none when an installed manager later proves atomic push, but always re-probe after push rather than assuming it.
|
|
30
|
+
|
|
31
|
+
## Step 7: upstack maintenance after an authorized target push
|
|
32
|
+
|
|
33
|
+
7. **After an authorized target-head push in a confirmed managed stack, preserve the upstack before resuming the watch.** This is manager-owned maintenance implicitly authorized by babysitting a managed layer, not permission for arbitrary history edits. Retain the delegate-reported pushed SHA, re-run read-only `gh stack view --json`, and require that it still identifies the target PR on the current local branch. Require a clean worktree, fetch the target branch from its tracking remote, and verify both the target's local head and remote-tracking tip still equal that pushed SHA; a moved target becomes an upstack residual, never something this step rebases or overwrites. From the fresh manager order, select the first open dependent branch immediately above the target. If there is none, no cascade is needed. If any precondition fails, leave an upstack residual without importing, checking out, or guessing at the stack. Otherwise run `gh stack rebase "<first-dependent-branch>" --upstack --no-trunk --remote <tracking-remote>`, verify the target local head is still unchanged at the pushed SHA, then run `gh stack push --remote <tracking-remote>` only — never raw `git push --force`. Starting at the first dependent excludes the target from the cascading rebase; `--no-trunk` confines the operation to inter-branch propagation and avoids a stale local trunk. After push success or rejection, fetch and re-probe: verify the target still equals the delegate-reported pushed SHA (already checked above); for every **open dependent** in the baseline, compare local and remote-tracking heads to the recorded pre-push OIDs and expected post-rebase tips — do **not** treat the target's intentional post-push OID change as divergence. Do not assume all-or-none. Treat already-updated dependent remotes as observed progress; name the first rejected or divergent dependent layer and return a precise recoverable upstack residual (retry from that layer after the cause is fixed). Never claim stack readiness until manager order, ancestry, review, and CI are re-proven on every current head. If the rebase conflicts, immediately run `gh stack rebase --abort` and report a `needs-human`/stack-sync residual — do not decide conflict semantics in another PR layer. If the target moved or a lease rejects unexpected remote state, do not retry with raw force; report the residual. This step never applies to a manual dependency chain, and the delegated target fixers never perform it.
|
|
34
|
+
|
|
35
|
+
During accepted managed-stack continuation there is one watcher and one mutated target, but the watcher probes below: arm it with `--downstack-pr <N>` for every open lower layer in the manager order (repeat the flag per PR). It wakes `downstack-actionable` (with `downstack_prs`) when a lower layer gains a new thread, comment, failing check, or head beyond what was there at arm time. On that wake, stop the watcher and return to the **lowest** re-opened non-draft layer with `--continue-invocation`, handle it, cascade, and walk back up. Also recheck the manager's ordered entries and downstack quiescence at every layer transition, immediately before an active-target mutation, and at the looks-ready decision. Never mutate two layers concurrently: a downstack return waits for any in-flight delegate on the current layer to finish. If manager confirmation disappears, end continuation and report the classification residual.
|
|
36
|
+
|
|
37
|
+
## Layer transitions and the stack-land land step
|
|
38
|
+
|
|
39
|
+
**A confirmed managed-stack layer stop may be a run transition.** Under `stack-ready` the transition condition is quiescence (table above; a layer carrying any standing residual is not quiescent, so the walk never leaves a red or human-blocked layer behind), not settle. When the active layer has nothing actionable, report its state in one line ("quiescent, CI running" / "ready as next"), revalidate the manager and downstack, then advance to the immediate next open non-draft layer that needs work without asking again; pass through already-quiescent non-draft layers only after freshly confirming each one. Its settle window keeps running under the downstack probe; the final report states each layer's settled/quiescent state from a fresh probe. Under `stack-land`, run the land step below **before** any plain advance once the bottom-most open layer is settled (do not skip merge and walk upstack while a settled prefix is still OPEN); a merely quiescent bottom layer may be walked past but not landed. Stop before a draft unless the user explicitly included that draft in scope. Under `target`, if no continuation decision has been made and this is the originally requested PR, use Step 1's one-time offer. A decline makes the target-local looks-ready result the final stop. A `needs-human` layer remains the active layer — keep watching its independent streams, but do not advance upstack. These transition rules apply only while a fresh probe still reports `manager_status == "confirmed"`; independent PRs, manual chains, and probe errors always use the target-local stop.
|
|
40
|
+
|
|
41
|
+
**`stack-land` land step (after settle / pipeline green gates, before any plain advance).** When posture is `stack-land` and the active layer looks ready (interactive settle) **or** satisfies the pipeline success conditions in `references/pipeline.md`, identify the **bottom-most open settled** PR in the manager order (CLI `gh stack merge <PR>` merges the full prefix through that PR atomically — never merge an upstack active PR while downstack PRs remain open when single-prefix landing is intended). Read `references/stack-commands.md` if needed, then run `gh stack merge <that-PR> --yes --squash` followed by `gh stack sync --remote <tracking-remote>`. Re-probe the landed PR's `pr_state` / queue status before treating the land as complete: on merge-queue bases, `gh stack merge` may succeed after **enqueue** while the PR stays OPEN — keep watching that PR (or return a queued residual in `mode:pipeline`) until it is actually `MERGED`; do not advance or declare pipeline success on a still-open queued prefix. Only when re-probe shows `MERGED`, treat that just-landed MERGED as a **managed-stack layer transition**: stop the watcher, re-probe the stack, and `--continue-invocation` onto the next open non-draft needing work with the same posture restated — **not** a run-level Terminal true stop for this babysit invocation. Distinguish that from externally observed `MERGED`/`CLOSED` on a layer this run did not just land (those remain true Terminal stops). On merge/sync failure, report a needs-human / stack residual; do not fall back to `gh pr merge`. In `mode:pipeline` with `posture:stack-land`, do **not** return success on a green settled prefix until this land+sync has completed to actual `MERGED` (or failed into a residual); after a successful land transition, continue the pipeline bound on the next layer.
|
|
42
|
+
|
|
43
|
+
CLI recipes: `references/stack-commands.md`.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# The tick: snapshot, watch, marks, ordering
|
|
2
|
+
|
|
3
|
+
Read this before the first snapshot. Shell state does not persist between tool calls; every command re-sets its own variables.
|
|
4
|
+
|
|
5
|
+
## Step 2: Run one tick
|
|
6
|
+
|
|
7
|
+
A tick is fully resumable from disk, so any re-invocation drives it: a scheduler, `/loop`, or the user re-running the skill an hour later. Set `SKILL_DIR` to the directory containing this SKILL.md, then snapshot both streams in one batch:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
SKILL_DIR="<absolute path of the directory containing the SKILL.md you just read>";
|
|
11
|
+
SCRATCH_ROOT="/tmp/compound-engineering-$(id -u)"; [ ! -L "$SCRATCH_ROOT" ] && (umask 077; mkdir -p "$SCRATCH_ROOT") 2>/dev/null && [ ! -L "$SCRATCH_ROOT" ] && [ -O "$SCRATCH_ROOT" ] && [ -w "$SCRATCH_ROOT" ] || SCRATCH_ROOT="${TMPDIR:-/tmp}/compound-engineering-$(id -u)"; [ ! -L "$SCRATCH_ROOT" ] && (umask 077; mkdir -p "$SCRATCH_ROOT") && [ ! -L "$SCRATCH_ROOT" ] && [ -O "$SCRATCH_ROOT" ] && chmod 700 "$SCRATCH_ROOT" || { echo "unsafe scratch root: $SCRATCH_ROOT" >&2; exit 1; };
|
|
12
|
+
STATE_DIR="$SCRATCH_ROOT/ce-babysit-pr/<host>-<owner>-<repo>-<N>";
|
|
13
|
+
(umask 077; mkdir -p "$STATE_DIR") || exit 1; chmod 700 "$STATE_DIR" || exit 1;
|
|
14
|
+
PY="$(for c in python3 python py; do command -v "$c" >/dev/null 2>&1 && "$c" -c '' >/dev/null 2>&1 && { echo "$c"; break; }; done)"; [ -n "$PY" ] || { echo "no working Python 3 interpreter on PATH" >&2; exit 1; };
|
|
15
|
+
"$PY" "$SKILL_DIR/scripts/pr-snapshot" snapshot --pr <N> --repo <[host/]owner/repo> --state-dir "$STATE_DIR" --start-invocation --invocation-budget-seconds <seconds>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
This is the only command that may start a budget. Use the user's requested duration when supplied; otherwise use the fixed **8-hour default** (`28800`). The budget is spent in **active watch-capability time**, not raw wall-clock time. While the in-session watch runs, a span where the whole process was suspended (a closed laptop) is excluded from `invocation_elapsed_seconds`, so time the agent could not watch does not drain the cap. Detection is coarse: an activity gap wider than a threshold well above the poll interval is charged to dead time, while ordinary polls, agent ticks, and human-blocked waits keep counting. A separate **3-calendar-day wall-clock backstop** caps every invocation regardless of excluded dead time; it is the ceiling for a stale PR or a zombie watch. Checkpoint mode and the durable/cron path have no continuous poll cadence, so they keep wall-clock accounting. Record the output's `invocation_id`, `invocation_started_at`, and `invocation_budget_seconds` as `RUN_INVOCATION_ID`, `RUN_STARTED_AT`, and `RUN_BUDGET_SECONDS`. Require `invocation_elapsed_seconds <= 60`; otherwise fail before arming a watcher. Durable PR dispositions, dedup, and trajectory survive a new invocation, but its budget clock does not. Every later snapshot and watch arm must present all three recorded values; the helper rejects a missing or mismatched token, anchor, or budget. A managed-stack layer transition additionally uses `--continue-invocation`. Never use `--start-invocation` after this first snapshot: re-arms, mutations, retries, review/CI rounds, and stack transitions share one non-rolling budget, and a re-arm preserves accumulated dead time rather than resetting it.
|
|
19
|
+
|
|
20
|
+
Treat every fresh `snapshot` as the canonical source of truth for review-thread state; its bundled fetch paginates the full thread connection. Never replace it with a one-shot `reviewThreads(first:N)` result. If a direct diagnostic query is genuinely necessary, follow `pageInfo` until `hasNextPage == false` before drawing a count or unresolved-state conclusion.
|
|
21
|
+
|
|
22
|
+
**In the self-sustaining watch, back the tick with the background change-detector.** `pr-snapshot watch` runs that same fetch→diff on an interval with **no agent tokens** and prints a single `BABYSIT_WAKE {reason,url,...}` line *only* when there is work to inspect or a stop/residual condition, then exits. Work reasons: `actionable` (an unresolved thread or failed CI) and `feedback-candidate` (a non-thread body that still needs the resolver's judgment). Stop/residual reasons: `terminal`, `blocked-external`, `blocked-external-drained`, `blocked-failing`, `base-ref-blocked`, `unrequested-base-merge`, `downstack-actionable` (a lower managed-stack layer re-opened; see `references/stack.md`), `stack-blocked`, `needs-human`, `merge-ready` after the settle window, `max-runtime`, `stop-signal`, and `invocation-superseded`. A `feedback-candidate` wake is not a detector claim that a fix or reply is required: a resolver pass that silently drops the body is a normal classification outcome, not a false positive. Keep the session active while waiting for that line with the harness's tools (Step 1); on the sentinel, run the tick below:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
SKILL_DIR="<absolute path of this skill's directory>"; SCRATCH_ROOT="/tmp/compound-engineering-$(id -u)"; [ ! -L "$SCRATCH_ROOT" ] && (umask 077; mkdir -p "$SCRATCH_ROOT") 2>/dev/null && [ ! -L "$SCRATCH_ROOT" ] && [ -O "$SCRATCH_ROOT" ] && [ -w "$SCRATCH_ROOT" ] || SCRATCH_ROOT="${TMPDIR:-/tmp}/compound-engineering-$(id -u)"; [ ! -L "$SCRATCH_ROOT" ] && (umask 077; mkdir -p "$SCRATCH_ROOT") && [ ! -L "$SCRATCH_ROOT" ] && [ -O "$SCRATCH_ROOT" ] && chmod 700 "$SCRATCH_ROOT" || { echo "unsafe scratch root: $SCRATCH_ROOT" >&2; exit 1; }; STATE_DIR="$SCRATCH_ROOT/ce-babysit-pr/<host>-<owner>-<repo>-<N>"; (umask 077; mkdir -p "$STATE_DIR") || exit 1; chmod 700 "$STATE_DIR" || exit 1; RUN_INVOCATION_ID="<invocation_id>"; RUN_STARTED_AT="<invocation_started_at>"; RUN_BUDGET_SECONDS="<invocation_budget_seconds>";
|
|
26
|
+
PY="$(for c in python3 python py; do command -v "$c" >/dev/null 2>&1 && "$c" -c '' >/dev/null 2>&1 && { echo "$c"; break; }; done)"; [ -n "$PY" ] || { echo "no working Python 3 interpreter on PATH" >&2; exit 1; };
|
|
27
|
+
"$PY" "$SKILL_DIR/scripts/pr-snapshot" watch --pr <N> --repo <[host/]owner/repo> --state-dir "$STATE_DIR" --interval 150 --invocation-id "$RUN_INVOCATION_ID" --session-started-at "$RUN_STARTED_AT" --invocation-budget-seconds "$RUN_BUDGET_SECONDS"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
In a confirmed managed stack under `stack-ready`/`stack-land`, append `--downstack-pr <N>` for every open lower layer to that arm so the watcher probes below (`references/stack.md`).
|
|
31
|
+
|
|
32
|
+
Watch ownership is **latest-valid-watcher-wins**: the newest invocation that completes a valid first snapshot takes over. A newer invocation first cancels any older invocation still preflighting, but does not disturb the active watcher; only after a successful first snapshot does it atomically supersede and gracefully terminate that active process. Every wake and snapshot carries `watch_generation`. On delivery, compare the wake's generation with one fresh snapshot. A stale wake is discarded and coalesced into that current read. A current wake whose attention set (the items still needing action) already cleared is also a no-op rather than another tick. An `invocation-superseded` wake means another explicit invocation now owns the durable state: end the old loop without acting or re-arming it. Re-arming with the same invocation token preserves `last_change_at`, `invocation_started_at`, and `invocation_budget_seconds`; it cannot restart or extend either timer.
|
|
33
|
+
|
|
34
|
+
Do **not** pass `--settle-seconds` or `--blocked-external-drain-seconds` on the ordinary arm. The script's 300s default is the initial merge-ready settle window. Step 3 alone sets `--settle-seconds` after a rejected `merge-ready` wake, and `--blocked-external-drain-seconds` after a `blocked-external` (approval-gate) wake begins the bounded review drain.
|
|
35
|
+
|
|
36
|
+
**Shell state does not persist between separate tool calls.** `SKILL_DIR` and `STATE_DIR` are set only for the command they appear in. The later `mark` calls (Steps 3 and 5) run as their own invocations, so re-set both inline in each of those commands, or pass the absolute paths directly. A bare `$SKILL_DIR` in a fresh call is empty and resolves to the wrong path.
|
|
37
|
+
|
|
38
|
+
**`<host>` in `STATE_DIR` is required for GitHub Enterprise.** Derive it from the PR URL's host (or `gh repo view --json url`); use the same value in every `mark`. Keying only by `<owner>-<repo>-<N>` would let two PRs with the same `owner/repo#N` on *different* hosts (github.com + a GHE instance) share one `state.json`, so one host's dispositions or dispatched CI would silence or contaminate the other's actionable set. On plain github.com the host segment is just `github.com`. **Pass the same host in `--repo <host>/<owner>/<repo>`** (the documented `[HOST/]OWNER/REPO` selector) so `pr-snapshot`'s first `gh pr view`, which runs before it parses the URL host, queries the right host instead of the checkout's default `github.com`.
|
|
39
|
+
|
|
40
|
+
The snapshot emits the **attention set**: the items that still need action. It contains unresolved threads you have not yet acted on, **non-thread feedback candidates** (top-level PR comments + review-submission bodies) you have not yet classified, and failing checks on the current head you have not yet dispatched, plus the exact current `branch_currency` item and its `attention` route. It also emits `pr_state`, `mergeable`, `merge_state_status`, `base`, `base_ref_blocker`, `host_branch_update_capability`, `branch_currency_blocker`, `review_decision`, `head_sha`, `head_changed`, `quiet_seconds`, `invocation_elapsed_seconds`, `invocation_remaining_seconds`, `persisted_state_age_seconds`, `checks_awaiting_approval` / `blocked_external`, and the head-scoped review-drain facts `blocked_external_first_seen_at`, `blocked_external_review_last_activity_at`, `blocked_external_review_quiet_seconds`, and `blocked_external_review_moved_this_tick` (see Step 3). It also emits a `pr_chain` block and a `trajectory` block of cross-tick facts: `check_recur_max`, `recurring_checks`, `unresolved_trend`, `new_threads_this_tick`, `stream_alternations`, `heads_since_progress`, `invariant_rounds`.
|
|
41
|
+
|
|
42
|
+
`base.historical_oid` is GitHub's historical `baseRefOid`; it is diagnostic and is not the current base tip. Current-base identity requires the independent exact Git ref (`base.oid`) to match the PR `baseRef.target.oid` (`base.graphql_oid`). For a mergeable result, the generated `potentialMergeCommit` must also name that current base and the observed PR head as its two parents. Only that proven binding emits `base.identity == "current"`. A base movement race emits `race`, temporary merge-commit generation emits `mergeability-pending`, and a failed or malformed probe emits `probe-error`. These transient blockers disable `mergeability_certain` and re-poll. A `DIRTY` / `CONFLICTING` result may omit `potentialMergeCommit`; matching current-base observations still make the conflict result usable. Invocation time and persisted-state age are separate; never report one as the other. `pr_chain` carries two independent axes, `manager_status` (`confirmed|absent|probe-error`) and `relationship_status` (`dependent|independent|probe-error`), plus the manager source, target/upstack freshness, ordered entries, and ordinary parent/dependent PRs when available.
|
|
43
|
+
|
|
44
|
+
The JSON field remains `actionable.comments` for the claim→act→confirm protocol, but its members are candidates awaiting semantic classification, not detector-proven action items. For non-thread feedback, the deterministic fetch excludes only empty bodies. A nonempty comment from the PR author stays in, since an author asking for a change on their agent-opened PR is ordinary feedback; loop prevention is the `dispatched` mark, not identity. The fetch does **not** decide from content, bot identity, or comment-vs-review surface whether an external message is valid feedback; `ce-resolve` applies that judgment. The snapshot **never** marks a shown item handled just from observing it. An item stays in the attention set until you confirm you acted or classified it (`mark`) or remote truth removes it (a resolved thread drops out of the fetch). Every `mark` write must present the same `RUN_INVOCATION_ID`, `RUN_STARTED_AT`, and `RUN_BUDGET_SECONDS`; a stale resolver tick must fail before it can silence work in a replacement invocation. So a crashed, failed, or superseded resolve pass leaves its items in the set for the next tick. The state schema and the claim→act→confirm protocol are in `references/watch-loop.md`; read it before acting if this session has not already loaded it.
|
|
45
|
+
|
|
46
|
+
**The `trajectory` is facts, not a verdict. You hand it to the delegated skills (the leaves: `ce-debug` and `ce-resolve-pr-feedback`), and they judge convergence.** The triggers are: `check_recur_max >= 2`; `stream_alternations >= 3`; a rising `unresolved_trend` with `new_threads_this_tick > 0` across passes; `heads_since_progress >= 2`; or any `invariant_rounds[].rounds >= 2` (a key at 2 recorded rounds means the next fix would be its third). When the trajectory crosses one, pass it to that tick's `ce-debug`/`ce-resolve-pr-feedback` invocation as **mandatory input** **before** that leaf mutates, and let the leaf decide whether this is ordinary progress or genuine non-convergence. A leaf may then return a `needs-human` residual that parks the *whole stream*, for example an emergent CI trade-off, a wrong-approach nitpick cluster, or a third invariant round. Never declare non-convergence yourself. The trigger→route→park→re-open protocol is in `references/watch-loop.md` (**Non-convergence**); read it before acting on this if this session has not already loaded it.
|
|
47
|
+
|
|
48
|
+
## Ordering invariant (full text)
|
|
49
|
+
|
|
50
|
+
**The ordering invariant (this is the whole point):**
|
|
51
|
+
|
|
52
|
+
1. **Terminal check first.** If `pr_state` is `MERGED` or `CLOSED`, stop and report; the loop is done. The one **exception**: when this run just completed an authorized `stack-land` merge on that PR, treat that MERGED outcome as a managed-stack layer transition (see Step 3's stack-land land step), not a run-level Terminal stop.
|
|
53
|
+
2. **Capture the head SHA now** (`git rev-parse HEAD` or the snapshot's `head_sha`) so you can tell later whether the comment pass pushed.
|
|
54
|
+
|
|
55
|
+
**Managed-stack pre-push baseline.** Before invoking a delegate that may push the active target in a confirmed managed stack, record a recoverable baseline from a fresh `gh stack view --json`: the manager-ordered open branches at or above the target (the target plus its open dependents) and each branch's current remote-tracking OID on the tracking remote. Require a clean worktree and still-confirmed manager membership for the target/current branch. If either precondition fails, stop the active invocation in every mode: do not invoke a delegate, run another tick, or arm/re-arm a watcher; state the residual and give the host-rendered resume invocation. Do not stop for missing atomic multi-ref push proof; current `gh stack push` may update branches non-atomically (`github/gh-stack#216`). Prefer all-or-none when an installed manager later proves atomic push, but always re-probe after push rather than assuming it.
|
|
56
|
+
|
|
57
|
+
3. **Feedback before CI.** If the attention set has **either** unresolved threads **or** non-thread feedback candidates (`counts.threads > 0` or `counts.comments > 0`), invoke `ce-resolve-pr-feedback` **once**, in full mode **with `mode:pipeline`**. Pass the resolved PR ref: the base `[HOST/]OWNER/REPO#N` or the full PR URL from the snapshot's `url`. That way a fork→upstream PR resolves against the **upstream base**, not the fork checkout's `origin`, which would query the wrong PR namespace. `mode:pipeline` is non-interactive: the resolver parks any `needs-human` on the thread and returns it as a structured residual instead of pausing on a blocking user question, which would stall the autonomous watch. This is the same reason step 5 (CI on the current head) invokes `ce-debug mode:pipeline`. The resolver re-fetches and judges *all* feedback (inline threads, review bodies, and top-level comments) and is idempotent on empty. The `actionable.comments` field contains the top-level and review-body candidates the resolver would otherwise not know the loop cares about; a Changes-Requested review body or a bare top-level "please rename X" with **no inline thread** must still trigger a pass. **When the review trigger above is crossed (rising backlog, new-item arrivals, a repeating cluster, or any `invariant_rounds[].rounds >= 2`), pass the `trajectory`** so the resolver can judge a treadmill or wrong-approach nitpick cluster and return one approach-level `needs-human` instead of fixing forever. **And when the recurring items are *valid* and share one root and fix, request a bounded-class assessment**, so it consolidates the equivalent sites this PR touched into a single fix rather than fixing one per head (`references/watch-loop.md`, Non-convergence). An `invariant_rounds[].rounds >= 2` trigger routes that trajectory **before** this pass may fix, commit, or push. One resolve pass per tick; never fan out multiple.
|
|
58
|
+
|
|
59
|
+
For any delegate result, process its typed `needs-human` residuals through one shared step. Immediately render every complete payload under `## Needs your decision`, preserve it unchanged for caller return, and write it to an OS-temp JSON file. Persist each residual once: the snapshot validates the full schema, freezes every source's current observation, and publishes one decision under the locked state write. It emits the unchanged payload in canonical `needs_human_residuals` and its answer-routing ID in `human_decisions`; source dispositions remain ordinary open/dispatched facts. Any covered observation changing or disappearing invalidates the whole decision and reactivates its surviving sources, but remote activity is never an answer. Continue independent work without resolving covered threads.
|
|
60
|
+
|
|
61
|
+
After the resolver, reconcile every **comment you passed that is not covered by a returned typed residual**. A top-level comment or review body never drops out of the fetch on its own, and `ce-resolve` may silently drop boilerplate, status noise, or other non-actionable feedback after applying agent judgment. Mark every comment you passed as `dispatched` unless the shared residual mark already parked it; never add a route-specific `needs-human` mark. Leaving an uncovered comment unmarked keeps it in the attention set, so the loop can never settle:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
SKILL_DIR="<absolute path of this skill's directory>"; SCRATCH_ROOT="/tmp/compound-engineering-$(id -u)"; [ ! -L "$SCRATCH_ROOT" ] && (umask 077; mkdir -p "$SCRATCH_ROOT") 2>/dev/null && [ ! -L "$SCRATCH_ROOT" ] && [ -O "$SCRATCH_ROOT" ] && [ -w "$SCRATCH_ROOT" ] || SCRATCH_ROOT="${TMPDIR:-/tmp}/compound-engineering-$(id -u)"; [ ! -L "$SCRATCH_ROOT" ] && (umask 077; mkdir -p "$SCRATCH_ROOT") && [ ! -L "$SCRATCH_ROOT" ] && [ -O "$SCRATCH_ROOT" ] && chmod 700 "$SCRATCH_ROOT" || { echo "unsafe scratch root: $SCRATCH_ROOT" >&2; exit 1; }; STATE_DIR="$SCRATCH_ROOT/ce-babysit-pr/<host>-<owner>-<repo>-<N>"; (umask 077; mkdir -p "$STATE_DIR") || exit 1; chmod 700 "$STATE_DIR" || exit 1;
|
|
65
|
+
PY="$(for c in python3 python py; do command -v "$c" >/dev/null 2>&1 && "$c" -c '' >/dev/null 2>&1 && { echo "$c"; break; }; done)"; [ -n "$PY" ] || { echo "no working Python 3 interpreter on PATH" >&2; exit 1; };
|
|
66
|
+
"$PY" "$SKILL_DIR/scripts/pr-snapshot" mark --pr <N> --repo <[host/]owner/repo> --state-dir "$STATE_DIR" --invocation-id "$RUN_INVOCATION_ID" --session-started-at "$RUN_STARTED_AT" --invocation-budget-seconds "$RUN_BUDGET_SECONDS" --disposition needs-human --residual-file <path-containing-one-exact-typed-residual>
|
|
67
|
+
"$PY" "$SKILL_DIR/scripts/pr-snapshot" mark --state-dir "$STATE_DIR" --invocation-id "$RUN_INVOCATION_ID" --session-started-at "$RUN_STARTED_AT" --invocation-budget-seconds "$RUN_BUDGET_SECONDS" --comment <ID> --disposition dispatched --invariant-key <key-returned-by-the-leaf-on-a-fix-outcome>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Passing `--pr`/`--repo` on the shared residual mark is required: `mark` re-reads every covered thread's current last comment (including your just-posted replies) before the atomic write, so later reviewer activity re-opens the whole group instead of being swallowed. Publish the decision only when every covered thread was re-read; a missing observation leaves the complete source set actionable. Covered comments and review bodies keep their snapshot edit identities for the same reason. A **dispatched comment** mark needs no baseline: it stays silenced until an explicit `mark --disposition open` and is never auto-reactivated by a body edit, because status bots rewrite their bodies on every push. A genuinely new request arrives as a review thread or a new comment, both of which are still shown. On a **fix** outcome, persist the leaf-returned `invariant_key` on that same dispatched mark (`--thread` or `--comment` plus `--invariant-key`); omit the flag when the leaf returned none. The leaf does not run `pr-snapshot`.
|
|
71
|
+
|
|
72
|
+
When the user answers, map their response to the displayed `decision_id`, preserve the exact response in a file, and record the shared answer transition before acting on it. The answer is consumed only when this mark succeeds. Read-only prohibits executing the transition, not rendering it. In a read-only run (one whose boundaries forbid writes), return the literal command below as the sole pending transition; substitute exact known values and retain explicit placeholders for unavailable invocation metadata or the answer-file path. That rendered command must include the literal `--answer-decision` and `--answer-file` flags. A prose paraphrase or an in-memory state move is incomplete.
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
SKILL_DIR="<absolute path of this skill's directory>"; SCRATCH_ROOT="/tmp/compound-engineering-$(id -u)"; [ ! -L "$SCRATCH_ROOT" ] && (umask 077; mkdir -p "$SCRATCH_ROOT") 2>/dev/null && [ ! -L "$SCRATCH_ROOT" ] && [ -O "$SCRATCH_ROOT" ] && [ -w "$SCRATCH_ROOT" ] || SCRATCH_ROOT="${TMPDIR:-/tmp}/compound-engineering-$(id -u)"; [ ! -L "$SCRATCH_ROOT" ] && (umask 077; mkdir -p "$SCRATCH_ROOT") && [ ! -L "$SCRATCH_ROOT" ] && [ -O "$SCRATCH_ROOT" ] && chmod 700 "$SCRATCH_ROOT" || { echo "unsafe scratch root: $SCRATCH_ROOT" >&2; exit 1; }; STATE_DIR="$SCRATCH_ROOT/ce-babysit-pr/<host>-<owner>-<repo>-<N>"; (umask 077; mkdir -p "$STATE_DIR") || exit 1; chmod 700 "$STATE_DIR" || exit 1; RUN_INVOCATION_ID="<invocation_id>"; RUN_STARTED_AT="<invocation_started_at>"; RUN_BUDGET_SECONDS="<invocation_budget_seconds>";
|
|
76
|
+
PY="$(for c in python3 python py; do command -v "$c" >/dev/null 2>&1 && "$c" -c '' >/dev/null 2>&1 && { echo "$c"; break; }; done)"; [ -n "$PY" ] || { echo "no working Python 3 interpreter on PATH" >&2; exit 1; };
|
|
77
|
+
"$PY" "$SKILL_DIR/scripts/pr-snapshot" mark --state-dir "$STATE_DIR" --invocation-id "$RUN_INVOCATION_ID" --session-started-at "$RUN_STARTED_AT" --invocation-budget-seconds "$RUN_BUDGET_SECONDS" --answer-decision <decision_id> --answer-file <path-containing-the-exact-human-response>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The next snapshot returns that record in `answered_human_decisions` and makes every still-matching source ordinary actionable work. Apply the answer, then use the existing dispatched/confirmed marks; the answered record retires when its sources are handled or their observations move. Never convert a thread reply, comment edit, check rerun, head move, or currency change into an answer implicitly.
|
|
81
|
+
|
|
82
|
+
Show those decisions in Step 4 (the report) and continue independent work. Once none remains, `mode:pipeline` returns the canonical set as its decision handoff; interactive continuous mode keeps watching around the parked sources. Also keep the resolver's **non-routine verdicts** for the Step 4 summary: a fix done differently than the reviewer suggested (`fixed-differently`), feedback it declined (`declined`), or feedback it rebutted as wrong (`not-addressing`). A plain `fixed` is routine and not worth carrying.
|
|
83
|
+
4. **Stale-SHA cancellation.** Compare the current head SHA to the one captured in step 2. If it **changed**, the comment pass (or someone else) pushed, so the CI failures in this snapshot are against a dead SHA: **do not act on them**. The new run will appear next tick. If it did **not** change, continue to CI.
|
|
84
|
+
5. **CI on the current head.** Aggregate *all* actionable failing checks into one remediation pass; do not dispatch per check. Classify from metadata:
|
|
85
|
+
- **Flaky/infra** (known-flaky job, infrastructure/timeout signal) → extract the run ID **and the full base repo including host** from the failing check's `details_url` (`https://<host>/<owner>/<repo>/actions/runs/<run-id>/…`) and run `gh run rerun <run-id> --failed -R <host>/<owner>/<repo>`. Passing the run ID is required when unattended: omitting it drops `gh run rerun` to an interactive run-picker menu that blocks `mode:pipeline`. Passing the host-qualified `-R <host>/<owner>/<repo>` is required for fork→upstream and GitHub Enterprise PRs: the run lives in the **base** repo on its own host, so a bare `-R <owner/repo>` (or no `-R`) targets the fork or the default `github.com` and 404s. On plain github.com the host segment is optional but harmless.
|
|
86
|
+
- **Blocked by a repository setting or permission** (the log shows GitHub refusing an action because of how the repository or token is configured, not because of the code — for example an auto-merge job failing with "Auto merge is not allowed for this repository") → do not invoke `ce-debug`; no code change can fix it, and changing the setting is outside this skill's authority. Once the setting is fixed, re-run only that job, not every failed job in its run: take `<run-id>` and the repo as the flaky/infra bullet does, look up the job's `databaseId` with `gh run view <run-id> --json jobs --jq '.jobs[] | {name, databaseId}' -R <host>/<owner>/<repo>` (the job number in the check's browser URL is a different ID and 404s), then run `gh run rerun --job <databaseId> -R <host>/<owner>/<repo>`. If it already reads fixed now (for auto-merge, `allow_auto_merge` is `true`), re-run it this tick. Otherwise write a typed `needs-human` residual whose `sources` is that check and whose `decision_context` names the setting or permission and where it is changed, persist it through the shared residual step above, and re-run when the user answers that it is fixed. Never push an empty commit or any other new head to retrigger it.
|
|
87
|
+
- **Real test/build failure** → invoke `ce-debug mode:pipeline` once, seeded with the failing jobs and their log tails. **When the CI trigger above is crossed, also pass the `trajectory` (`recurring_checks`, `check_recur_max`, `heads_since_progress`) so it can judge oscillation vs ordinary progress.** Its structured return `status` is exactly one of `fixed-and-pushed`, `fixed-not-pushed`, `flaky-infra`, `diagnosed-no-fix`, or `needs-human`; do not invent `infra-retry` or `stale`. Handle convergent fixes and retries as before. A `needs-human` result returns complete typed residuals whose `check` sources enter the canonical set through the shared residual step above; park those checks with the same residual file. `diagnosed-no-fix` and failed pushes remain ordinary red residuals. Never weaken a test, rebase, reset, or force-push to clear them.
|
|
88
|
+
The shared residual step above already parks every check covered by a `needs-human` residual. Record each other check you acted on so it is not re-dispatched at this head (re-set the vars inline):
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
SKILL_DIR="<absolute path of this skill's directory>"; SCRATCH_ROOT="/tmp/compound-engineering-$(id -u)"; [ ! -L "$SCRATCH_ROOT" ] && (umask 077; mkdir -p "$SCRATCH_ROOT") 2>/dev/null && [ ! -L "$SCRATCH_ROOT" ] && [ -O "$SCRATCH_ROOT" ] && [ -w "$SCRATCH_ROOT" ] || SCRATCH_ROOT="${TMPDIR:-/tmp}/compound-engineering-$(id -u)"; [ ! -L "$SCRATCH_ROOT" ] && (umask 077; mkdir -p "$SCRATCH_ROOT") && [ ! -L "$SCRATCH_ROOT" ] && [ -O "$SCRATCH_ROOT" ] && chmod 700 "$SCRATCH_ROOT" || { echo "unsafe scratch root: $SCRATCH_ROOT" >&2; exit 1; }; STATE_DIR="$SCRATCH_ROOT/ce-babysit-pr/<host>-<owner>-<repo>-<N>"; (umask 077; mkdir -p "$STATE_DIR") || exit 1; chmod 700 "$STATE_DIR" || exit 1;
|
|
92
|
+
PY="$(for c in python3 python py; do command -v "$c" >/dev/null 2>&1 && "$c" -c '' >/dev/null 2>&1 && { echo "$c"; break; }; done)"; [ -n "$PY" ] || { echo "no working Python 3 interpreter on PATH" >&2; exit 1; };
|
|
93
|
+
"$PY" "$SKILL_DIR/scripts/pr-snapshot" mark --state-dir "$STATE_DIR" --invocation-id "$RUN_INVOCATION_ID" --session-started-at "$RUN_STARTED_AT" --invocation-budget-seconds "$RUN_BUDGET_SECONDS" --check "<key>"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
(A new head SHA clears these automatically.)
|
|
97
|
+
|
|
98
|
+
6. **Branch currency & conflicts (the third stream — after comments and CI).** Consume the exact current `branch_currency` item; no item means no base-into-head mutation. The full route, the claim lifecycle, and the `BEHIND`/`DIRTY` mechanisms are in `references/branch-currency.md`.
|
|
99
|
+
7. **After an authorized target-head push in a confirmed managed stack, preserve the upstack before resuming the watch** (`gh stack rebase "<first-dependent-branch>" --upstack --no-trunk` + `gh stack push`; abort and park on conflict): `references/stack.md`.
|
|
100
|
+
8. **After any mutation, re-snapshot** at the start of the next tick, passing the same `--invocation-id "$RUN_INVOCATION_ID" --session-started-at "$RUN_STARTED_AT" --invocation-budget-seconds "$RUN_BUDGET_SECONDS"`. The head SHA and the CI universe have changed, but the invocation-wide budget has not. Do not run a second `snapshot` mid-tick to re-derive CI; that is what caused stale-SHA confusion.
|
|
101
|
+
|
|
102
|
+
During accepted managed-stack continuation there is one watcher (probing lower layers via `--downstack-pr`) and one mutated target; the transition condition, the return rule, and the landing gate are defined in `references/stack.md`.
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# Watch loop — scheduling, state, dedup, edge cases
|
|
2
|
+
|
|
3
|
+
Read this once per babysit session, before acting on the first tick's output. It explains four things: how ticks are scheduled on each harness, what the on-disk state file contains, the claim→act→confirm protocol that keeps ticks idempotent and crash-safe, and how edge cases are handled. SKILL.md states the tick ordering; this file explains the mechanics behind it.
|
|
4
|
+
|
|
5
|
+
## How the watch sustains itself
|
|
6
|
+
|
|
7
|
+
Keep monitoring in the current session until a stop condition is met. Run the deterministic change detector and wait for its output using the harness's tools:
|
|
8
|
+
|
|
9
|
+
- **`pr-snapshot watch`** is that detector. It runs the same fetch→diff on an interval, uses **no agent tokens**, and prints one `BABYSIT_WAKE {reason,url,...}` line *only* when there is work to inspect or a stop condition, then exits. Work reasons: `actionable` (unresolved threads or failed CI), `feedback-candidate` (non-thread content that still needs the resolver's judgment), and `branch-currency` (an item that needs a claim, semantic inspection, or reconciliation). Stop reasons: `terminal`, `blocked-external`, `blocked-external-drained`, `blocked-failing` (a dispatched check was left terminally red), `base-ref-blocked`, `needs-human`, `merge-ready` after settle, `review-evidence-moved`, `max-runtime`, `stop-signal`, and `invocation-superseded`. A `review-evidence-moved` wake means readiness-bearing state changed while a widened settle window was still running; the agent re-judges instead of sleeping out a window that was armed against evidence that has since moved. A `feedback-candidate` that the resolver silently drops is a normal classification outcome, not a detector false positive.
|
|
10
|
+
- At the fixed deadline, the final refresh preserves `terminal` and already-settled `merge-ready` stops; `max-runtime` outranks every non-terminal work/residual wake so the cap cannot start another agent round.
|
|
11
|
+
- The agent waits for that line, runs a tick, and re-arms the detector. Keep the session active while waiting so each wake returns to this agent with the conversation's decisions intact.
|
|
12
|
+
|
|
13
|
+
Watcher ownership is **latest-valid-watcher-wins**: the newest invocation that completes a valid first snapshot takes over. A newer invocation cancels an older invocation whose first fetch is still in flight, so network completion order cannot hand ownership back to the older one. That reservation does not displace the watcher that is already active. Only a successful first snapshot supersedes the active watcher and terminates it gracefully; a failed preflight leaves the active watcher healthy. Every wake and snapshot carries `watch_generation`. When a wake arrives, compare its generation with a fresh snapshot. A stale wake is discarded and coalesced into that current read. If the generation matches but the attention set (the items the snapshot says still need action) has already cleared, do no work. An `invocation-superseded` wake means a later explicit invocation now owns the state: end the old loop without a tick or a re-arm. A replacement watcher keeps `last_change_at`, `invocation_started_at`, and `invocation_budget_seconds`, so it polls immediately without adding a new settle delay or renewing the budget.
|
|
14
|
+
|
|
15
|
+
Use the harness's tools to run the detector and wait for its output. A skill drives tool calls; it does not type slash commands the way a user would. The rows below are examples, not a required tool list:
|
|
16
|
+
|
|
17
|
+
| Harness | Run and wait tools | Durable beyond the session? |
|
|
18
|
+
|---------|--------------------|-----------------------------|
|
|
19
|
+
| Claude Code (CLI) | background `Bash` + a `Monitor`/wait; or `ScheduleWakeup` under `/loop` | No (session-bound) — cron for durable |
|
|
20
|
+
| Grok (CLI/TUI) | background `run_terminal_command` + `get_command_or_subagent_output`; `scheduler_create --durable` for a cross-session schedule | Yes via `scheduler_create --durable` (60s min, 7d) |
|
|
21
|
+
| Cursor (CLI) | `Shell` background + `notify_on_output` sentinel (its `/loop` is user-typed, **not** skill-invocable) | No (session-bound) |
|
|
22
|
+
| Codex (CLI) | `exec_command` to start the detector, then `write_stdin` to wait on the returned process session | No (session-bound) |
|
|
23
|
+
|
|
24
|
+
**User-runnable resume syntax.** Whenever this reference tells the skill to print or copy a resume invocation, default to `/ce-babysit-pr <url>` and, when the run posture is not `target`, append the same `posture:stack-ready` or `posture:stack-land` token so checkpoint / durable / session re-entry keeps stack scope. Use `$ce-babysit-pr <url> [posture:…]` only when the active host is Codex or explicitly documents dollar-prefixed skill invocation. Render only the invocation as inline code and output one form only.
|
|
25
|
+
|
|
26
|
+
**Checkpoint:** Use checkpoint mode only when the user requests it or the harness cannot keep the session active while waiting for the detector's output. Run one tick, persist state, report, and print the exact host-rendered resume invocation. Say monitoring is paused. Never fake a loop with a foreground `sleep` or an unmanaged detached process.
|
|
27
|
+
|
|
28
|
+
**Durability:** the in-session watch dies with the session. Re-invoking the skill resumes from disk, because `/tmp` persists across ticks. For an unattended multi-day watch, escalate to a durable scheduler: Grok's `scheduler_create --durable`, or cron running `<cli> exec '<host-rendered resume invocation>'`. A fresh headless run has no memory of this conversation, so persist consequential decisions to disk. **Shell env vars do not persist between separate tool calls** on any harness — re-set `SKILL_DIR`/`STATE_DIR` inline in every command.
|
|
29
|
+
|
|
30
|
+
## Cadence (the watch interval)
|
|
31
|
+
|
|
32
|
+
- `pr-snapshot watch --interval` is the poll cadence: about 2-3 minutes while the PR is active, widened to about 5-10 minutes when it is quiet. The detector is cheap, but each poll is a `gh` call, so respect rate limits.
|
|
33
|
+
- `--settle-seconds` (default 300) is the quiet window before a `merge-ready` wake, so the agent is woken to declare readiness only once the PR has actually cooled off, not on every poll. Leave it unset on the normal arm; the script's default is the initial policy. The only invocation that sets it is the re-arm after a rejected `merge-ready` wake, described in SKILL.md Step 3 (the merge-ready wake protocol).
|
|
34
|
+
- `--feedback-coalesce-seconds` (default 180) is the quiet window before a **comment-only** wake. Every non-empty top-level body is a candidate the resolver classifies. Waking on the first one would spend a full `ce-resolve-pr-feedback` dispatch to conclude "status noise", and would split the ordinary sequence (push -> bots comment -> CI finishes) into two agent rounds. The candidates stay actionable while the window runs, so any other wake reason claims the tick and handles them with it; nothing is dropped, only deferred. Leave it unset on the normal arm.
|
|
35
|
+
- `--blocked-external-drain-seconds` is set only after an interactive approval-gate wake (`blocked-external`: CI is waiting on a human approval). Keep the active ~150s interval throughout this short 300/900/1800-second review drain: a quiet 5-10 minute poll could consume an entire tier before any signal arrives. The persisted head-scoped review clock decides expiry, not the arm time and not the broader merge-ready quiet clock.
|
|
36
|
+
- A push or other mutation moves the head. Re-arm `watch` at the active cadence so it reads the new state.
|
|
37
|
+
- Every re-arm presents the same `--invocation-id "$RUN_INVOCATION_ID" --session-started-at "$RUN_STARTED_AT" --invocation-budget-seconds "$RUN_BUDGET_SECONDS"`; the helper rejects a changed token, anchor, or budget. Only the first snapshot uses `--start-invocation`; only a managed-stack layer transition uses `--continue-invocation`.
|
|
38
|
+
- Honor GitHub rate-limit reset headers; back off on `403`/`429`.
|
|
39
|
+
- After any mutation, re-snapshot at the *start of the next tick*, not mid-tick.
|
|
40
|
+
|
|
41
|
+
## Pipeline mode bound (`mode:pipeline`)
|
|
42
|
+
|
|
43
|
+
An orchestrator (`lfg`) drives ticks in-line and needs the loop to terminate. Run ticks back-to-back until one of the stops below. **To wait for CI to progress between ticks, use the harness's native non-blocking wait — never a bare foreground `sleep`** (blocked on Claude Code, discouraged elsewhere). Examples: Claude Code's `Monitor` until-loop; Grok's `get_command_or_subagent_output(timeout_ms=…)` or a `monitor`; Cursor's `Await` on a backgrounded `gh pr checks --watch`. If the harness has no non-blocking wait, do one tick and return control to the orchestrator rather than busy-spinning. Loop until:
|
|
44
|
+
|
|
45
|
+
- **Report success only when** `all_checks_ok` is true, the actionable backlog and canonical `needs_human_residuals` set are empty, `mergeability_certain` is true, `merge_state_status == "CLEAN"`, `base_ref_blocker` and `stack_blocker` are null, `branch_currency_blocker` is null, and `unrequested_base_merge` is null. A red check, a human decision, an empty check rollup, uncertain merge/base/stack state, or an open/claimed/parked currency item is not success: keep ticking until it clears or the time budget expires; or
|
|
46
|
+
- when the canonical set (the persisted `needs_human_residuals`) is non-empty and no autonomous work remains, return `{ status: "needs-human", checks_terminal, fixes_applied, residuals: needs_human_residuals }` immediately without waiting for human input or the budget; or
|
|
47
|
+
- a **budget** is hit. The default is **3 CI fix rounds** per head-lineage (the same cap `lfg` has always used) and an overall time cap of about 30-45 minutes. When the budget runs out, the still-red checks and any `needs-human` items become residuals.
|
|
48
|
+
|
|
49
|
+
Never wait on the merge-ready settle window or human review in pipeline mode; those are interactive stops. A check stuck `IN_PROGRESS` past the time cap ends the run with a "CI still running" residual rather than blocking forever.
|
|
50
|
+
|
|
51
|
+
The round/time budget above is a coarse cost limit, not a convergence detector. It catches a runaway that never trips the trajectory-driven stop below. Prefer to stop *because the work is demonstrably not converging*, not because a timer expired.
|
|
52
|
+
|
|
53
|
+
## Non-convergence (trigger → route → park → re-open)
|
|
54
|
+
|
|
55
|
+
A loop can churn without finishing. Three shapes: CI **ping-pong** (fix A surfaces B, fix B brings A back, often an emergent trade-off); a review-bot **treadmill** (each commit spawns fresh nits); and **wrong-approach whack-a-mole** (each nit is valid, but the approach, say a regex, is the problem). A raw attempt counter cannot tell these apart from *legitimate progress* (four independent failures, each fixed once). So the decision is agent reasoning over the trajectory, and the split of responsibilities is strict:
|
|
56
|
+
|
|
57
|
+
- **`pr-snapshot` (babysit) reports facts.** The `trajectory` block is deterministic and coarse. Its fields: `check_recur_max`/`recurring_checks` (a check that failed, cleared, then failed again on a *new* head; same-head flapping is excluded, so this is not flaky noise), `unresolved_trend` + `new_threads_this_tick` (backlog growing, fresh threads arriving), `stream_alternations` (ci↔review bouncing, which is cross-stream churn only babysit can see), `heads_since_progress` (heads moved without a new low in open problems), and `invariant_rounds` (resolver-supplied `invariant_key` values counted per unique head; the script never infers a key from paths, regexes, or comment text). Babysit **never** labels this "non-convergence."
|
|
58
|
+
- **The leaf judges.** The leaf is the delegated skill for that tick: `ce-debug` for CI, `ce-resolve-pr-feedback` for review. When a trigger is crossed (the thresholds are listed in SKILL.md Step 2 / `references/tick.md`, the single source of truth; do not re-list them here), pass the trajectory into that tick's leaf as **mandatory input**. The leaf must do one of two things. Either it demonstrates progress by naming the invariant the next bounded fix resolves, or it returns a `needs-human` that **parks the whole stream** (puts it on hold as a standing residual) with a `decision_context` (the tension or root cause, options, tradeoffs, and its lean). On a **fix** outcome, the leaf **returns** a stable `invariant_key` for each root it fixed, associated with the items that root covered. The leaf does not run `pr-snapshot` and must not mark. Babysit persists each key at its existing atomic dispatched-mark step (`--thread` / `--comment` plus `--invariant-key`). A pass that would begin a third unique-head round for the same key is itself a trigger: `invariant_rounds[].rounds >= 2`, because a round is recorded only after its fix completes. Route the trajectory **before** another fix/commit/push so the leaf returns one approach-level `needs-human` instead of mutating again. Unrelated keys stay independently actionable. One- and two-round progress is not a blocker.
|
|
59
|
+
|
|
60
|
+
**The anti-cry-wolf line (put it to the leaf):** *progressive failure migration* (A fixed, B appears once, B fixed, done) is ordinary repair; **do not park.** *Oscillation* is non-convergence; park. Oscillation looks like any of these: A returns after B's fix; the failing set cycles; defects migrate X→Y→Z with the same invariant unsatisfied; or fix size grows superlinearly. "We've tried a lot" is never enough.
|
|
61
|
+
|
|
62
|
+
**A third case the counter must not miss: a *correct* finding recurring across sibling sites.** Sometimes each new head brings a fresh thread that is *valid* and shares one root and one treatment with an already-fixed thread. That is not a wrong-approach cluster and not oscillation. It is a single fix with a multi-site blast radius, surfacing one site per head. Fixing it one site per head is as wasteful as parking it is wrong. **Route it, don't decide it here:** pass the recurring feedback cluster **plus** the trajectory to `ce-resolve-pr-feedback` and request a **bounded-class assessment**. The resolver has the diff and makes the call. It decides whether the sites are genuinely equivalent (same invariant, same fix, only behavior this PR touched), enumerates the concrete locations, and fixes the class in one pass. Babysit does **not** infer the root or the sites from the `trajectory`; those are churn counts, not semantic identity. If the resolver judges the sites *not* equivalent, it falls back to per-site fixes. If it judges the shared root a wrong approach, it parks, unchanged from above.
|
|
63
|
+
|
|
64
|
+
**Guards:**
|
|
65
|
+
|
|
66
|
+
- **Moving-target ≠ non-convergence.** Base-branch merges, dependency bumps, flaky infra, and bot-rule changes create unrelated new failures. Recurrence already excludes same-SHA flapping. Still, do not park a failure the leaf attributes to an external cause rather than the approach.
|
|
67
|
+
- **Cross-stream contradiction.** If `ce-debug` concludes the review-requested behavior is invalid while `ce-resolve-pr-feedback` concludes it is required, that is a single **cross-stream** residual. Do not arbitrarily park one side.
|
|
68
|
+
- **A current decision is a hard blocker with one lifetime condition.** It makes the PR *not* merge-ready until the shared answer transition consumes it or any frozen source observation changes or disappears. The latter invalidates the whole decision automatically and makes the surviving ordinary sources actionable; it never counts as the human's answer. An explicit source `--disposition open` may cancel a covering decision when the agent has separately proved the observation is no longer the question. No stream has its own reopen rule.
|
|
69
|
+
|
|
70
|
+
## On-disk state contract
|
|
71
|
+
|
|
72
|
+
State lives at `<scratch-root>/ce-babysit-pr/<host>-<owner>-<repo>-<pr>/state.json`. That path is stable and reusable across invocations, so any later tick (scheduled or hand-run) finds it. The `<host>` segment (from the PR URL; `github.com` on the public host) is required for GitHub Enterprise: without it, two PRs sharing `owner/repo#N` on different hosts would reuse one `state.json` and cross-contaminate dispositions. The `pr-snapshot` script performs all reads and writes under a file lock. Shape:
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"pr": { "owner": "...", "repo": "...", "number": 123, "url": "..." },
|
|
77
|
+
"head_sha": "abc123",
|
|
78
|
+
"tick": 7,
|
|
79
|
+
"state_created_at": "<iso8601>",
|
|
80
|
+
"started_at": "<iso8601>",
|
|
81
|
+
"invocation_id": "<opaque invocation token>",
|
|
82
|
+
"invocation_budget_seconds": 28800,
|
|
83
|
+
"last_activity_at": "<iso8601 — activity heartbeat: last watch poll or agent snapshot/mark>",
|
|
84
|
+
"dead_time_seconds": 0,
|
|
85
|
+
"invocation_backstop_seconds": 259200,
|
|
86
|
+
"watch_generation": "<opaque generation>",
|
|
87
|
+
"watch_pid": 12345,
|
|
88
|
+
"watch_process_identity": "<pid-reuse guard>",
|
|
89
|
+
"checks": { "<check_key>": { "name": "...", "status": "COMPLETED", "conclusion": "FAILURE", "head_sha": "abc123" } },
|
|
90
|
+
"threads": { "<thread_id>": { "last_comment_id": "...", "last_comment_at": "<iso8601>", "disposition": "open|dispatched", "acted_identity": ["<comment_id>", "<comment_at>"] } },
|
|
91
|
+
"feedback": { "<comment_or_review_id>": { "kind": "comment|review", "author": "...", "disposition": "open|dispatched", "acted_identity": ["<edit_id>"] } },
|
|
92
|
+
"ci_dispatched": { "<head_sha>": ["<check_key>", "..."] },
|
|
93
|
+
"human_decisions": [{ "id": "decision:<hash>", "residual": "<complete typed residual>", "sources": [{ "kind": "check", "id": "CI/test", "observation": "<frozen source facts>" }] }],
|
|
94
|
+
"answered_human_decisions": [{ "id": "decision:<hash>", "answer": "<exact human response>", "residual": "<complete typed residual>", "sources": ["<same frozen observations>"] }],
|
|
95
|
+
"review_decision": "APPROVED",
|
|
96
|
+
"blocked_external_head_sha": "abc123",
|
|
97
|
+
"blocked_external_first_seen_at": "<iso8601>",
|
|
98
|
+
"blocked_external_review_last_activity_at": "<iso8601>",
|
|
99
|
+
"mergeable": "MERGEABLE",
|
|
100
|
+
"merge_state_status": "CLEAN",
|
|
101
|
+
"base": {
|
|
102
|
+
"host": "github.com",
|
|
103
|
+
"repository": "owner/repo",
|
|
104
|
+
"ref": "main",
|
|
105
|
+
"oid": "live-base-sha",
|
|
106
|
+
"graphql_oid": "graphql-live-base-sha",
|
|
107
|
+
"historical_oid": "historical-pr-base-sha",
|
|
108
|
+
"merge_commit_oid": "generated-test-merge-sha",
|
|
109
|
+
"merge_parent_oids": ["live-base-sha", "head-sha"],
|
|
110
|
+
"identity": "current"
|
|
111
|
+
},
|
|
112
|
+
"base_ref_blocker": null,
|
|
113
|
+
"branch_currency_state": {
|
|
114
|
+
"current_key": "currency:<identity-hash>",
|
|
115
|
+
"head_sha": "abc123",
|
|
116
|
+
"items": { "<currency-key>": { "status": "BEHIND|DIRTY", "disposition": "open|claimed|confirmed", "host_branch_update_capability": true, "recovery_state": "claimed|mutation-observed|ambiguous|retry-authorized|retry-exhausted", "semantic_conflict_fingerprint": "<paths-and-stage-blobs>" } },
|
|
117
|
+
"semantic_parks": { "<fingerprint>": { "head_sha": "abc123", "status": "DIRTY", "route": "normal-base", "observation_key": "<currency-key>" } }
|
|
118
|
+
},
|
|
119
|
+
"pr_chain": {
|
|
120
|
+
"manager_status": "confirmed|absent|probe-error",
|
|
121
|
+
"manager_source": "gh-stack|graphql|null",
|
|
122
|
+
"relationship_status": "dependent|independent|probe-error",
|
|
123
|
+
"target_position": 2,
|
|
124
|
+
"target_needs_rebase": false,
|
|
125
|
+
"upstack_needs_rebase": [],
|
|
126
|
+
"entries": [],
|
|
127
|
+
"parent_prs": [],
|
|
128
|
+
"dependent_prs": []
|
|
129
|
+
},
|
|
130
|
+
"last_change_at": "<iso8601>",
|
|
131
|
+
"last_action": "<short string>",
|
|
132
|
+
"trajectory": {
|
|
133
|
+
"check_history": { "<check_key>": { "state": "failing|clear", "last_head": "abc123", "recur": 0 } },
|
|
134
|
+
"seen_threads": { "<thread_id>": 3 },
|
|
135
|
+
"unresolved_series": [2, 3, 4],
|
|
136
|
+
"stream_series": ["ci", "review", "ci"],
|
|
137
|
+
"min_open_problems": 1,
|
|
138
|
+
"heads_since_progress": 0,
|
|
139
|
+
"invariant_heads": {}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
`human_decisions` is the one durable decision set across review, CI, and branch currency. Source records keep only their ordinary open/dispatched/claimed facts. A current decision covers the exact observations frozen in its `sources`. The snapshot emits each unchanged payload in `needs_human_residuals` and pairs it with its `decision_id` in the `human_decisions` view, for rendering and for routing the answer. Any covered observation changing or disappearing invalidates the whole decision and makes the surviving ordinary sources actionable again; remote activity is never inferred to be an answer. `mark --answer-decision <decision_id> --answer-file <path>` is the only answer transition. It moves the exact record to `answered_human_decisions`, preserves the response while matching sources remain actionable, and prevents the unchanged evidence from immediately re-parking. Legacy parked records without a complete payload fail open: they do not block, and their sources return to ordinary actionable work.
|
|
145
|
+
|
|
146
|
+
A `check_key` is `"<workflow>/<name>"` (or `"<name>"` when there is no workflow). It is stable across polls for the same head, which is all the dedup needs (see below). Each `snapshot` emits `changed_this_tick`, `quiet_seconds`, `invocation_id`, `invocation_started_at`, `invocation_elapsed_seconds`, `invocation_budget_seconds`, `invocation_remaining_seconds`, `persisted_state_created_at`, `persisted_state_age_seconds`, `pr_chain`, `stack_blocker`, `blocked_external_first_seen_at`, `blocked_external_review_last_activity_at`, `blocked_external_review_quiet_seconds`, `blocked_external_review_moved_this_tick`, and the derived `trajectory` facts (see **Non-convergence** above). The blocked-external clock is head-scoped and narrower than `quiet_seconds`: external thread, comment, or review movement, or a new head, resets it; check, base, stack, and disposition-only movement does not. An announcement the engine does not fetch (a body reaction, for example) cannot reset it either. So a reviewer that announces itself mid-drain is discovered by the agent at the expiry wake rather than by this clock, and the drain the agent then selects is measured from the last movement the engine did see. `blocked_external_review_moved_this_tick` lets a newly started or changed lifecycle wake through an already-baselined gate so the agent can select the longer tier. The first snapshot starts one fixed invocation; later calls must match its token, anchor, and budget. Persisted-state age describes how long the resumable PR journal has existed and never counts toward the invocation cap. The chain probe is CLI-first: accept `gh stack view --json` only when it contains the target PR, then use the GraphQL fallback. Only a stack-field schema-unavailable response with a successful read-only default-branch lookup degrades to `absent`; auth, transport, rate-limit, malformed, other GraphQL, and failed default-branch probes stay `probe-error`. Ordinary open-PR base/head relationships classify manual dependencies only when no manager is confirmed. The `trajectory` sub-state is deterministic bookkeeping the script maintains; the leaves reason over the emitted facts.
|
|
147
|
+
|
|
148
|
+
## Claim → act → confirm (the dedup protocol)
|
|
149
|
+
|
|
150
|
+
This is the rule that makes ticks idempotent *and* crash-safe: **the snapshot never marks an item handled just from observing it.** An item leaves the actionable set only when the agent confirms it acted (via `mark`) or when remote truth removes it. So if a resolve or debug pass crashes, errors, or returns without finishing, the item is still actionable on the next tick. The loop cannot silently drop work.
|
|
151
|
+
|
|
152
|
+
- **Review threads.** A thread is actionable while it is unresolved and you have not recorded acting on it. After a resolve pass, `mark --thread <id> --disposition dispatched` handles an ordinary unresolved thread; when the leaf returned an `invariant_key`, that same mark carries `--invariant-key`. The shared residual mark freezes its complete source observations without changing their dispositions. A later reviewer comment invalidates any covering decision and reopens every surviving sibling; it does not answer the decision. Every mark carries the active invocation tuple (invocation ID, start anchor, budget), so stale ticks cannot silence work in a newer invocation.
|
|
153
|
+
- **Non-thread feedback candidates** (top-level PR comments + review-submission bodies). These appear as `actionable.comments` when feedback has no inline thread. The detector excludes only empty bodies and never classifies content, authors, or posting surfaces; `ce-resolve` makes that judgment. Because there is no remote resolve action for these, every passed candidate must either be marked `dispatched` or be covered by one validated current decision. A dispatched candidate stays silent across body edits, because status bots routinely rewrite comments; an edit to a covered candidate invalidates the decision. A new comment has a new ID and is actionable. Both feedback surfaces remain one review stream for trajectory and backlog accounting.
|
|
154
|
+
- **CI checks.** A failing check on the current head is actionable until you `mark --check <key>`. A typed decision enters the same canonical set through the shared residual mark, never through check-specific decision state. A new head clears dispatch state, invalidates check-sourced residuals, and re-evaluates the decision against the new commit.
|
|
155
|
+
|
|
156
|
+
- **Current-base merge identity.** `base.historical_oid` is the PR's historical `baseRefOid`; it may legitimately differ after the base branch advances and never blocks by itself. `base.graphql_oid` comes from `baseRef.target.oid`. `base.oid` comes from an independent exact ref read on `base.host`: the REST Git-ref endpoint first, then non-interactive `git ls-remote` only when REST returns 404 (authenticated through the `gh` session via `gh auth git-credential`, so it needs no separately configured Git credential helper). A mergeable result is current only when those live-base OIDs match and `potentialMergeCommit` names that base and the observed head as its two parents. Base movement between probes emits `race`; a temporarily absent generated merge emits `mergeability-pending`; failed, deleted, or malformed ref/identity reads emit `probe-error`. Those blocking identities disable `mergeability_certain`, emit no branch-currency item, wake `base-ref-blocked`, and reset the settle clock when readiness-bearing identity or freshness changes. A `race` whose only mismatch is the test merge's base parent (head and live base agree) degrades to `stale-computation` after `STALE_MERGE_COMPUTATION_SECONDS` of stability and no longer blocks; `merge_computation_stale` carries the disclosure. That transition is presentation-only and keeps the existing quiet clock. `DIRTY` / `CONFLICTING` is the fallback: GitHub may omit `potentialMergeCommit`, so matching live-base observations make the conflict result usable without inventing a generated merge.
|
|
157
|
+
- **Branch currency.** `branch_currency` is the third attention stream. Its identity binds the host-qualified live base repository/ref/OID, the head SHA, the merge status, and the route. "Mergeable against the current base" and "head contains the latest base" are separate questions: only `BEHIND`, `DIRTY`, a branch-protection requirement, or an explicitly selected always-current policy requires maintenance. Ordinary historical base movement with GitHub reporting `CLEAN` does not. `UNKNOWN` mergeability or any non-null `base_ref_blocker` emits no item. Managed stacks and manager/relationship `probe-error` are excluded. A `normal-base` target may be independent or an eligible target-local manual dependency, and a root with open child dependents remains allowed. Never mutate those dependents.
|
|
158
|
+
|
|
159
|
+
A current open item with carried `parked_semantic_fingerprints` must be previewed first. Compute the current fingerprint from the sorted conflicted paths plus stage blob identities, excluding the base OID, and mark `--currency-inspected-fingerprint <fingerprint>`. Unchanged evidence remains parked; changed evidence retires the old park and reopens attention. Before either a host update or a local merge, atomically mark the exact item with `--currency-key <currency_key> --currency-disposition claimed`, plus `--invocation-id "$RUN_INVOCATION_ID" --session-started-at "$RUN_STARTED_AT" --invocation-budget-seconds "$RUN_BUDGET_SECONDS"`. Stale invocation fencing and `max-runtime` take precedence over a new claim or mutation.
|
|
160
|
+
|
|
161
|
+
Re-entry into `claimed` is reconciliation-only, never a direct resubmission. Record the observed recovery as `--currency-outcome mutation-observed`, `--currency-outcome proven-no-mutation`, or `--currency-outcome ambiguous`. Continue autonomously only through the recovery the recorded outcome leaves safe: reconcile an observed mutation toward confirmation. Exactly one retry follows only conclusive no-mutation proof and the engine backoff. Ambiguous recovery never retries or resubmits. Confirm the same exact item with `--currency-disposition confirmed`. Every terminal currency result with no safe autonomous continuation includes one complete `--residual-file` in that same locked mark with source `{ id: <currency_key>, kind: "currency" }`. Answers use the shared decision-ID transition above; branch currency has no separate answer state. Never transfer a claim or decision to a moved head/base observation.
|
|
162
|
+
|
|
163
|
+
`human_decisions`, `answered_human_decisions`, `ci_dispatched`, the thread and feedback dispositions, and `branch_currency_state` share the locked state journal. There is no second decision ledger and no route-specific crash-recovery record. An unmarked review or CI item stays actionable. A current decision covers its exact observations. A claimed currency item stays reconciliation-only until it is explicitly confirmed or handed to the shared decision step.
|
|
164
|
+
|
|
165
|
+
## Merge-readiness and the settle window
|
|
166
|
+
|
|
167
|
+
Do not re-derive "required checks" or required reviews; GitHub already computes them. For GitHub's part of readiness, use `mergeability_certain`, `mergeable == "MERGEABLE"`, and `merge_state_status == "CLEAN"` once the merge computation is bound to the current base and head. Then require the chain and branch currency to be clear. A managed target is ready only when `stack_blocker == null`. The snapshot derives that field: it defers to GitHub's certain `MERGEABLE`/`CLEAN` read for plain trunk drift and emits a blocker only for a stale-or-unknown target that GitHub does not clear. Any current `base_ref_blocker` or `branch_currency_blocker` blocks ready until fresh remote evidence clears it. A manual dependency can be ready relative to its parent but is not independently landable while that parent remains open. `UNSTABLE` means mergeable but a non-required check is red; `BLOCKED` means a required gate is unmet. The snapshot also emits `has_failing_checks` so you can act on a red check even while `merge_state_status` is `UNSTABLE`.
|
|
168
|
+
|
|
169
|
+
The settle window guards against the most damaging false positive: "CI went green, told the user to merge, then feedback landed."
|
|
170
|
+
|
|
171
|
+
- The script stamps `last_change_at` whenever readiness-bearing state moves: a check status or conclusion, a thread's identity (added, edited, or resolved-away), the head SHA, `review_decision`, `mergeable`, or `merge_state_status`. The bounded `race` -> `stale-computation` presentation transition is excluded. Each snapshot emits `quiet_seconds`.
|
|
172
|
+
- "Looks ready" requires `quiet_seconds >= 300` (default) on top of a CLEAN mergeable state and zero actionable backlog (threads **and** non-thread feedback). A reviewer or bot still working shows up as recent activity, which resets `quiet_seconds`.
|
|
173
|
+
- Whether a review is still coming is the agent's judgment at the settle decision, not a state the engine tracks. `references/settle.md` defines that judgment; the engine only reports that the PR went quiet.
|
|
174
|
+
- **It is a cooling-off signal, not a guarantee.** Five quiet minutes is evidence the PR stopped moving, not proof no review is coming. Report "looks ready — your call," never "safe to merge"; a stalled lifecycle uses the stronger cautious-ready disclosure and resume path from SKILL.md.
|
|
175
|
+
|
|
176
|
+
## Concurrency
|
|
177
|
+
|
|
178
|
+
- **Lock.** The script takes a file lock around each state read and write. The lock cannot cover the agent's own mutations, which happen between script calls, so it is necessary but not sufficient.
|
|
179
|
+
- **Pre-mutation claim and revalidation.** Before a `BEHIND`/`DIRTY` external mutation, claim the exact item, then immediately prove its observed head and base OIDs are still current. A moved head or base invalidates the stale action. Treat the snapshot as a hint, never as a guarantee that the world is unchanged at mutation time.
|
|
180
|
+
- **Interrupted local merge.** On re-entry, inspect the checkout and the remote before acting. Reconcile an interrupted merge to the already-validated commit or abort it safely; never begin another merge over unknown local state.
|
|
181
|
+
|
|
182
|
+
## Managed-stack continuation
|
|
183
|
+
|
|
184
|
+
Sequential babysitting (moving up the stack one PR at a time) is available only while a fresh probe positively reports `manager_status == "confirmed"` for the active PR and the run posture is `stack-ready` or `stack-land`. Under `target` it becomes available after the one-time offer is accepted, which selects `stack-ready`. It uses one active PR target and one watcher, never a watcher per stack layer.
|
|
185
|
+
|
|
186
|
+
On an authorized transition to the next layer: stop the old watcher; re-read `gh stack view --json`; require the next PR to be the manager's immediate open entry and either non-draft or explicitly included by the user; check out that branch; and initialize its own state directory with `--continue-invocation` plus the same recorded values on the same flags the first snapshot used: `--invocation-id`, `--session-started-at` (the anchor flag, not `--invocation-started-at`), and `--invocation-budget-seconds`. Also pass **`--continue-dead-time-seconds <prior layer's `invocation_dead_time_seconds`>`** so the shared active-time budget carries the suspended time already excluded on earlier layers, **and re-state the same `posture:`**. One fixed budget covers the entire accepted traversal rather than restarting per PR. Each layer's state dir accumulates its own dead time, so omitting the carry value would re-count the prior layer's excluded suspend time as active.
|
|
187
|
+
|
|
188
|
+
The transition condition under `stack-ready` is quiescence, not settle. Quiescence means zero actionable backlog, no standing residual (needs-human, blocked-failing, stack-blocked, or an open/claimed currency item), and no delegate in flight. Landing under `stack-land` still requires the full settled gate. Arm the active watcher with `--downstack-pr <N>` for every open lower layer, so a lower layer that gains a new thread, comment, failing check, or head wakes `downstack-actionable`. Also recheck downstack quiescence at transitions, immediately before a mutation, and at readiness. On either signal, return to the lowest re-opened layer rather than writing to both concurrently. Loss of manager confirmation ends continuation.
|
|
189
|
+
|
|
190
|
+
Under `stack-land`, after settle the caller-owned land step merges the bottom-most open settled PR and then syncs before continuing; the recipes are in `references/stack-commands.md`. A just-landed MERGED outcome is a layer transition, not a Terminal exit for the run.
|
|
191
|
+
|
|
192
|
+
The one-time semantic-scope offer (under `target`), the posture table, and the draft/human boundaries are stated inline in SKILL.md, because they are routing decisions rather than detector mechanics. A manual dependency chain never enters this continuation path; it remains target-local even when its base/head relationships have the same shape.
|
|
193
|
+
|
|
194
|
+
## Edge cases
|
|
195
|
+
|
|
196
|
+
- **Managed stack:** trunk movement alone is not a reason to restack. The manager's `needsRebase` flag turns on for plain trunk drift, and GitHub already prices that into the target's mergeability against its parent base, so `stack_blocker` is null and the target may be ready whenever GitHub reports a certain `MERGEABLE`/`CLEAN`. `stack_blocker` is `target-needs-rebase` / `managed-freshness-unknown` only when the target is stale or of unknown freshness **and** GitHub does not clear it; that becomes `stack-blocked`. Never use `gh pr update-branch` or a local base merge on a managed target.
|
|
197
|
+
|
|
198
|
+
Before a delegate may push the target, record the manager-ordered open branches at or above the target and each one's remote-tracking OID (see SKILL.md pre-push baseline). If the worktree is not clean or manager membership cannot be confirmed, stop without delegating. Do not stop for missing atomic multi-ref proof (`github/gh-stack#216`).
|
|
199
|
+
|
|
200
|
+
After an authorized target push: retain the pushed SHA; re-confirm that local `gh stack view --json` still owns the target/current branch; require a clean worktree; fetch the target branch; and verify its local and remote-tracking heads remain at the pushed SHA. Select the first open dependent immediately above the target, then run `gh stack rebase "<first-dependent-branch>" --upstack --no-trunk --remote <tracking-remote>` followed by `gh stack push --remote <tracking-remote>` only; never use a raw force push. If there is no dependent, skip the cascade. Quote the branch name. Starting at the dependent excludes the target from the rebase; verify the target local head is still unchanged before pushing. The rebase confines the cascade to inter-branch propagation.
|
|
201
|
+
|
|
202
|
+
After push success or rejection, re-probe the target against the pushed SHA and every **open dependent** in the baseline against its recorded OID and expected tip. Do not treat the target's intentional post-push OID change as divergence. Treat partial dependent updates as observed progress, and name the first rejected or divergent dependent layer in a recoverable residual. Prefer all-or-none when the installed manager proves atomic push, but never assume it. This manager-owned continuation is implicit in babysitting a managed layer. On a rebase conflict, run `gh stack rebase --abort` and leave a `needs-human`/upstack residual. On target movement or lease rejection, leave the residual and never retry with raw force. If local CLI membership cannot be re-confirmed, do not import or guess at the stack. Never claim stack readiness until manager order, ancestry, review, and CI are re-proven on every current head.
|
|
203
|
+
- **Manual dependency chain:** keep the requested PR as the target, qualify readiness relative to its parent, and report downstream impact. An eligible emitted `normal-base` item may be repaired target-locally, but never rebase, rewrite, restack, or otherwise mutate its dependent branches. A root with open child dependents remains eligible; a target push may make those children stale, which is a residual only.
|
|
204
|
+
- **Normal-base `BEHIND`:** require `host_branch_update_capability == true`; denied/`false` or `unknown` becomes `needs-human`, and that capability is never treated as Git push or direct push authority. Claim the exact item and revalidate its observed head and base OIDs. Invoke the host update operation once through GitHub's `PUT /repos/{owner}/{repo}/pulls/{number}/update-branch` endpoint with `expected_head_sha` set to the claimed observation's head SHA; never use an update helper that cannot transmit that precondition. Treat an HTTP 422 head mismatch as a stale claim: re-snapshot and reconcile without resubmitting. Host acceptance or a `mutation-observed` outcome is not completion. Confirm only after a fresh snapshot plus ancestry evidence proves the resulting head contains the observed base OID and the currency gate is clear. Remote head movement alone is not proof.
|
|
205
|
+
- **Normal-base `DIRTY`:** `host_branch_update_capability` is irrelevant and does not apply. Separately prove ordinary direct push authority to the exact head ref without mutating; unknown or denied proof parks the item. Require a verified clean PR-head checkout at the observed head, fetch the exact observed base OID, and perform a non-mutating merge preview against it. Fingerprint the sorted conflicted paths and stage blob identities, explicitly excluding the base OID. If `parked_semantic_fingerprints` is present, record `--currency-inspected-fingerprint` before any claim: an unchanged fingerprint remains parked, while changed evidence retires the old park and reopens the item.
|
|
206
|
+
|
|
207
|
+
A resolution is mechanical only when positive intent evidence leaves no reasonable alternative behavior. Two plausible resolutions, a material intent change, stale/unauthorized/incomplete evidence, or unbounded work requires a safe abort and the shared typed currency residual above; pass `--semantic-conflict-fingerprint <fingerprint>` with its residual mark and include options, tradeoffs, and a lean. For a mechanical case, claim and revalidate, merge the exact observed base, and mark `--currency-outcome mutation-observed` when the local merge starts. Resolve only the previewed conflict, validate proportionally, and use a normal push. Never rebase or force-push. Confirm only after the remote head equals or contains the validated merge commit and a fresh snapshot clears the gate; remote head movement alone is not confirmation. An interrupted local merge must reconcile or abort safely.
|
|
208
|
+
- **Manager/relationship probe error:** continue the review and CI streams, but perform no branch-currency mutation and do not declare ready until classification succeeds.
|
|
209
|
+
- **External head change / force-push:** the head SHA moved under the loop. The snapshot clears SHA-scoped CI state automatically; just re-snapshot. Never clobber unrelated pushed work.
|
|
210
|
+
- **PR closed or merged externally:** detected as `pr_state != "OPEN"` on any tick, which is a clean exit with a final status. The exception is a just-landed MERGED from this run's authorized `stack-land` land step, which is a layer transition (see Managed-stack continuation above), not a Terminal exit.
|
|
211
|
+
- **needs-human decision:** persist the complete returned residual once through the shared mark, show it immediately, and leave every covered review thread open. Keep handling independent streams. In interactive continuous mode the decision remains a standing residual that blocks merge-ready and stack advancement without ending the watch; in `mode:pipeline`, return the canonical set as soon as no autonomous work remains. Never auto-decline, auto-resolve, or wait silently on the decision.
|
|
212
|
+
- **No push access / fork PR:** a delegated push will fail. Detect that from the delegated skill's result, report it, and stop. The loop cannot make progress it has no permission to make.
|
|
213
|
+
- **CI that never completes:** a check stuck `IN_PROGRESS` for a long time will keep the loop from settling. The invocation budget is reached at either the 8h **active** cap (`invocation_elapsed_seconds`, which excludes suspended time) or the 3-calendar-day **wall-clock backstop** (`invocation_wall_elapsed_seconds`). When it is, hand back with the measured `invocation_elapsed_seconds` and the `max_runtime_ceiling` that was hit. Never substitute the age of persisted PR state, and never automatically start another budget.
|
|
214
|
+
- **Rate limits / transient API errors:** honor the reset time, back off, resume. The claim→confirm protocol protects against replay.
|
|
215
|
+
|
|
216
|
+
## Sustaining the watch (Step 5, full text)
|
|
217
|
+
|
|
218
|
+
**The self-sustaining watch runs autonomously after scope is set.** It never asks permission for the fixes, pushes, replies, resolves, and PR-description refreshes it is allowed to make, and a stack-wide posture does not re-ask at each layer. In interactive continuous mode, re-arm after a tick that reached neither a stop that ends the watch nor a managed-stack transition; a standing residual stays visible and blocks readiness or advancement while the watch continues around it. In `mode:pipeline`, apply the bounded stop above instead: when the canonical decision set is non-empty and no autonomous work remains, return it immediately rather than re-arming.
|
|
219
|
+
|
|
220
|
+
Re-arm `watch` after any mutation that moved the head, with the same invocation ID, start, and budget. A not-ready-but-not-blocked state is neither a stop nor a question; keep waiting for the background sentinel. Watcher silence carries no PR-state information; it means only that no wake condition has fired. Never infer review state from silence. When the user asks for status before a wake, run a fresh `snapshot` with the same invocation fields and report that state. The loop's only interactive question is Step 1's one-time posture and scope choice, asked once under `target` when a managed stack is confirmed.
|
|
221
|
+
|
|
222
|
+
In **checkpoint mode** you are done after Step 4; the next tick is the user re-running the skill. Because every tick is resumable from disk, each wake (a `watch` sentinel, a scheduler fire, or a manual re-run) is a clean re-entry into Step 2.
|