autonomous-coding-toolkit 1.0.0
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/.claude-plugin/marketplace.json +22 -0
- package/.claude-plugin/plugin.json +13 -0
- package/LICENSE +21 -0
- package/Makefile +21 -0
- package/README.md +140 -0
- package/SECURITY.md +28 -0
- package/agents/bash-expert.md +113 -0
- package/agents/dependency-auditor.md +138 -0
- package/agents/integration-tester.md +120 -0
- package/agents/lesson-scanner.md +149 -0
- package/agents/python-expert.md +179 -0
- package/agents/service-monitor.md +141 -0
- package/agents/shell-expert.md +147 -0
- package/benchmarks/runner.sh +147 -0
- package/benchmarks/tasks/01-rest-endpoint/rubric.sh +29 -0
- package/benchmarks/tasks/01-rest-endpoint/task.md +17 -0
- package/benchmarks/tasks/02-refactor-module/task.md +8 -0
- package/benchmarks/tasks/03-fix-integration-bug/task.md +8 -0
- package/benchmarks/tasks/04-add-test-coverage/task.md +8 -0
- package/benchmarks/tasks/05-multi-file-feature/task.md +8 -0
- package/bin/act.js +238 -0
- package/commands/autocode.md +6 -0
- package/commands/cancel-ralph.md +18 -0
- package/commands/code-factory.md +53 -0
- package/commands/create-prd.md +55 -0
- package/commands/ralph-loop.md +18 -0
- package/commands/run-plan.md +117 -0
- package/commands/submit-lesson.md +122 -0
- package/docs/ARCHITECTURE.md +630 -0
- package/docs/CONTRIBUTING.md +125 -0
- package/docs/lessons/0001-bare-exception-swallowing.md +34 -0
- package/docs/lessons/0002-async-def-without-await.md +28 -0
- package/docs/lessons/0003-create-task-without-callback.md +28 -0
- package/docs/lessons/0004-hardcoded-test-counts.md +28 -0
- package/docs/lessons/0005-sqlite-without-closing.md +33 -0
- package/docs/lessons/0006-venv-pip-path.md +27 -0
- package/docs/lessons/0007-runner-state-self-rejection.md +35 -0
- package/docs/lessons/0008-quality-gate-blind-spot.md +33 -0
- package/docs/lessons/0009-parser-overcount-empty-batches.md +36 -0
- package/docs/lessons/0010-local-outside-function-bash.md +33 -0
- package/docs/lessons/0011-batch-tests-for-unimplemented-code.md +36 -0
- package/docs/lessons/0012-api-markdown-unescaped-chars.md +33 -0
- package/docs/lessons/0013-export-prefix-env-parsing.md +33 -0
- package/docs/lessons/0014-decorator-registry-import-side-effect.md +43 -0
- package/docs/lessons/0015-frontend-backend-schema-drift.md +43 -0
- package/docs/lessons/0016-event-driven-cold-start-seeding.md +44 -0
- package/docs/lessons/0017-copy-paste-logic-diverges.md +43 -0
- package/docs/lessons/0018-layer-passes-pipeline-broken.md +45 -0
- package/docs/lessons/0019-systemd-envfile-ignores-export.md +41 -0
- package/docs/lessons/0020-persist-state-incrementally.md +44 -0
- package/docs/lessons/0021-dual-axis-testing.md +48 -0
- package/docs/lessons/0022-jsx-factory-shadowing.md +43 -0
- package/docs/lessons/0023-static-analysis-spiral.md +51 -0
- package/docs/lessons/0024-shared-pipeline-implementation.md +55 -0
- package/docs/lessons/0025-defense-in-depth-all-entry-points.md +65 -0
- package/docs/lessons/0026-linter-no-rules-false-enforcement.md +54 -0
- package/docs/lessons/0027-jsx-silent-prop-drop.md +64 -0
- package/docs/lessons/0028-no-infrastructure-in-client-code.md +49 -0
- package/docs/lessons/0029-never-write-secrets-to-files.md +61 -0
- package/docs/lessons/0030-cache-merge-not-replace.md +62 -0
- package/docs/lessons/0031-verify-units-at-boundaries.md +66 -0
- package/docs/lessons/0032-module-lifecycle-subscribe-unsubscribe.md +89 -0
- package/docs/lessons/0033-async-iteration-mutable-snapshot.md +72 -0
- package/docs/lessons/0034-caller-missing-await-silent-discard.md +65 -0
- package/docs/lessons/0035-duplicate-registration-silent-overwrite.md +85 -0
- package/docs/lessons/0036-websocket-dirty-disconnect.md +33 -0
- package/docs/lessons/0037-parallel-agents-worktree-corruption.md +31 -0
- package/docs/lessons/0038-subscribe-no-stored-ref.md +36 -0
- package/docs/lessons/0039-fallback-or-default-hides-bugs.md +34 -0
- package/docs/lessons/0040-event-firehose-filter-first.md +36 -0
- package/docs/lessons/0041-ambiguous-base-dir-path-nesting.md +32 -0
- package/docs/lessons/0042-spec-compliance-insufficient.md +36 -0
- package/docs/lessons/0043-exact-count-extensible-collections.md +32 -0
- package/docs/lessons/0044-relative-file-deps-worktree.md +39 -0
- package/docs/lessons/0045-iterative-design-improvement.md +33 -0
- package/docs/lessons/0046-plan-assertion-math-bugs.md +38 -0
- package/docs/lessons/0047-pytest-single-threaded-default.md +37 -0
- package/docs/lessons/0048-integration-wiring-batch.md +40 -0
- package/docs/lessons/0049-ab-verification.md +41 -0
- package/docs/lessons/0050-editing-sourced-files-during-execution.md +33 -0
- package/docs/lessons/0051-infrastructure-fixes-cant-self-heal.md +30 -0
- package/docs/lessons/0052-uncommitted-changes-poison-quality-gates.md +31 -0
- package/docs/lessons/0053-jq-compact-flag-inconsistency.md +31 -0
- package/docs/lessons/0054-parser-matches-inside-code-blocks.md +30 -0
- package/docs/lessons/0055-agents-compensate-for-garbled-prompts.md +31 -0
- package/docs/lessons/0056-grep-count-exit-code-on-zero.md +42 -0
- package/docs/lessons/0057-new-artifacts-break-git-clean-gates.md +42 -0
- package/docs/lessons/0058-dead-config-keys-never-consumed.md +49 -0
- package/docs/lessons/0059-contract-test-shared-structures.md +53 -0
- package/docs/lessons/0060-set-e-silent-death-in-runners.md +53 -0
- package/docs/lessons/0061-context-injection-dirty-state.md +50 -0
- package/docs/lessons/0062-sibling-bug-neighborhood-scan.md +29 -0
- package/docs/lessons/0063-one-flag-two-lifetimes.md +31 -0
- package/docs/lessons/0064-test-passes-wrong-reason.md +31 -0
- package/docs/lessons/0065-pipefail-grep-count-double-output.md +39 -0
- package/docs/lessons/0066-local-keyword-outside-function.md +37 -0
- package/docs/lessons/0067-stdin-hang-non-interactive-shell.md +36 -0
- package/docs/lessons/0068-agent-builds-wrong-thing-correctly.md +31 -0
- package/docs/lessons/0069-plan-quality-dominates-execution.md +30 -0
- package/docs/lessons/0070-spec-echo-back-prevents-drift.md +31 -0
- package/docs/lessons/0071-positive-instructions-outperform-negative.md +30 -0
- package/docs/lessons/0072-lost-in-the-middle-context-placement.md +30 -0
- package/docs/lessons/0073-unscoped-lessons-cause-false-positives.md +30 -0
- package/docs/lessons/0074-stale-context-injection-wrong-batch.md +32 -0
- package/docs/lessons/0075-research-artifacts-must-persist.md +32 -0
- package/docs/lessons/0076-wrong-decomposition-contaminates-downstream.md +30 -0
- package/docs/lessons/0077-cherry-pick-merges-need-manual-resolution.md +30 -0
- package/docs/lessons/0078-static-review-without-live-test.md +30 -0
- package/docs/lessons/0079-integration-wiring-batch-required.md +32 -0
- package/docs/lessons/FRAMEWORK.md +161 -0
- package/docs/lessons/SUMMARY.md +201 -0
- package/docs/lessons/TEMPLATE.md +85 -0
- package/docs/plans/2026-02-21-code-factory-v2-design.md +204 -0
- package/docs/plans/2026-02-21-code-factory-v2-implementation-plan.md +2189 -0
- package/docs/plans/2026-02-21-code-factory-v2-phase4-design.md +537 -0
- package/docs/plans/2026-02-21-code-factory-v2-phase4-implementation-plan.md +2012 -0
- package/docs/plans/2026-02-21-hardening-pass-design.md +108 -0
- package/docs/plans/2026-02-21-hardening-pass-plan.md +1378 -0
- package/docs/plans/2026-02-21-mab-research-report.md +406 -0
- package/docs/plans/2026-02-21-marketplace-restructure-design.md +240 -0
- package/docs/plans/2026-02-21-marketplace-restructure-plan.md +832 -0
- package/docs/plans/2026-02-21-phase4-completion-plan.md +697 -0
- package/docs/plans/2026-02-21-validator-suite-design.md +148 -0
- package/docs/plans/2026-02-21-validator-suite-plan.md +540 -0
- package/docs/plans/2026-02-22-mab-research-round2.md +556 -0
- package/docs/plans/2026-02-22-mab-run-design.md +462 -0
- package/docs/plans/2026-02-22-mab-run-plan.md +2046 -0
- package/docs/plans/2026-02-22-operations-design-methodology-research.md +681 -0
- package/docs/plans/2026-02-22-research-agent-failure-taxonomy.md +532 -0
- package/docs/plans/2026-02-22-research-code-guideline-policies.md +886 -0
- package/docs/plans/2026-02-22-research-codebase-audit-refactoring.md +908 -0
- package/docs/plans/2026-02-22-research-coding-standards-documentation.md +541 -0
- package/docs/plans/2026-02-22-research-competitive-landscape.md +687 -0
- package/docs/plans/2026-02-22-research-comprehensive-testing.md +1076 -0
- package/docs/plans/2026-02-22-research-context-utilization.md +459 -0
- package/docs/plans/2026-02-22-research-cost-quality-tradeoff.md +548 -0
- package/docs/plans/2026-02-22-research-lesson-transferability.md +508 -0
- package/docs/plans/2026-02-22-research-multi-agent-coordination.md +312 -0
- package/docs/plans/2026-02-22-research-phase-integration.md +602 -0
- package/docs/plans/2026-02-22-research-plan-quality.md +428 -0
- package/docs/plans/2026-02-22-research-prompt-engineering.md +558 -0
- package/docs/plans/2026-02-22-research-unconventional-perspectives.md +528 -0
- package/docs/plans/2026-02-22-research-user-adoption.md +638 -0
- package/docs/plans/2026-02-22-research-verification-effectiveness.md +433 -0
- package/docs/plans/2026-02-23-agent-suite-design.md +299 -0
- package/docs/plans/2026-02-23-agent-suite-plan.md +578 -0
- package/docs/plans/2026-02-23-phase3-cost-infrastructure-design.md +148 -0
- package/docs/plans/2026-02-23-phase3-cost-infrastructure-plan.md +1062 -0
- package/docs/plans/2026-02-23-research-bash-expert-agent.md +543 -0
- package/docs/plans/2026-02-23-research-dependency-auditor-agent.md +564 -0
- package/docs/plans/2026-02-23-research-improving-existing-agents.md +503 -0
- package/docs/plans/2026-02-23-research-integration-tester-agent.md +454 -0
- package/docs/plans/2026-02-23-research-python-expert-agent.md +429 -0
- package/docs/plans/2026-02-23-research-service-monitor-agent.md +425 -0
- package/docs/plans/2026-02-23-research-shell-expert-agent.md +533 -0
- package/docs/plans/2026-02-23-roadmap-to-completion.md +530 -0
- package/docs/plans/2026-02-24-headless-module-split-design.md +98 -0
- package/docs/plans/2026-02-24-headless-module-split.md +443 -0
- package/docs/plans/2026-02-24-lesson-scope-metadata-design.md +228 -0
- package/docs/plans/2026-02-24-lesson-scope-metadata-plan.md +968 -0
- package/docs/plans/2026-02-24-npm-packaging-design.md +841 -0
- package/docs/plans/2026-02-24-npm-packaging-plan.md +1965 -0
- package/docs/plans/audit-findings.md +186 -0
- package/docs/telegram-notification-format.md +98 -0
- package/examples/example-plan.md +51 -0
- package/examples/example-prd.json +72 -0
- package/examples/example-roadmap.md +33 -0
- package/examples/quickstart-plan.md +63 -0
- package/hooks/hooks.json +26 -0
- package/hooks/setup-symlinks.sh +48 -0
- package/hooks/stop-hook.sh +135 -0
- package/package.json +47 -0
- package/policies/bash.md +71 -0
- package/policies/python.md +71 -0
- package/policies/testing.md +61 -0
- package/policies/universal.md +60 -0
- package/scripts/analyze-report.sh +97 -0
- package/scripts/architecture-map.sh +145 -0
- package/scripts/auto-compound.sh +273 -0
- package/scripts/batch-audit.sh +42 -0
- package/scripts/batch-test.sh +101 -0
- package/scripts/entropy-audit.sh +221 -0
- package/scripts/failure-digest.sh +51 -0
- package/scripts/generate-ast-rules.sh +96 -0
- package/scripts/init.sh +112 -0
- package/scripts/lesson-check.sh +428 -0
- package/scripts/lib/common.sh +61 -0
- package/scripts/lib/cost-tracking.sh +153 -0
- package/scripts/lib/ollama.sh +60 -0
- package/scripts/lib/progress-writer.sh +128 -0
- package/scripts/lib/run-plan-context.sh +215 -0
- package/scripts/lib/run-plan-echo-back.sh +231 -0
- package/scripts/lib/run-plan-headless.sh +396 -0
- package/scripts/lib/run-plan-notify.sh +57 -0
- package/scripts/lib/run-plan-parser.sh +81 -0
- package/scripts/lib/run-plan-prompt.sh +215 -0
- package/scripts/lib/run-plan-quality-gate.sh +132 -0
- package/scripts/lib/run-plan-routing.sh +315 -0
- package/scripts/lib/run-plan-sampling.sh +170 -0
- package/scripts/lib/run-plan-scoring.sh +146 -0
- package/scripts/lib/run-plan-state.sh +142 -0
- package/scripts/lib/run-plan-team.sh +199 -0
- package/scripts/lib/telegram.sh +54 -0
- package/scripts/lib/thompson-sampling.sh +176 -0
- package/scripts/license-check.sh +74 -0
- package/scripts/mab-run.sh +575 -0
- package/scripts/module-size-check.sh +146 -0
- package/scripts/patterns/async-no-await.yml +5 -0
- package/scripts/patterns/bare-except.yml +6 -0
- package/scripts/patterns/empty-catch.yml +6 -0
- package/scripts/patterns/hardcoded-localhost.yml +9 -0
- package/scripts/patterns/retry-loop-no-backoff.yml +12 -0
- package/scripts/pipeline-status.sh +197 -0
- package/scripts/policy-check.sh +226 -0
- package/scripts/prior-art-search.sh +133 -0
- package/scripts/promote-mab-lessons.sh +126 -0
- package/scripts/prompts/agent-a-superpowers.md +29 -0
- package/scripts/prompts/agent-b-ralph.md +29 -0
- package/scripts/prompts/judge-agent.md +61 -0
- package/scripts/prompts/planner-agent.md +44 -0
- package/scripts/pull-community-lessons.sh +90 -0
- package/scripts/quality-gate.sh +266 -0
- package/scripts/research-gate.sh +90 -0
- package/scripts/run-plan.sh +329 -0
- package/scripts/scope-infer.sh +159 -0
- package/scripts/setup-ralph-loop.sh +155 -0
- package/scripts/telemetry.sh +230 -0
- package/scripts/tests/run-all-tests.sh +52 -0
- package/scripts/tests/test-act-cli.sh +46 -0
- package/scripts/tests/test-agents-md.sh +87 -0
- package/scripts/tests/test-analyze-report.sh +114 -0
- package/scripts/tests/test-architecture-map.sh +89 -0
- package/scripts/tests/test-auto-compound.sh +169 -0
- package/scripts/tests/test-batch-test.sh +65 -0
- package/scripts/tests/test-benchmark-runner.sh +25 -0
- package/scripts/tests/test-common.sh +168 -0
- package/scripts/tests/test-cost-tracking.sh +158 -0
- package/scripts/tests/test-echo-back.sh +180 -0
- package/scripts/tests/test-entropy-audit.sh +146 -0
- package/scripts/tests/test-failure-digest.sh +66 -0
- package/scripts/tests/test-generate-ast-rules.sh +145 -0
- package/scripts/tests/test-helpers.sh +82 -0
- package/scripts/tests/test-init.sh +47 -0
- package/scripts/tests/test-lesson-check.sh +278 -0
- package/scripts/tests/test-lesson-local.sh +55 -0
- package/scripts/tests/test-license-check.sh +109 -0
- package/scripts/tests/test-mab-run.sh +182 -0
- package/scripts/tests/test-ollama-lib.sh +49 -0
- package/scripts/tests/test-ollama.sh +60 -0
- package/scripts/tests/test-pipeline-status.sh +198 -0
- package/scripts/tests/test-policy-check.sh +124 -0
- package/scripts/tests/test-prior-art-search.sh +96 -0
- package/scripts/tests/test-progress-writer.sh +140 -0
- package/scripts/tests/test-promote-mab-lessons.sh +110 -0
- package/scripts/tests/test-pull-community-lessons.sh +149 -0
- package/scripts/tests/test-quality-gate.sh +241 -0
- package/scripts/tests/test-research-gate.sh +132 -0
- package/scripts/tests/test-run-plan-cli.sh +86 -0
- package/scripts/tests/test-run-plan-context.sh +305 -0
- package/scripts/tests/test-run-plan-e2e.sh +153 -0
- package/scripts/tests/test-run-plan-headless.sh +424 -0
- package/scripts/tests/test-run-plan-notify.sh +124 -0
- package/scripts/tests/test-run-plan-parser.sh +217 -0
- package/scripts/tests/test-run-plan-prompt.sh +254 -0
- package/scripts/tests/test-run-plan-quality-gate.sh +222 -0
- package/scripts/tests/test-run-plan-routing.sh +178 -0
- package/scripts/tests/test-run-plan-scoring.sh +148 -0
- package/scripts/tests/test-run-plan-state.sh +261 -0
- package/scripts/tests/test-run-plan-team.sh +157 -0
- package/scripts/tests/test-scope-infer.sh +150 -0
- package/scripts/tests/test-setup-ralph-loop.sh +63 -0
- package/scripts/tests/test-telegram-env.sh +38 -0
- package/scripts/tests/test-telegram.sh +121 -0
- package/scripts/tests/test-telemetry.sh +46 -0
- package/scripts/tests/test-thompson-sampling.sh +139 -0
- package/scripts/tests/test-validate-all.sh +60 -0
- package/scripts/tests/test-validate-commands.sh +89 -0
- package/scripts/tests/test-validate-hooks.sh +98 -0
- package/scripts/tests/test-validate-lessons.sh +150 -0
- package/scripts/tests/test-validate-plan-quality.sh +235 -0
- package/scripts/tests/test-validate-plans.sh +187 -0
- package/scripts/tests/test-validate-plugin.sh +106 -0
- package/scripts/tests/test-validate-prd.sh +184 -0
- package/scripts/tests/test-validate-skills.sh +134 -0
- package/scripts/validate-all.sh +57 -0
- package/scripts/validate-commands.sh +67 -0
- package/scripts/validate-hooks.sh +89 -0
- package/scripts/validate-lessons.sh +98 -0
- package/scripts/validate-plan-quality.sh +369 -0
- package/scripts/validate-plans.sh +120 -0
- package/scripts/validate-plugin.sh +86 -0
- package/scripts/validate-policies.sh +42 -0
- package/scripts/validate-prd.sh +118 -0
- package/scripts/validate-skills.sh +96 -0
- package/skills/autocode/SKILL.md +285 -0
- package/skills/autocode/ab-verification.md +51 -0
- package/skills/autocode/code-quality-standards.md +37 -0
- package/skills/autocode/competitive-mode.md +364 -0
- package/skills/brainstorming/SKILL.md +97 -0
- package/skills/capture-lesson/SKILL.md +187 -0
- package/skills/check-lessons/SKILL.md +116 -0
- package/skills/dispatching-parallel-agents/SKILL.md +110 -0
- package/skills/executing-plans/SKILL.md +85 -0
- package/skills/finishing-a-development-branch/SKILL.md +201 -0
- package/skills/receiving-code-review/SKILL.md +72 -0
- package/skills/requesting-code-review/SKILL.md +59 -0
- package/skills/requesting-code-review/code-reviewer.md +82 -0
- package/skills/research/SKILL.md +145 -0
- package/skills/roadmap/SKILL.md +115 -0
- package/skills/subagent-driven-development/SKILL.md +98 -0
- package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +18 -0
- package/skills/subagent-driven-development/implementer-prompt.md +73 -0
- package/skills/subagent-driven-development/spec-reviewer-prompt.md +57 -0
- package/skills/systematic-debugging/SKILL.md +134 -0
- package/skills/systematic-debugging/condition-based-waiting.md +64 -0
- package/skills/systematic-debugging/defense-in-depth.md +32 -0
- package/skills/systematic-debugging/root-cause-tracing.md +55 -0
- package/skills/test-driven-development/SKILL.md +167 -0
- package/skills/using-git-worktrees/SKILL.md +219 -0
- package/skills/using-superpowers/SKILL.md +54 -0
- package/skills/verification-before-completion/SKILL.md +140 -0
- package/skills/verify/SKILL.md +82 -0
- package/skills/writing-plans/SKILL.md +128 -0
- package/skills/writing-skills/SKILL.md +93 -0
|
@@ -0,0 +1,2189 @@
|
|
|
1
|
+
# Code Factory v2 Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
|
4
|
+
|
|
5
|
+
**Goal:** Eliminate code duplication, fix broken pipeline steps, expand quality gates, and add new capabilities to the Code Factory toolchain.
|
|
6
|
+
|
|
7
|
+
**Architecture:** Extract shared bash libraries from duplicated code across 6 scripts, then refactor each script to use them. Fix accuracy issues (test count parsing, cross-batch context). Add lint, prior-art search, and pipeline status tools. Finally add failure digest, structured context refs, and team mode.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Bash, jq, ruff (Python linter), gh CLI, ast-grep (structural code search)
|
|
10
|
+
|
|
11
|
+
## Quality Gates
|
|
12
|
+
|
|
13
|
+
Between each batch, run:
|
|
14
|
+
```bash
|
|
15
|
+
scripts/tests/run-all-tests.sh # All existing + new tests pass
|
|
16
|
+
wc -l scripts/*.sh scripts/lib/*.sh # No script >300 lines
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Batch 1: Foundation Libraries
|
|
22
|
+
|
|
23
|
+
Create `scripts/lib/common.sh` and `scripts/lib/ollama.sh` — shared functions extracted from duplicated code across scripts. These are the building blocks for all subsequent refactoring.
|
|
24
|
+
|
|
25
|
+
### Task 1: Create common.sh with detect_project_type
|
|
26
|
+
|
|
27
|
+
**Files:**
|
|
28
|
+
- Create: `scripts/lib/common.sh`
|
|
29
|
+
- Create: `scripts/tests/test-common.sh`
|
|
30
|
+
|
|
31
|
+
**Step 1: Write the failing test**
|
|
32
|
+
|
|
33
|
+
Create `scripts/tests/test-common.sh`:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
#!/usr/bin/env bash
|
|
37
|
+
# Test shared common.sh functions
|
|
38
|
+
set -euo pipefail
|
|
39
|
+
|
|
40
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
41
|
+
source "$SCRIPT_DIR/../lib/common.sh"
|
|
42
|
+
|
|
43
|
+
FAILURES=0
|
|
44
|
+
TESTS=0
|
|
45
|
+
|
|
46
|
+
assert_eq() {
|
|
47
|
+
local desc="$1" expected="$2" actual="$3"
|
|
48
|
+
TESTS=$((TESTS + 1))
|
|
49
|
+
if [[ "$expected" != "$actual" ]]; then
|
|
50
|
+
echo "FAIL: $desc"
|
|
51
|
+
echo " expected: $expected"
|
|
52
|
+
echo " actual: $actual"
|
|
53
|
+
FAILURES=$((FAILURES + 1))
|
|
54
|
+
else
|
|
55
|
+
echo "PASS: $desc"
|
|
56
|
+
fi
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
assert_exit() {
|
|
60
|
+
local desc="$1" expected_exit="$2"
|
|
61
|
+
shift 2
|
|
62
|
+
local actual_exit=0
|
|
63
|
+
"$@" || actual_exit=$?
|
|
64
|
+
TESTS=$((TESTS + 1))
|
|
65
|
+
if [[ "$expected_exit" != "$actual_exit" ]]; then
|
|
66
|
+
echo "FAIL: $desc"
|
|
67
|
+
echo " expected exit: $expected_exit"
|
|
68
|
+
echo " actual exit: $actual_exit"
|
|
69
|
+
FAILURES=$((FAILURES + 1))
|
|
70
|
+
else
|
|
71
|
+
echo "PASS: $desc"
|
|
72
|
+
fi
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
WORK=$(mktemp -d)
|
|
76
|
+
trap "rm -rf '$WORK'" EXIT
|
|
77
|
+
|
|
78
|
+
# === detect_project_type tests ===
|
|
79
|
+
|
|
80
|
+
# Python project (pyproject.toml)
|
|
81
|
+
mkdir -p "$WORK/py-proj"
|
|
82
|
+
touch "$WORK/py-proj/pyproject.toml"
|
|
83
|
+
val=$(detect_project_type "$WORK/py-proj")
|
|
84
|
+
assert_eq "detect_project_type: pyproject.toml -> python" "python" "$val"
|
|
85
|
+
|
|
86
|
+
# Python project (setup.py)
|
|
87
|
+
mkdir -p "$WORK/py-setup"
|
|
88
|
+
touch "$WORK/py-setup/setup.py"
|
|
89
|
+
val=$(detect_project_type "$WORK/py-setup")
|
|
90
|
+
assert_eq "detect_project_type: setup.py -> python" "python" "$val"
|
|
91
|
+
|
|
92
|
+
# Node project (package.json)
|
|
93
|
+
mkdir -p "$WORK/node-proj"
|
|
94
|
+
echo '{"name":"test"}' > "$WORK/node-proj/package.json"
|
|
95
|
+
val=$(detect_project_type "$WORK/node-proj")
|
|
96
|
+
assert_eq "detect_project_type: package.json -> node" "node" "$val"
|
|
97
|
+
|
|
98
|
+
# Makefile project
|
|
99
|
+
mkdir -p "$WORK/make-proj"
|
|
100
|
+
echo 'test:' > "$WORK/make-proj/Makefile"
|
|
101
|
+
val=$(detect_project_type "$WORK/make-proj")
|
|
102
|
+
assert_eq "detect_project_type: Makefile -> make" "make" "$val"
|
|
103
|
+
|
|
104
|
+
# Unknown project
|
|
105
|
+
mkdir -p "$WORK/empty"
|
|
106
|
+
val=$(detect_project_type "$WORK/empty")
|
|
107
|
+
assert_eq "detect_project_type: empty -> unknown" "unknown" "$val"
|
|
108
|
+
|
|
109
|
+
# === strip_json_fences tests ===
|
|
110
|
+
|
|
111
|
+
val=$(echo '```json
|
|
112
|
+
{"key":"value"}
|
|
113
|
+
```' | strip_json_fences)
|
|
114
|
+
assert_eq "strip_json_fences: removes fences" '{"key":"value"}' "$val"
|
|
115
|
+
|
|
116
|
+
val=$(echo '{"key":"value"}' | strip_json_fences)
|
|
117
|
+
assert_eq "strip_json_fences: plain JSON unchanged" '{"key":"value"}' "$val"
|
|
118
|
+
|
|
119
|
+
# === check_memory_available tests ===
|
|
120
|
+
|
|
121
|
+
# This test just verifies the function exists and returns 0/1
|
|
122
|
+
# We can't control actual memory, so test the interface
|
|
123
|
+
assert_exit "check_memory_available: runs without error" 0 \
|
|
124
|
+
check_memory_available 0
|
|
125
|
+
|
|
126
|
+
# === require_command tests ===
|
|
127
|
+
|
|
128
|
+
assert_exit "require_command: bash exists" 0 \
|
|
129
|
+
require_command "bash"
|
|
130
|
+
|
|
131
|
+
assert_exit "require_command: nonexistent-binary-xyz fails" 1 \
|
|
132
|
+
require_command "nonexistent-binary-xyz"
|
|
133
|
+
|
|
134
|
+
# === Summary ===
|
|
135
|
+
echo ""
|
|
136
|
+
echo "Results: $((TESTS - FAILURES))/$TESTS passed"
|
|
137
|
+
if [[ $FAILURES -gt 0 ]]; then
|
|
138
|
+
echo "FAILURES: $FAILURES"
|
|
139
|
+
exit 1
|
|
140
|
+
fi
|
|
141
|
+
echo "ALL PASSED"
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Step 2: Run test to verify it fails**
|
|
145
|
+
|
|
146
|
+
Run: `bash scripts/tests/test-common.sh`
|
|
147
|
+
Expected: FAIL — `common.sh` does not exist yet
|
|
148
|
+
|
|
149
|
+
**Step 3: Write minimal implementation**
|
|
150
|
+
|
|
151
|
+
Create `scripts/lib/common.sh`:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
#!/usr/bin/env bash
|
|
155
|
+
# common.sh — Shared utility functions for Code Factory scripts
|
|
156
|
+
#
|
|
157
|
+
# Source this in any script: source "$SCRIPT_DIR/lib/common.sh"
|
|
158
|
+
#
|
|
159
|
+
# Functions:
|
|
160
|
+
# detect_project_type <dir> -> "python"|"node"|"make"|"unknown"
|
|
161
|
+
# strip_json_fences -> stdin filter: remove ```json wrappers
|
|
162
|
+
# check_memory_available <threshold_gb> -> exit 0 if available >= threshold, 1 otherwise
|
|
163
|
+
# require_command <cmd> [install_hint] -> exit 1 with message if cmd not found
|
|
164
|
+
|
|
165
|
+
detect_project_type() {
|
|
166
|
+
local dir="$1"
|
|
167
|
+
if [[ -f "$dir/pyproject.toml" || -f "$dir/setup.py" || -f "$dir/pytest.ini" ]]; then
|
|
168
|
+
echo "python"
|
|
169
|
+
elif [[ -f "$dir/package.json" ]]; then
|
|
170
|
+
echo "node"
|
|
171
|
+
elif [[ -f "$dir/Makefile" ]]; then
|
|
172
|
+
echo "make"
|
|
173
|
+
else
|
|
174
|
+
echo "unknown"
|
|
175
|
+
fi
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
strip_json_fences() {
|
|
179
|
+
sed '/^```json$/d; /^```$/d'
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
check_memory_available() {
|
|
183
|
+
local threshold_gb="${1:-4}"
|
|
184
|
+
local available_gb
|
|
185
|
+
available_gb=$(free -g 2>/dev/null | awk '/Mem:/{print $7}' || echo "999")
|
|
186
|
+
if [[ "$available_gb" -ge "$threshold_gb" ]]; then
|
|
187
|
+
return 0
|
|
188
|
+
else
|
|
189
|
+
echo "WARNING: Low memory (${available_gb}G available, need ${threshold_gb}G)" >&2
|
|
190
|
+
return 1
|
|
191
|
+
fi
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
require_command() {
|
|
195
|
+
local cmd="$1"
|
|
196
|
+
local hint="${2:-}"
|
|
197
|
+
if ! command -v "$cmd" >/dev/null 2>&1; then
|
|
198
|
+
echo "ERROR: Required command not found: $cmd" >&2
|
|
199
|
+
if [[ -n "$hint" ]]; then
|
|
200
|
+
echo " Install with: $hint" >&2
|
|
201
|
+
fi
|
|
202
|
+
return 1
|
|
203
|
+
fi
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**Step 4: Run test to verify it passes**
|
|
208
|
+
|
|
209
|
+
Run: `bash scripts/tests/test-common.sh`
|
|
210
|
+
Expected: ALL PASSED
|
|
211
|
+
|
|
212
|
+
**Step 5: Commit**
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
git add scripts/lib/common.sh scripts/tests/test-common.sh
|
|
216
|
+
git commit -m "feat: create scripts/lib/common.sh shared library with tests"
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Task 2: Create ollama.sh shared library
|
|
220
|
+
|
|
221
|
+
**Files:**
|
|
222
|
+
- Create: `scripts/lib/ollama.sh`
|
|
223
|
+
- Create: `scripts/tests/test-ollama.sh`
|
|
224
|
+
|
|
225
|
+
**Step 1: Write the failing test**
|
|
226
|
+
|
|
227
|
+
Create `scripts/tests/test-ollama.sh`. Note: Ollama tests must work offline (mock the HTTP call). Test the URL construction and JSON parsing, not the actual API call.
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
#!/usr/bin/env bash
|
|
231
|
+
# Test ollama.sh shared library functions
|
|
232
|
+
set -euo pipefail
|
|
233
|
+
|
|
234
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
235
|
+
source "$SCRIPT_DIR/../lib/common.sh"
|
|
236
|
+
source "$SCRIPT_DIR/../lib/ollama.sh"
|
|
237
|
+
|
|
238
|
+
FAILURES=0
|
|
239
|
+
TESTS=0
|
|
240
|
+
|
|
241
|
+
assert_eq() {
|
|
242
|
+
local desc="$1" expected="$2" actual="$3"
|
|
243
|
+
TESTS=$((TESTS + 1))
|
|
244
|
+
if [[ "$expected" != "$actual" ]]; then
|
|
245
|
+
echo "FAIL: $desc"
|
|
246
|
+
echo " expected: $expected"
|
|
247
|
+
echo " actual: $actual"
|
|
248
|
+
FAILURES=$((FAILURES + 1))
|
|
249
|
+
else
|
|
250
|
+
echo "PASS: $desc"
|
|
251
|
+
fi
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
# === ollama_build_payload tests ===
|
|
255
|
+
|
|
256
|
+
val=$(ollama_build_payload "deepseek-r1:8b" "Hello world")
|
|
257
|
+
model=$(echo "$val" | jq -r '.model')
|
|
258
|
+
assert_eq "ollama_build_payload: model set" "deepseek-r1:8b" "$model"
|
|
259
|
+
|
|
260
|
+
stream=$(echo "$val" | jq -r '.stream')
|
|
261
|
+
assert_eq "ollama_build_payload: stream false" "false" "$stream"
|
|
262
|
+
|
|
263
|
+
# === ollama_parse_response tests ===
|
|
264
|
+
|
|
265
|
+
val=$(echo '{"response":"hello"}' | ollama_parse_response)
|
|
266
|
+
assert_eq "ollama_parse_response: extracts response" "hello" "$val"
|
|
267
|
+
|
|
268
|
+
val=$(echo '{}' | ollama_parse_response)
|
|
269
|
+
assert_eq "ollama_parse_response: empty on missing field" "" "$val"
|
|
270
|
+
|
|
271
|
+
# === ollama_extract_json tests ===
|
|
272
|
+
|
|
273
|
+
val=$(echo '```json
|
|
274
|
+
{"key":"value"}
|
|
275
|
+
```' | ollama_extract_json)
|
|
276
|
+
key=$(echo "$val" | jq -r '.key')
|
|
277
|
+
assert_eq "ollama_extract_json: strips fences and validates" "value" "$key"
|
|
278
|
+
|
|
279
|
+
val=$(echo 'not json at all' | ollama_extract_json)
|
|
280
|
+
assert_eq "ollama_extract_json: returns empty on invalid" "" "$val"
|
|
281
|
+
|
|
282
|
+
# === Summary ===
|
|
283
|
+
echo ""
|
|
284
|
+
echo "Results: $((TESTS - FAILURES))/$TESTS passed"
|
|
285
|
+
if [[ $FAILURES -gt 0 ]]; then
|
|
286
|
+
echo "FAILURES: $FAILURES"
|
|
287
|
+
exit 1
|
|
288
|
+
fi
|
|
289
|
+
echo "ALL PASSED"
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
**Step 2: Run test to verify it fails**
|
|
293
|
+
|
|
294
|
+
Run: `bash scripts/tests/test-ollama.sh`
|
|
295
|
+
Expected: FAIL — `ollama.sh` does not exist
|
|
296
|
+
|
|
297
|
+
**Step 3: Write minimal implementation**
|
|
298
|
+
|
|
299
|
+
Create `scripts/lib/ollama.sh`:
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
#!/usr/bin/env bash
|
|
303
|
+
# ollama.sh — Shared Ollama API interaction for Code Factory scripts
|
|
304
|
+
#
|
|
305
|
+
# Requires: common.sh sourced first (for strip_json_fences)
|
|
306
|
+
#
|
|
307
|
+
# Functions:
|
|
308
|
+
# ollama_build_payload <model> <prompt> -> JSON payload string
|
|
309
|
+
# ollama_parse_response -> stdin filter: extract .response from Ollama JSON
|
|
310
|
+
# ollama_extract_json -> stdin filter: parse response, strip fences, validate JSON
|
|
311
|
+
# ollama_query <model> <prompt> -> full query: build payload, call API, return response text
|
|
312
|
+
# ollama_query_json <model> <prompt> -> full query + JSON extraction
|
|
313
|
+
|
|
314
|
+
OLLAMA_DIRECT_URL="${OLLAMA_DIRECT_URL:-http://localhost:11434}"
|
|
315
|
+
OLLAMA_QUEUE_URL="${OLLAMA_QUEUE_URL:-http://localhost:7683}"
|
|
316
|
+
|
|
317
|
+
ollama_build_payload() {
|
|
318
|
+
local model="$1" prompt="$2"
|
|
319
|
+
jq -n --arg model "$model" --arg prompt "$prompt" \
|
|
320
|
+
'{model: $model, prompt: $prompt, stream: false}'
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
ollama_parse_response() {
|
|
324
|
+
jq -r '.response // empty'
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
ollama_extract_json() {
|
|
328
|
+
local text
|
|
329
|
+
text=$(cat)
|
|
330
|
+
# Strip fences
|
|
331
|
+
text=$(echo "$text" | strip_json_fences)
|
|
332
|
+
# Validate JSON
|
|
333
|
+
if echo "$text" | jq . >/dev/null 2>&1; then
|
|
334
|
+
echo "$text"
|
|
335
|
+
else
|
|
336
|
+
echo ""
|
|
337
|
+
fi
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
ollama_query() {
|
|
341
|
+
local model="$1" prompt="$2"
|
|
342
|
+
local payload api_url response
|
|
343
|
+
|
|
344
|
+
payload=$(ollama_build_payload "$model" "$prompt")
|
|
345
|
+
|
|
346
|
+
# Prefer queue if available
|
|
347
|
+
if curl -s -o /dev/null -w '%{http_code}' "$OLLAMA_QUEUE_URL/health" 2>/dev/null | grep -q "200"; then
|
|
348
|
+
api_url="$OLLAMA_QUEUE_URL/api/generate"
|
|
349
|
+
else
|
|
350
|
+
api_url="$OLLAMA_DIRECT_URL/api/generate"
|
|
351
|
+
fi
|
|
352
|
+
|
|
353
|
+
response=$(curl -s "$api_url" -d "$payload" --max-time 300)
|
|
354
|
+
echo "$response" | ollama_parse_response
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
ollama_query_json() {
|
|
358
|
+
local model="$1" prompt="$2"
|
|
359
|
+
ollama_query "$model" "$prompt" | ollama_extract_json
|
|
360
|
+
}
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
**Step 4: Run test to verify it passes**
|
|
364
|
+
|
|
365
|
+
Run: `bash scripts/tests/test-ollama.sh`
|
|
366
|
+
Expected: ALL PASSED
|
|
367
|
+
|
|
368
|
+
**Step 5: Commit**
|
|
369
|
+
|
|
370
|
+
```bash
|
|
371
|
+
git add scripts/lib/ollama.sh scripts/tests/test-ollama.sh
|
|
372
|
+
git commit -m "feat: create scripts/lib/ollama.sh shared Ollama API library with tests"
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### Task 3: Create telegram.sh shared library
|
|
376
|
+
|
|
377
|
+
**Files:**
|
|
378
|
+
- Create: `scripts/lib/telegram.sh`
|
|
379
|
+
- Modify: `scripts/lib/run-plan-notify.sh` (remove `_load_telegram_env` and `_send_telegram`, source telegram.sh)
|
|
380
|
+
|
|
381
|
+
**Step 1: Write the failing test**
|
|
382
|
+
|
|
383
|
+
Create `scripts/tests/test-telegram.sh`:
|
|
384
|
+
|
|
385
|
+
```bash
|
|
386
|
+
#!/usr/bin/env bash
|
|
387
|
+
# Test telegram.sh shared library
|
|
388
|
+
set -euo pipefail
|
|
389
|
+
|
|
390
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
391
|
+
source "$SCRIPT_DIR/../lib/telegram.sh"
|
|
392
|
+
|
|
393
|
+
FAILURES=0
|
|
394
|
+
TESTS=0
|
|
395
|
+
|
|
396
|
+
assert_eq() {
|
|
397
|
+
local desc="$1" expected="$2" actual="$3"
|
|
398
|
+
TESTS=$((TESTS + 1))
|
|
399
|
+
if [[ "$expected" != "$actual" ]]; then
|
|
400
|
+
echo "FAIL: $desc"
|
|
401
|
+
echo " expected: $expected"
|
|
402
|
+
echo " actual: $actual"
|
|
403
|
+
FAILURES=$((FAILURES + 1))
|
|
404
|
+
else
|
|
405
|
+
echo "PASS: $desc"
|
|
406
|
+
fi
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
assert_exit() {
|
|
410
|
+
local desc="$1" expected_exit="$2"
|
|
411
|
+
shift 2
|
|
412
|
+
local actual_exit=0
|
|
413
|
+
"$@" || actual_exit=$?
|
|
414
|
+
TESTS=$((TESTS + 1))
|
|
415
|
+
if [[ "$expected_exit" != "$actual_exit" ]]; then
|
|
416
|
+
echo "FAIL: $desc"
|
|
417
|
+
echo " expected exit: $expected_exit"
|
|
418
|
+
echo " actual exit: $actual_exit"
|
|
419
|
+
FAILURES=$((FAILURES + 1))
|
|
420
|
+
else
|
|
421
|
+
echo "PASS: $desc"
|
|
422
|
+
fi
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
WORK=$(mktemp -d)
|
|
426
|
+
trap "rm -rf '$WORK'" EXIT
|
|
427
|
+
|
|
428
|
+
# === _load_telegram_env tests ===
|
|
429
|
+
|
|
430
|
+
# Missing file
|
|
431
|
+
assert_exit "_load_telegram_env: missing file returns 1" 1 \
|
|
432
|
+
_load_telegram_env "$WORK/nonexistent"
|
|
433
|
+
|
|
434
|
+
# File without keys
|
|
435
|
+
echo "SOME_OTHER_KEY=value" > "$WORK/empty.env"
|
|
436
|
+
assert_exit "_load_telegram_env: missing keys returns 1" 1 \
|
|
437
|
+
_load_telegram_env "$WORK/empty.env"
|
|
438
|
+
|
|
439
|
+
# File with both keys
|
|
440
|
+
cat > "$WORK/valid.env" << 'ENVFILE'
|
|
441
|
+
TELEGRAM_BOT_TOKEN=test-token-123
|
|
442
|
+
TELEGRAM_CHAT_ID=test-chat-456
|
|
443
|
+
ENVFILE
|
|
444
|
+
assert_exit "_load_telegram_env: valid file returns 0" 0 \
|
|
445
|
+
_load_telegram_env "$WORK/valid.env"
|
|
446
|
+
assert_eq "_load_telegram_env: token loaded" "test-token-123" "$TELEGRAM_BOT_TOKEN"
|
|
447
|
+
assert_eq "_load_telegram_env: chat_id loaded" "test-chat-456" "$TELEGRAM_CHAT_ID"
|
|
448
|
+
|
|
449
|
+
# === _send_telegram without credentials ===
|
|
450
|
+
|
|
451
|
+
unset TELEGRAM_BOT_TOKEN TELEGRAM_CHAT_ID
|
|
452
|
+
assert_exit "_send_telegram: no creds returns 0 (skip)" 0 \
|
|
453
|
+
_send_telegram "test message"
|
|
454
|
+
|
|
455
|
+
# === Summary ===
|
|
456
|
+
echo ""
|
|
457
|
+
echo "Results: $((TESTS - FAILURES))/$TESTS passed"
|
|
458
|
+
if [[ $FAILURES -gt 0 ]]; then
|
|
459
|
+
echo "FAILURES: $FAILURES"
|
|
460
|
+
exit 1
|
|
461
|
+
fi
|
|
462
|
+
echo "ALL PASSED"
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
**Step 2: Run test to verify it fails**
|
|
466
|
+
|
|
467
|
+
Run: `bash scripts/tests/test-telegram.sh`
|
|
468
|
+
Expected: FAIL — `telegram.sh` does not exist
|
|
469
|
+
|
|
470
|
+
**Step 3: Write implementation**
|
|
471
|
+
|
|
472
|
+
Create `scripts/lib/telegram.sh` — extract from `run-plan-notify.sh` lines 27-63:
|
|
473
|
+
|
|
474
|
+
```bash
|
|
475
|
+
#!/usr/bin/env bash
|
|
476
|
+
# telegram.sh — Shared Telegram notification helpers
|
|
477
|
+
#
|
|
478
|
+
# Functions:
|
|
479
|
+
# _load_telegram_env [env_file] -> load TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID
|
|
480
|
+
# _send_telegram <message> -> send via Telegram Bot API
|
|
481
|
+
|
|
482
|
+
_load_telegram_env() {
|
|
483
|
+
local env_file="${1:-$HOME/.env}"
|
|
484
|
+
|
|
485
|
+
if [[ ! -f "$env_file" ]]; then
|
|
486
|
+
echo "WARNING: env file not found: $env_file" >&2
|
|
487
|
+
return 1
|
|
488
|
+
fi
|
|
489
|
+
|
|
490
|
+
TELEGRAM_BOT_TOKEN=$(grep -E '^TELEGRAM_BOT_TOKEN=' "$env_file" | head -1 | cut -d= -f2-)
|
|
491
|
+
TELEGRAM_CHAT_ID=$(grep -E '^TELEGRAM_CHAT_ID=' "$env_file" | head -1 | cut -d= -f2-)
|
|
492
|
+
|
|
493
|
+
if [[ -z "${TELEGRAM_BOT_TOKEN:-}" || -z "${TELEGRAM_CHAT_ID:-}" ]]; then
|
|
494
|
+
echo "WARNING: TELEGRAM_BOT_TOKEN or TELEGRAM_CHAT_ID not found in $env_file" >&2
|
|
495
|
+
return 1
|
|
496
|
+
fi
|
|
497
|
+
|
|
498
|
+
export TELEGRAM_BOT_TOKEN TELEGRAM_CHAT_ID
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
_send_telegram() {
|
|
502
|
+
local message="$1"
|
|
503
|
+
|
|
504
|
+
if [[ -z "${TELEGRAM_BOT_TOKEN:-}" || -z "${TELEGRAM_CHAT_ID:-}" ]]; then
|
|
505
|
+
echo "WARNING: Telegram credentials not set — skipping notification" >&2
|
|
506
|
+
return 0
|
|
507
|
+
fi
|
|
508
|
+
|
|
509
|
+
local url="https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage"
|
|
510
|
+
|
|
511
|
+
curl -s -X POST "$url" \
|
|
512
|
+
-d chat_id="$TELEGRAM_CHAT_ID" \
|
|
513
|
+
-d text="$message" \
|
|
514
|
+
-d parse_mode="Markdown" \
|
|
515
|
+
--max-time 10 > /dev/null 2>&1 || {
|
|
516
|
+
echo "WARNING: Failed to send Telegram notification" >&2
|
|
517
|
+
return 0
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
Then update `scripts/lib/run-plan-notify.sh` — replace `_load_telegram_env` and `_send_telegram` with a source line:
|
|
523
|
+
|
|
524
|
+
```bash
|
|
525
|
+
#!/usr/bin/env bash
|
|
526
|
+
# run-plan-notify.sh — Telegram notification helpers for run-plan
|
|
527
|
+
#
|
|
528
|
+
# Functions:
|
|
529
|
+
# format_success_message <plan_name> <batch_num> <test_count> <prev_count> <duration> <mode>
|
|
530
|
+
# format_failure_message <plan_name> <batch_num> <test_count> <failing_count> <error> <action>
|
|
531
|
+
# notify_success (same args as format_success_message) — format + send
|
|
532
|
+
# notify_failure (same args as format_failure_message) — format + send
|
|
533
|
+
|
|
534
|
+
# Source shared telegram functions
|
|
535
|
+
_NOTIFY_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
536
|
+
source "$_NOTIFY_SCRIPT_DIR/telegram.sh"
|
|
537
|
+
|
|
538
|
+
format_success_message() {
|
|
539
|
+
local plan_name="$1" batch_num="$2" test_count="$3" prev_count="$4" duration="$5" mode="$6"
|
|
540
|
+
local delta=$(( test_count - prev_count ))
|
|
541
|
+
|
|
542
|
+
printf '%s — Batch %s ✓\nTests: %s (↑%s)\nDuration: %s\nMode: %s' \
|
|
543
|
+
"$plan_name" "$batch_num" "$test_count" "$delta" "$duration" "$mode"
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
format_failure_message() {
|
|
547
|
+
local plan_name="$1" batch_num="$2" test_count="$3" failing_count="$4" error="$5" action="$6"
|
|
548
|
+
|
|
549
|
+
printf '%s — Batch %s ✗\nTests: %s (%s failing)\nError: %s\nAction: %s' \
|
|
550
|
+
"$plan_name" "$batch_num" "$test_count" "$failing_count" "$error" "$action"
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
notify_success() {
|
|
554
|
+
local msg
|
|
555
|
+
msg=$(format_success_message "$@")
|
|
556
|
+
_send_telegram "$msg"
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
notify_failure() {
|
|
560
|
+
local msg
|
|
561
|
+
msg=$(format_failure_message "$@")
|
|
562
|
+
_send_telegram "$msg"
|
|
563
|
+
}
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
**Step 4: Run tests to verify they pass**
|
|
567
|
+
|
|
568
|
+
Run: `bash scripts/tests/test-telegram.sh && bash scripts/tests/test-run-plan-notify.sh`
|
|
569
|
+
Expected: ALL PASSED (both)
|
|
570
|
+
|
|
571
|
+
**Step 5: Commit**
|
|
572
|
+
|
|
573
|
+
```bash
|
|
574
|
+
git add scripts/lib/telegram.sh scripts/tests/test-telegram.sh scripts/lib/run-plan-notify.sh
|
|
575
|
+
git commit -m "feat: extract scripts/lib/telegram.sh from run-plan-notify.sh"
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
## Batch 2: Refactor Scripts to Use Shared Libraries
|
|
579
|
+
|
|
580
|
+
Refactor all 5 scripts (`auto-compound.sh`, `quality-gate.sh`, `entropy-audit.sh`, `analyze-report.sh`, `run-plan.sh`) to use the shared libraries from Batch 1. No behavior changes — pure deduplication.
|
|
581
|
+
|
|
582
|
+
### Task 4: Refactor quality-gate.sh to use common.sh
|
|
583
|
+
|
|
584
|
+
**Files:**
|
|
585
|
+
- Modify: `scripts/quality-gate.sh:29-45` (arg parsing), `scripts/quality-gate.sh:79-91` (project detection), `scripts/quality-gate.sh:97-107` (memory check)
|
|
586
|
+
|
|
587
|
+
**Step 1: Read current quality-gate.sh**
|
|
588
|
+
|
|
589
|
+
Identify the three sections to replace:
|
|
590
|
+
- Lines 79-91: inline project type detection → `detect_project_type()`
|
|
591
|
+
- Lines 100-106: inline memory check → `check_memory_available()`
|
|
592
|
+
|
|
593
|
+
**Step 2: Add source line and refactor**
|
|
594
|
+
|
|
595
|
+
Add after line 6 (`SCRIPT_DIR=...`):
|
|
596
|
+
```bash
|
|
597
|
+
source "$SCRIPT_DIR/lib/common.sh"
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
Replace lines 79-91 (test suite detection) with:
|
|
601
|
+
```bash
|
|
602
|
+
project_type=$(detect_project_type "$PROJECT_ROOT")
|
|
603
|
+
case "$project_type" in
|
|
604
|
+
python)
|
|
605
|
+
echo "Detected: pytest project"
|
|
606
|
+
.venv/bin/python -m pytest --timeout=120 -x -q
|
|
607
|
+
test_ran=1
|
|
608
|
+
;;
|
|
609
|
+
node)
|
|
610
|
+
if grep -q '"test"' "$PROJECT_ROOT/package.json" 2>/dev/null; then
|
|
611
|
+
echo "Detected: npm project"
|
|
612
|
+
npm test
|
|
613
|
+
test_ran=1
|
|
614
|
+
fi
|
|
615
|
+
;;
|
|
616
|
+
make)
|
|
617
|
+
if grep -q '^test:' "$PROJECT_ROOT/Makefile" 2>/dev/null; then
|
|
618
|
+
echo "Detected: Makefile project"
|
|
619
|
+
make test
|
|
620
|
+
test_ran=1
|
|
621
|
+
fi
|
|
622
|
+
;;
|
|
623
|
+
esac
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
Replace lines 100-106 (memory check) with:
|
|
627
|
+
```bash
|
|
628
|
+
if check_memory_available 4; then
|
|
629
|
+
available_gb=$(free -g | awk '/Mem:/{print $7}')
|
|
630
|
+
echo "Memory OK (${available_gb}G available)"
|
|
631
|
+
else
|
|
632
|
+
echo "WARNING: Consider -n 0 for pytest"
|
|
633
|
+
fi
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
**Step 3: Run quality gate test (existing tests must still pass)**
|
|
637
|
+
|
|
638
|
+
Run: `bash scripts/tests/run-all-tests.sh`
|
|
639
|
+
Expected: ALL PASSED
|
|
640
|
+
|
|
641
|
+
**Step 4: Verify line count**
|
|
642
|
+
|
|
643
|
+
Run: `wc -l scripts/quality-gate.sh`
|
|
644
|
+
Expected: Under 300 lines
|
|
645
|
+
|
|
646
|
+
**Step 5: Commit**
|
|
647
|
+
|
|
648
|
+
```bash
|
|
649
|
+
git add scripts/quality-gate.sh
|
|
650
|
+
git commit -m "refactor: quality-gate.sh uses common.sh for project detection and memory check"
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
### Task 5: Refactor auto-compound.sh to use common.sh and ollama.sh
|
|
654
|
+
|
|
655
|
+
**Files:**
|
|
656
|
+
- Modify: `scripts/auto-compound.sh:17` (add source lines), `scripts/auto-compound.sh:127` (fix PRD discard), `scripts/auto-compound.sh:144-163` (replace inline project detection)
|
|
657
|
+
|
|
658
|
+
**Step 1: Read and identify sections**
|
|
659
|
+
|
|
660
|
+
Three changes:
|
|
661
|
+
1. Add source lines after `SCRIPT_DIR` (line 17)
|
|
662
|
+
2. Fix line 127: `> /dev/null 2>&1 || true` discards PRD output — capture it and log errors (lesson-7 fix)
|
|
663
|
+
3. Replace lines 144-163 (fallback quality checks detection) with `detect_project_type()`
|
|
664
|
+
|
|
665
|
+
**Step 2: Apply changes**
|
|
666
|
+
|
|
667
|
+
After line 17, add:
|
|
668
|
+
```bash
|
|
669
|
+
source "$SCRIPT_DIR/lib/common.sh"
|
|
670
|
+
source "$SCRIPT_DIR/lib/ollama.sh"
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
Replace line 127:
|
|
674
|
+
```bash
|
|
675
|
+
# OLD: claude --print "/create-prd $PRIORITY. Context from analysis: $(cat analysis.json)" > /dev/null 2>&1 || true
|
|
676
|
+
prd_output=$(claude --print "/create-prd $PRIORITY. Context from analysis: $(cat analysis.json)" 2>&1) || {
|
|
677
|
+
echo "WARNING: PRD generation failed:" >&2
|
|
678
|
+
echo "$prd_output" | tail -10 >&2
|
|
679
|
+
}
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
Replace lines 144-163 (fallback detection) with:
|
|
683
|
+
```bash
|
|
684
|
+
local project_type
|
|
685
|
+
project_type=$(detect_project_type "$PROJECT_DIR")
|
|
686
|
+
case "$project_type" in
|
|
687
|
+
python) QUALITY_CHECKS="pytest --timeout=120 -x -q" ;;
|
|
688
|
+
node)
|
|
689
|
+
QUALITY_CHECKS=""
|
|
690
|
+
grep -q '"test"' package.json 2>/dev/null && QUALITY_CHECKS+="npm test"
|
|
691
|
+
grep -q '"lint"' package.json 2>/dev/null && { [[ -n "$QUALITY_CHECKS" ]] && QUALITY_CHECKS+=";"; QUALITY_CHECKS+="npm run lint"; }
|
|
692
|
+
;;
|
|
693
|
+
make) QUALITY_CHECKS="make test" ;;
|
|
694
|
+
*) QUALITY_CHECKS="" ;;
|
|
695
|
+
esac
|
|
696
|
+
echo " Fallback mode — quality-gate.sh not found"
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
**Step 3: Verify no test regressions**
|
|
700
|
+
|
|
701
|
+
Run: `bash scripts/tests/run-all-tests.sh`
|
|
702
|
+
Expected: ALL PASSED
|
|
703
|
+
|
|
704
|
+
**Step 4: Verify line count**
|
|
705
|
+
|
|
706
|
+
Run: `wc -l scripts/auto-compound.sh`
|
|
707
|
+
Expected: Under 300 lines (was 230, should be ~220 now)
|
|
708
|
+
|
|
709
|
+
**Step 5: Commit**
|
|
710
|
+
|
|
711
|
+
```bash
|
|
712
|
+
git add scripts/auto-compound.sh
|
|
713
|
+
git commit -m "refactor: auto-compound.sh uses common.sh, fixes PRD output discard (lesson-7)"
|
|
714
|
+
```
|
|
715
|
+
|
|
716
|
+
### Task 6: Refactor analyze-report.sh to use ollama.sh
|
|
717
|
+
|
|
718
|
+
**Files:**
|
|
719
|
+
- Modify: `scripts/analyze-report.sh:75-111` (replace inline Ollama call and JSON stripping)
|
|
720
|
+
|
|
721
|
+
**Step 1: Identify replacement sections**
|
|
722
|
+
|
|
723
|
+
Lines 75-88: Ollama API call logic → `ollama_query()`
|
|
724
|
+
Lines 100-111: JSON fence stripping → `strip_json_fences` + `ollama_extract_json()`
|
|
725
|
+
|
|
726
|
+
**Step 2: Apply changes**
|
|
727
|
+
|
|
728
|
+
Add after the `set -euo pipefail` line:
|
|
729
|
+
```bash
|
|
730
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
731
|
+
source "$SCRIPT_DIR/lib/common.sh"
|
|
732
|
+
source "$SCRIPT_DIR/lib/ollama.sh"
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
Replace lines 75-111 with:
|
|
736
|
+
```bash
|
|
737
|
+
# Query Ollama
|
|
738
|
+
ANALYSIS=$(ollama_query "$MODEL" "$PROMPT")
|
|
739
|
+
|
|
740
|
+
if [[ -z "$ANALYSIS" ]]; then
|
|
741
|
+
echo "Error: Empty response from Ollama" >&2
|
|
742
|
+
exit 1
|
|
743
|
+
fi
|
|
744
|
+
|
|
745
|
+
# Parse as JSON
|
|
746
|
+
CLEANED=$(echo "$ANALYSIS" | ollama_extract_json)
|
|
747
|
+
if [[ -n "$CLEANED" ]]; then
|
|
748
|
+
echo "$CLEANED" | jq . > "$OUTPUT_DIR/analysis.json"
|
|
749
|
+
else
|
|
750
|
+
echo "Warning: Could not parse LLM response as JSON, saving raw" >&2
|
|
751
|
+
echo "{\"raw_response\": $(echo "$ANALYSIS" | jq -Rs .)}" > "$OUTPUT_DIR/analysis.json"
|
|
752
|
+
fi
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+
**Step 3: Run tests**
|
|
756
|
+
|
|
757
|
+
Run: `bash scripts/tests/run-all-tests.sh`
|
|
758
|
+
Expected: ALL PASSED
|
|
759
|
+
|
|
760
|
+
**Step 4: Verify line count**
|
|
761
|
+
|
|
762
|
+
Run: `wc -l scripts/analyze-report.sh`
|
|
763
|
+
Expected: Under 100 lines (was 114, trimmed ~30 lines of Ollama logic)
|
|
764
|
+
|
|
765
|
+
**Step 5: Commit**
|
|
766
|
+
|
|
767
|
+
```bash
|
|
768
|
+
git add scripts/analyze-report.sh
|
|
769
|
+
git commit -m "refactor: analyze-report.sh uses ollama.sh shared library"
|
|
770
|
+
```
|
|
771
|
+
|
|
772
|
+
### Task 7: Refactor entropy-audit.sh — remove hardcoded path
|
|
773
|
+
|
|
774
|
+
**Files:**
|
|
775
|
+
- Modify: `scripts/entropy-audit.sh:17` (replace hardcoded path with env var + arg)
|
|
776
|
+
|
|
777
|
+
**Step 1: Identify the fix**
|
|
778
|
+
|
|
779
|
+
Line 17: `PROJECTS_DIR="$HOME/Documents/projects"` is hardcoded. Replace with `--projects-dir` arg that defaults to `PROJECTS_DIR` env var, then `$HOME/Documents/projects`.
|
|
780
|
+
|
|
781
|
+
**Step 2: Apply changes**
|
|
782
|
+
|
|
783
|
+
Add after `set -euo pipefail`:
|
|
784
|
+
```bash
|
|
785
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
786
|
+
source "$SCRIPT_DIR/lib/common.sh"
|
|
787
|
+
```
|
|
788
|
+
|
|
789
|
+
Replace line 17:
|
|
790
|
+
```bash
|
|
791
|
+
PROJECTS_DIR="${PROJECTS_DIR:-$HOME/Documents/projects}"
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
Add `--projects-dir` to the arg parser (after `--fix` case):
|
|
795
|
+
```bash
|
|
796
|
+
--projects-dir) PROJECTS_DIR="$2"; shift 2 ;;
|
|
797
|
+
```
|
|
798
|
+
|
|
799
|
+
**Step 3: Run tests**
|
|
800
|
+
|
|
801
|
+
Run: `bash scripts/tests/run-all-tests.sh`
|
|
802
|
+
Expected: ALL PASSED
|
|
803
|
+
|
|
804
|
+
**Step 4: Commit**
|
|
805
|
+
|
|
806
|
+
```bash
|
|
807
|
+
git add scripts/entropy-audit.sh
|
|
808
|
+
git commit -m "refactor: entropy-audit.sh uses env var/arg instead of hardcoded projects path"
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
### Task 8: Extract run-plan-headless.sh from run-plan.sh
|
|
812
|
+
|
|
813
|
+
**Files:**
|
|
814
|
+
- Create: `scripts/lib/run-plan-headless.sh`
|
|
815
|
+
- Modify: `scripts/run-plan.sh:229-376` (replace `run_mode_headless()` with source + call)
|
|
816
|
+
|
|
817
|
+
**Step 1: Extract `run_mode_headless()` from run-plan.sh**
|
|
818
|
+
|
|
819
|
+
Move lines 229-376 of `scripts/run-plan.sh` (the entire `run_mode_headless()` function) into `scripts/lib/run-plan-headless.sh`. The function reads these globals: `WORKTREE`, `RESUME`, `START_BATCH`, `END_BATCH`, `NOTIFY`, `PLAN_FILE`, `QUALITY_GATE_CMD`, `PYTHON`, `MAX_RETRIES`, `ON_FAILURE`, `VERIFY`, `MODE`. These are all set before the function is called.
|
|
820
|
+
|
|
821
|
+
**Step 2: Create `scripts/lib/run-plan-headless.sh`**
|
|
822
|
+
|
|
823
|
+
```bash
|
|
824
|
+
#!/usr/bin/env bash
|
|
825
|
+
# run-plan-headless.sh — Headless batch execution loop for run-plan
|
|
826
|
+
#
|
|
827
|
+
# Extracted from run-plan.sh to keep the main script under 300 lines.
|
|
828
|
+
#
|
|
829
|
+
# Requires these globals set before calling:
|
|
830
|
+
# WORKTREE, RESUME, START_BATCH, END_BATCH, NOTIFY, PLAN_FILE,
|
|
831
|
+
# QUALITY_GATE_CMD, PYTHON, MAX_RETRIES, ON_FAILURE, VERIFY, MODE
|
|
832
|
+
#
|
|
833
|
+
# Requires these libs sourced:
|
|
834
|
+
# run-plan-parser.sh, run-plan-state.sh, run-plan-quality-gate.sh,
|
|
835
|
+
# run-plan-notify.sh, run-plan-prompt.sh
|
|
836
|
+
|
|
837
|
+
run_mode_headless() {
|
|
838
|
+
# (paste the entire function body from run-plan.sh lines 230-376 here verbatim)
|
|
839
|
+
}
|
|
840
|
+
```
|
|
841
|
+
|
|
842
|
+
Copy the function body exactly from lines 230-376. Do not modify any logic.
|
|
843
|
+
|
|
844
|
+
**Step 3: Update run-plan.sh**
|
|
845
|
+
|
|
846
|
+
Add source line after the other source statements (line 21):
|
|
847
|
+
```bash
|
|
848
|
+
source "$SCRIPT_DIR/lib/run-plan-headless.sh"
|
|
849
|
+
```
|
|
850
|
+
|
|
851
|
+
Remove lines 229-376 (the old `run_mode_headless` function).
|
|
852
|
+
|
|
853
|
+
**Step 4: Run all tests**
|
|
854
|
+
|
|
855
|
+
Run: `bash scripts/tests/run-all-tests.sh`
|
|
856
|
+
Expected: ALL PASSED (existing tests exercise run-plan.sh and should still work)
|
|
857
|
+
|
|
858
|
+
**Step 5: Verify line counts**
|
|
859
|
+
|
|
860
|
+
Run: `wc -l scripts/run-plan.sh scripts/lib/run-plan-headless.sh`
|
|
861
|
+
Expected: `run-plan.sh` ~260 lines (under 300), `run-plan-headless.sh` ~150 lines
|
|
862
|
+
|
|
863
|
+
**Step 6: Commit**
|
|
864
|
+
|
|
865
|
+
```bash
|
|
866
|
+
git add scripts/lib/run-plan-headless.sh scripts/run-plan.sh
|
|
867
|
+
git commit -m "refactor: extract run_mode_headless() into scripts/lib/run-plan-headless.sh"
|
|
868
|
+
```
|
|
869
|
+
|
|
870
|
+
## Batch 3: Accuracy Fixes — Test Count Parsing and Cross-Batch Context
|
|
871
|
+
|
|
872
|
+
Fix the two most impactful accuracy bugs: test count parsing that only works for pytest, and missing cross-batch context that causes agents to repeat work.
|
|
873
|
+
|
|
874
|
+
### Task 9: Fix test count parsing for multiple test frameworks
|
|
875
|
+
|
|
876
|
+
**Files:**
|
|
877
|
+
- Modify: `scripts/lib/run-plan-quality-gate.sh:19-29` (replace `extract_test_count`)
|
|
878
|
+
- Modify: `scripts/tests/test-run-plan-quality-gate.sh` (add new test cases)
|
|
879
|
+
|
|
880
|
+
**Step 1: Add failing tests for jest, go, and unknown formats**
|
|
881
|
+
|
|
882
|
+
Add to `scripts/tests/test-run-plan-quality-gate.sh` after the existing `extract_test_count` tests (before the `check_test_count_regression` section):
|
|
883
|
+
|
|
884
|
+
```bash
|
|
885
|
+
# --- Test: extract from jest output ---
|
|
886
|
+
output="Tests: 3 failed, 45 passed, 48 total"
|
|
887
|
+
val=$(extract_test_count "$output")
|
|
888
|
+
assert_eq "extract_test_count: jest output" "45" "$val"
|
|
889
|
+
|
|
890
|
+
# --- Test: extract from jest all-pass output ---
|
|
891
|
+
output="Tests: 12 passed, 12 total"
|
|
892
|
+
val=$(extract_test_count "$output")
|
|
893
|
+
assert_eq "extract_test_count: jest all-pass" "12" "$val"
|
|
894
|
+
|
|
895
|
+
# --- Test: extract from go test output ---
|
|
896
|
+
output="ok github.com/foo/bar 0.123s
|
|
897
|
+
ok github.com/foo/baz 0.456s
|
|
898
|
+
FAIL github.com/foo/qux 0.789s"
|
|
899
|
+
val=$(extract_test_count "$output")
|
|
900
|
+
assert_eq "extract_test_count: go test (2 ok of 3)" "2" "$val"
|
|
901
|
+
|
|
902
|
+
# --- Test: unrecognized format returns -1 ---
|
|
903
|
+
val=$(extract_test_count "Some random build output with no test results")
|
|
904
|
+
assert_eq "extract_test_count: unrecognized format" "-1" "$val"
|
|
905
|
+
```
|
|
906
|
+
|
|
907
|
+
**Step 2: Run test to verify failures**
|
|
908
|
+
|
|
909
|
+
Run: `bash scripts/tests/test-run-plan-quality-gate.sh`
|
|
910
|
+
Expected: FAIL on jest, go, and unrecognized format tests
|
|
911
|
+
|
|
912
|
+
**Step 3: Update extract_test_count implementation**
|
|
913
|
+
|
|
914
|
+
Replace `extract_test_count()` in `scripts/lib/run-plan-quality-gate.sh`:
|
|
915
|
+
|
|
916
|
+
```bash
|
|
917
|
+
extract_test_count() {
|
|
918
|
+
local output="$1"
|
|
919
|
+
local count
|
|
920
|
+
|
|
921
|
+
# 1. pytest: "N passed" (e.g., "85 passed" in "3 failed, 85 passed, 2 skipped in 30.1s")
|
|
922
|
+
count=$(echo "$output" | grep -oP '\b(\d+) passed\b' | tail -1 | grep -oP '^\d+' || true)
|
|
923
|
+
if [[ -n "$count" ]]; then
|
|
924
|
+
echo "$count"
|
|
925
|
+
return
|
|
926
|
+
fi
|
|
927
|
+
|
|
928
|
+
# 2. jest: "Tests: N passed" (e.g., "Tests: 45 passed, 48 total")
|
|
929
|
+
count=$(echo "$output" | grep -oP 'Tests:\s+(\d+ failed, )?\K\d+(?= passed)' || true)
|
|
930
|
+
if [[ -n "$count" ]]; then
|
|
931
|
+
echo "$count"
|
|
932
|
+
return
|
|
933
|
+
fi
|
|
934
|
+
|
|
935
|
+
# 3. go test: count "ok" lines (each = one passing package)
|
|
936
|
+
count=$(echo "$output" | grep -c '^ok' || true)
|
|
937
|
+
if [[ "$count" -gt 0 ]]; then
|
|
938
|
+
echo "$count"
|
|
939
|
+
return
|
|
940
|
+
fi
|
|
941
|
+
|
|
942
|
+
# 4. No recognized format — return -1 to signal "skip regression check"
|
|
943
|
+
echo "-1"
|
|
944
|
+
}
|
|
945
|
+
```
|
|
946
|
+
|
|
947
|
+
Also update `check_test_count_regression()` to handle `-1`:
|
|
948
|
+
```bash
|
|
949
|
+
check_test_count_regression() {
|
|
950
|
+
local new_count="$1" previous_count="$2"
|
|
951
|
+
# -1 means unrecognized format — skip regression check
|
|
952
|
+
if [[ "$new_count" == "-1" || "$previous_count" == "-1" ]]; then
|
|
953
|
+
echo "INFO: Skipping test count regression check (unrecognized test format)" >&2
|
|
954
|
+
return 0
|
|
955
|
+
fi
|
|
956
|
+
if [[ "$new_count" -ge "$previous_count" ]]; then
|
|
957
|
+
return 0
|
|
958
|
+
else
|
|
959
|
+
echo "WARNING: Test count regression: $new_count < $previous_count (previous)" >&2
|
|
960
|
+
return 1
|
|
961
|
+
fi
|
|
962
|
+
}
|
|
963
|
+
```
|
|
964
|
+
|
|
965
|
+
**Step 4: Run tests**
|
|
966
|
+
|
|
967
|
+
Run: `bash scripts/tests/test-run-plan-quality-gate.sh`
|
|
968
|
+
Expected: ALL PASSED
|
|
969
|
+
|
|
970
|
+
Also add a regression test for `check_test_count_regression` with -1:
|
|
971
|
+
```bash
|
|
972
|
+
# --- Test: -1 skips regression check ---
|
|
973
|
+
assert_exit "check_test_count_regression: -1 new skips check" 0 \
|
|
974
|
+
check_test_count_regression -1 150
|
|
975
|
+
|
|
976
|
+
assert_exit "check_test_count_regression: -1 previous skips check" 0 \
|
|
977
|
+
check_test_count_regression 50 -1
|
|
978
|
+
```
|
|
979
|
+
|
|
980
|
+
**Step 5: Commit**
|
|
981
|
+
|
|
982
|
+
```bash
|
|
983
|
+
git add scripts/lib/run-plan-quality-gate.sh scripts/tests/test-run-plan-quality-gate.sh
|
|
984
|
+
git commit -m "fix: test count parsing supports jest, go test, and returns -1 for unknown formats"
|
|
985
|
+
```
|
|
986
|
+
|
|
987
|
+
### Task 10: Add cross-batch context to prompts
|
|
988
|
+
|
|
989
|
+
**Files:**
|
|
990
|
+
- Modify: `scripts/lib/run-plan-prompt.sh` (add git log, progress.txt, quality gate context)
|
|
991
|
+
- Modify: `scripts/tests/test-run-plan-prompt.sh` (add assertions for new context)
|
|
992
|
+
|
|
993
|
+
**Step 1: Read existing prompt test**
|
|
994
|
+
|
|
995
|
+
Read `scripts/tests/test-run-plan-prompt.sh` to understand current test structure.
|
|
996
|
+
|
|
997
|
+
**Step 2: Add failing test**
|
|
998
|
+
|
|
999
|
+
Add assertion that the prompt output contains cross-batch context markers:
|
|
1000
|
+
|
|
1001
|
+
```bash
|
|
1002
|
+
# --- Test: prompt includes cross-batch context ---
|
|
1003
|
+
# Create a state file with previous quality gate
|
|
1004
|
+
echo '{"last_quality_gate":{"batch":1,"passed":true,"test_count":42}}' > "$WORK/.run-plan-state.json"
|
|
1005
|
+
# Create progress.txt
|
|
1006
|
+
echo "Batch 1: Implemented auth module" > "$WORK/progress.txt"
|
|
1007
|
+
# Create a git commit in the work dir
|
|
1008
|
+
echo "file" > "$WORK/code.py"
|
|
1009
|
+
git -C "$WORK" add code.py && git -C "$WORK" commit -q -m "feat: add auth"
|
|
1010
|
+
|
|
1011
|
+
prompt=$(build_batch_prompt "$PLAN" 2 "$WORK" "python3" "scripts/quality-gate.sh" 42)
|
|
1012
|
+
echo "$prompt" | grep -q "Recent commits" || {
|
|
1013
|
+
echo "FAIL: prompt missing 'Recent commits' section"
|
|
1014
|
+
FAILURES=$((FAILURES + 1))
|
|
1015
|
+
}
|
|
1016
|
+
TESTS=$((TESTS + 1))
|
|
1017
|
+
echo "$prompt" | grep -q "progress.txt" || echo "$prompt" | grep -q "Previous progress" || {
|
|
1018
|
+
echo "FAIL: prompt missing progress.txt context"
|
|
1019
|
+
FAILURES=$((FAILURES + 1))
|
|
1020
|
+
}
|
|
1021
|
+
TESTS=$((TESTS + 1))
|
|
1022
|
+
```
|
|
1023
|
+
|
|
1024
|
+
**Step 3: Run test to verify failure**
|
|
1025
|
+
|
|
1026
|
+
Run: `bash scripts/tests/test-run-plan-prompt.sh`
|
|
1027
|
+
Expected: FAIL on cross-batch context assertions
|
|
1028
|
+
|
|
1029
|
+
**Step 4: Update build_batch_prompt**
|
|
1030
|
+
|
|
1031
|
+
Replace `build_batch_prompt()` in `scripts/lib/run-plan-prompt.sh`:
|
|
1032
|
+
|
|
1033
|
+
```bash
|
|
1034
|
+
build_batch_prompt() {
|
|
1035
|
+
local plan_file="$1"
|
|
1036
|
+
local batch_num="$2"
|
|
1037
|
+
local worktree="$3"
|
|
1038
|
+
local python="$4"
|
|
1039
|
+
local quality_gate_cmd="$5"
|
|
1040
|
+
local prev_test_count="$6"
|
|
1041
|
+
|
|
1042
|
+
local title branch batch_text recent_commits progress_tail prev_gate
|
|
1043
|
+
|
|
1044
|
+
title=$(get_batch_title "$plan_file" "$batch_num")
|
|
1045
|
+
branch=$(git -C "$worktree" branch --show-current 2>/dev/null || echo "unknown")
|
|
1046
|
+
batch_text=$(get_batch_text "$plan_file" "$batch_num")
|
|
1047
|
+
|
|
1048
|
+
# Cross-batch context: recent commits
|
|
1049
|
+
recent_commits=$(git -C "$worktree" log --oneline -5 2>/dev/null || echo "(no commits)")
|
|
1050
|
+
|
|
1051
|
+
# Cross-batch context: progress.txt tail
|
|
1052
|
+
progress_tail=""
|
|
1053
|
+
if [[ -f "$worktree/progress.txt" ]]; then
|
|
1054
|
+
progress_tail=$(tail -20 "$worktree/progress.txt" 2>/dev/null || true)
|
|
1055
|
+
fi
|
|
1056
|
+
|
|
1057
|
+
# Cross-batch context: previous quality gate result
|
|
1058
|
+
prev_gate=""
|
|
1059
|
+
if [[ -f "$worktree/.run-plan-state.json" ]]; then
|
|
1060
|
+
prev_gate=$(jq -r '.last_quality_gate // empty' "$worktree/.run-plan-state.json" 2>/dev/null || true)
|
|
1061
|
+
fi
|
|
1062
|
+
|
|
1063
|
+
cat <<PROMPT
|
|
1064
|
+
You are implementing Batch ${batch_num}: ${title} from ${plan_file}.
|
|
1065
|
+
|
|
1066
|
+
Working directory: ${worktree}
|
|
1067
|
+
Python: ${python}
|
|
1068
|
+
Branch: ${branch}
|
|
1069
|
+
|
|
1070
|
+
Tasks in this batch:
|
|
1071
|
+
${batch_text}
|
|
1072
|
+
|
|
1073
|
+
Recent commits:
|
|
1074
|
+
${recent_commits}
|
|
1075
|
+
$(if [[ -n "$progress_tail" ]]; then
|
|
1076
|
+
echo "
|
|
1077
|
+
Previous progress:
|
|
1078
|
+
${progress_tail}"
|
|
1079
|
+
fi)
|
|
1080
|
+
$(if [[ -n "$prev_gate" && "$prev_gate" != "null" ]]; then
|
|
1081
|
+
echo "
|
|
1082
|
+
Previous quality gate: ${prev_gate}"
|
|
1083
|
+
fi)
|
|
1084
|
+
|
|
1085
|
+
Requirements:
|
|
1086
|
+
- TDD: write test -> verify fail -> implement -> verify pass -> commit each task
|
|
1087
|
+
- After all tasks: run quality gate (${quality_gate_cmd})
|
|
1088
|
+
- Update progress.txt with batch summary and commit
|
|
1089
|
+
- All ${prev_test_count}+ tests must pass
|
|
1090
|
+
PROMPT
|
|
1091
|
+
}
|
|
1092
|
+
```
|
|
1093
|
+
|
|
1094
|
+
**Step 5: Run tests**
|
|
1095
|
+
|
|
1096
|
+
Run: `bash scripts/tests/test-run-plan-prompt.sh`
|
|
1097
|
+
Expected: ALL PASSED
|
|
1098
|
+
|
|
1099
|
+
**Step 6: Commit**
|
|
1100
|
+
|
|
1101
|
+
```bash
|
|
1102
|
+
git add scripts/lib/run-plan-prompt.sh scripts/tests/test-run-plan-prompt.sh
|
|
1103
|
+
git commit -m "feat: add cross-batch context (git log, progress.txt, gate result) to prompts"
|
|
1104
|
+
```
|
|
1105
|
+
|
|
1106
|
+
### Task 11: Add duration tracking to state
|
|
1107
|
+
|
|
1108
|
+
**Files:**
|
|
1109
|
+
- Modify: `scripts/lib/run-plan-state.sh` (add `duration_seconds` field to `complete_batch`)
|
|
1110
|
+
- Modify: `scripts/lib/run-plan-headless.sh` (pass duration to `complete_batch`)
|
|
1111
|
+
- Modify: `scripts/tests/test-run-plan-state.sh` (add duration assertions)
|
|
1112
|
+
|
|
1113
|
+
**Step 1: Add failing test**
|
|
1114
|
+
|
|
1115
|
+
Add to `scripts/tests/test-run-plan-state.sh`:
|
|
1116
|
+
|
|
1117
|
+
```bash
|
|
1118
|
+
# --- Test: complete_batch stores duration ---
|
|
1119
|
+
complete_batch "$WORK" 1 42 120
|
|
1120
|
+
duration=$(jq -r '.durations["1"]' "$WORK/.run-plan-state.json")
|
|
1121
|
+
assert_eq "complete_batch: stores duration" "120" "$duration"
|
|
1122
|
+
```
|
|
1123
|
+
|
|
1124
|
+
**Step 2: Run test to verify failure**
|
|
1125
|
+
|
|
1126
|
+
Run: `bash scripts/tests/test-run-plan-state.sh`
|
|
1127
|
+
Expected: FAIL — `complete_batch` only takes 3 args currently
|
|
1128
|
+
|
|
1129
|
+
**Step 3: Update complete_batch to accept optional duration**
|
|
1130
|
+
|
|
1131
|
+
In `scripts/lib/run-plan-state.sh`, update `complete_batch()`:
|
|
1132
|
+
|
|
1133
|
+
```bash
|
|
1134
|
+
complete_batch() {
|
|
1135
|
+
local worktree="$1" batch_num="$2" test_count="$3" duration="${4:-0}"
|
|
1136
|
+
local sf tmp
|
|
1137
|
+
sf=$(_state_file "$worktree")
|
|
1138
|
+
tmp=$(mktemp)
|
|
1139
|
+
|
|
1140
|
+
jq \
|
|
1141
|
+
--argjson batch "$batch_num" \
|
|
1142
|
+
--argjson tc "$test_count" \
|
|
1143
|
+
--argjson dur "$duration" \
|
|
1144
|
+
'
|
|
1145
|
+
.completed_batches += [$batch] |
|
|
1146
|
+
.current_batch = ($batch + 1) |
|
|
1147
|
+
.test_counts[($batch | tostring)] = $tc |
|
|
1148
|
+
.durations[($batch | tostring)] = $dur
|
|
1149
|
+
' "$sf" > "$tmp" && mv "$tmp" "$sf"
|
|
1150
|
+
}
|
|
1151
|
+
```
|
|
1152
|
+
|
|
1153
|
+
Update `init_state()` to include `durations: {}`:
|
|
1154
|
+
```bash
|
|
1155
|
+
jq -n \
|
|
1156
|
+
--arg plan_file "$plan_file" \
|
|
1157
|
+
--arg mode "$mode" \
|
|
1158
|
+
--arg started_at "$now" \
|
|
1159
|
+
'{
|
|
1160
|
+
plan_file: $plan_file,
|
|
1161
|
+
mode: $mode,
|
|
1162
|
+
current_batch: 1,
|
|
1163
|
+
completed_batches: [],
|
|
1164
|
+
test_counts: {},
|
|
1165
|
+
durations: {},
|
|
1166
|
+
started_at: $started_at,
|
|
1167
|
+
last_quality_gate: null
|
|
1168
|
+
}' > "$sf"
|
|
1169
|
+
```
|
|
1170
|
+
|
|
1171
|
+
Update `run_mode_headless()` in `run-plan-headless.sh` — pass duration to `complete_batch`. Find the line `complete_batch "$WORKTREE" "$batch_num" "$test_count"` inside `run_quality_gate` and also ensure the headless loop passes duration. The duration is already computed as `$duration` variable.
|
|
1172
|
+
|
|
1173
|
+
**Step 4: Run tests**
|
|
1174
|
+
|
|
1175
|
+
Run: `bash scripts/tests/test-run-plan-state.sh && bash scripts/tests/run-all-tests.sh`
|
|
1176
|
+
Expected: ALL PASSED
|
|
1177
|
+
|
|
1178
|
+
**Step 5: Commit**
|
|
1179
|
+
|
|
1180
|
+
```bash
|
|
1181
|
+
git add scripts/lib/run-plan-state.sh scripts/lib/run-plan-headless.sh scripts/tests/test-run-plan-state.sh
|
|
1182
|
+
git commit -m "feat: add per-batch duration tracking to run-plan state"
|
|
1183
|
+
```
|
|
1184
|
+
|
|
1185
|
+
## Batch 4: Quality Gate Expansion
|
|
1186
|
+
|
|
1187
|
+
Add lint checking and prior-art search to the quality gate pipeline.
|
|
1188
|
+
|
|
1189
|
+
### Task 12: Add ruff lint step to quality-gate.sh
|
|
1190
|
+
|
|
1191
|
+
**Files:**
|
|
1192
|
+
- Modify: `scripts/quality-gate.sh` (add lint check between lesson-check and test suite)
|
|
1193
|
+
|
|
1194
|
+
**Step 1: Verify ruff is installed**
|
|
1195
|
+
|
|
1196
|
+
Run: `ruff --version`
|
|
1197
|
+
If not installed: `pip install ruff` (or `brew install ruff`)
|
|
1198
|
+
|
|
1199
|
+
**Step 2: Add lint check to quality-gate.sh**
|
|
1200
|
+
|
|
1201
|
+
Insert after the lesson check section (after the `fi` on line ~72), before the test suite section:
|
|
1202
|
+
|
|
1203
|
+
```bash
|
|
1204
|
+
# === Check 2: Lint (ruff for Python, eslint for Node) ===
|
|
1205
|
+
echo ""
|
|
1206
|
+
echo "=== Quality Gate: Lint Check ==="
|
|
1207
|
+
project_type=$(detect_project_type "$PROJECT_ROOT")
|
|
1208
|
+
lint_ran=0
|
|
1209
|
+
|
|
1210
|
+
case "$project_type" in
|
|
1211
|
+
python)
|
|
1212
|
+
if command -v ruff >/dev/null 2>&1; then
|
|
1213
|
+
echo "Running: ruff check --select E,W,F"
|
|
1214
|
+
if ! ruff check --select E,W,F "$PROJECT_ROOT"; then
|
|
1215
|
+
echo ""
|
|
1216
|
+
echo "quality-gate: FAILED at lint check"
|
|
1217
|
+
exit 1
|
|
1218
|
+
fi
|
|
1219
|
+
lint_ran=1
|
|
1220
|
+
else
|
|
1221
|
+
echo "ruff not installed — skipping Python lint"
|
|
1222
|
+
fi
|
|
1223
|
+
;;
|
|
1224
|
+
node)
|
|
1225
|
+
if [[ -f "$PROJECT_ROOT/.eslintrc" || -f "$PROJECT_ROOT/.eslintrc.js" || -f "$PROJECT_ROOT/.eslintrc.json" || -f "$PROJECT_ROOT/eslint.config.js" ]]; then
|
|
1226
|
+
echo "Running: npx eslint"
|
|
1227
|
+
if ! npx eslint "$PROJECT_ROOT" 2>/dev/null; then
|
|
1228
|
+
echo ""
|
|
1229
|
+
echo "quality-gate: FAILED at lint check"
|
|
1230
|
+
exit 1
|
|
1231
|
+
fi
|
|
1232
|
+
lint_ran=1
|
|
1233
|
+
else
|
|
1234
|
+
echo "No eslint config found — skipping Node lint"
|
|
1235
|
+
fi
|
|
1236
|
+
;;
|
|
1237
|
+
esac
|
|
1238
|
+
|
|
1239
|
+
if [[ $lint_ran -eq 0 ]]; then
|
|
1240
|
+
echo "No linter configured — skipped"
|
|
1241
|
+
fi
|
|
1242
|
+
```
|
|
1243
|
+
|
|
1244
|
+
Renumber the subsequent checks (test suite becomes Check 3, memory becomes Check 4).
|
|
1245
|
+
|
|
1246
|
+
**Step 3: Run tests**
|
|
1247
|
+
|
|
1248
|
+
Run: `bash scripts/tests/run-all-tests.sh`
|
|
1249
|
+
Expected: ALL PASSED
|
|
1250
|
+
|
|
1251
|
+
**Step 4: Verify line count**
|
|
1252
|
+
|
|
1253
|
+
Run: `wc -l scripts/quality-gate.sh`
|
|
1254
|
+
Expected: Under 300 lines (~145 now)
|
|
1255
|
+
|
|
1256
|
+
**Step 5: Commit**
|
|
1257
|
+
|
|
1258
|
+
```bash
|
|
1259
|
+
git add scripts/quality-gate.sh
|
|
1260
|
+
git commit -m "feat: add ruff/eslint lint step to quality-gate.sh"
|
|
1261
|
+
```
|
|
1262
|
+
|
|
1263
|
+
### Task 13: Create prior-art-search.sh
|
|
1264
|
+
|
|
1265
|
+
**Files:**
|
|
1266
|
+
- Create: `scripts/prior-art-search.sh`
|
|
1267
|
+
- Create: `scripts/tests/test-prior-art-search.sh`
|
|
1268
|
+
|
|
1269
|
+
**Step 1: Write the failing test**
|
|
1270
|
+
|
|
1271
|
+
```bash
|
|
1272
|
+
#!/usr/bin/env bash
|
|
1273
|
+
# Test prior-art-search.sh
|
|
1274
|
+
set -euo pipefail
|
|
1275
|
+
|
|
1276
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
1277
|
+
SEARCH_SCRIPT="$SCRIPT_DIR/../prior-art-search.sh"
|
|
1278
|
+
|
|
1279
|
+
FAILURES=0
|
|
1280
|
+
TESTS=0
|
|
1281
|
+
|
|
1282
|
+
assert_eq() {
|
|
1283
|
+
local desc="$1" expected="$2" actual="$3"
|
|
1284
|
+
TESTS=$((TESTS + 1))
|
|
1285
|
+
if [[ "$expected" != "$actual" ]]; then
|
|
1286
|
+
echo "FAIL: $desc"
|
|
1287
|
+
echo " expected: $expected"
|
|
1288
|
+
echo " actual: $actual"
|
|
1289
|
+
FAILURES=$((FAILURES + 1))
|
|
1290
|
+
else
|
|
1291
|
+
echo "PASS: $desc"
|
|
1292
|
+
fi
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
assert_exit() {
|
|
1296
|
+
local desc="$1" expected_exit="$2"
|
|
1297
|
+
shift 2
|
|
1298
|
+
local actual_exit=0
|
|
1299
|
+
"$@" || actual_exit=$?
|
|
1300
|
+
TESTS=$((TESTS + 1))
|
|
1301
|
+
if [[ "$expected_exit" != "$actual_exit" ]]; then
|
|
1302
|
+
echo "FAIL: $desc"
|
|
1303
|
+
echo " expected exit: $expected_exit"
|
|
1304
|
+
echo " actual exit: $actual_exit"
|
|
1305
|
+
FAILURES=$((FAILURES + 1))
|
|
1306
|
+
else
|
|
1307
|
+
echo "PASS: $desc"
|
|
1308
|
+
fi
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
# --- Test: --help exits 0 ---
|
|
1312
|
+
assert_exit "prior-art-search --help exits 0" 0 \
|
|
1313
|
+
bash "$SEARCH_SCRIPT" --help
|
|
1314
|
+
|
|
1315
|
+
# --- Test: --dry-run produces output without calling gh ---
|
|
1316
|
+
output=$(bash "$SEARCH_SCRIPT" --dry-run "implement webhook handler" 2>&1)
|
|
1317
|
+
echo "$output" | grep -q "Search query:" && TESTS=$((TESTS + 1)) && echo "PASS: dry-run shows search query" || {
|
|
1318
|
+
TESTS=$((TESTS + 1)); echo "FAIL: dry-run missing search query"; FAILURES=$((FAILURES + 1))
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
# --- Test: missing query shows usage ---
|
|
1322
|
+
assert_exit "prior-art-search: no args exits 1" 1 \
|
|
1323
|
+
bash "$SEARCH_SCRIPT"
|
|
1324
|
+
|
|
1325
|
+
# === Summary ===
|
|
1326
|
+
echo ""
|
|
1327
|
+
echo "Results: $((TESTS - FAILURES))/$TESTS passed"
|
|
1328
|
+
if [[ $FAILURES -gt 0 ]]; then
|
|
1329
|
+
echo "FAILURES: $FAILURES"
|
|
1330
|
+
exit 1
|
|
1331
|
+
fi
|
|
1332
|
+
echo "ALL PASSED"
|
|
1333
|
+
```
|
|
1334
|
+
|
|
1335
|
+
**Step 2: Run test to verify it fails**
|
|
1336
|
+
|
|
1337
|
+
Run: `bash scripts/tests/test-prior-art-search.sh`
|
|
1338
|
+
Expected: FAIL — script does not exist
|
|
1339
|
+
|
|
1340
|
+
**Step 3: Write implementation**
|
|
1341
|
+
|
|
1342
|
+
Create `scripts/prior-art-search.sh`:
|
|
1343
|
+
|
|
1344
|
+
```bash
|
|
1345
|
+
#!/usr/bin/env bash
|
|
1346
|
+
# prior-art-search.sh — Search GitHub and local codebase for prior art
|
|
1347
|
+
#
|
|
1348
|
+
# Usage: prior-art-search.sh [--dry-run] [--local-only] [--github-only] <query>
|
|
1349
|
+
#
|
|
1350
|
+
# Searches:
|
|
1351
|
+
# 1. GitHub repos (gh search repos)
|
|
1352
|
+
# 2. GitHub code (gh search code)
|
|
1353
|
+
# 3. Local ~/Documents/projects/ (grep -r)
|
|
1354
|
+
#
|
|
1355
|
+
# Output: Ranked results with source, relevance, and URL/path
|
|
1356
|
+
set -euo pipefail
|
|
1357
|
+
|
|
1358
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
1359
|
+
source "$SCRIPT_DIR/lib/common.sh"
|
|
1360
|
+
|
|
1361
|
+
DRY_RUN=false
|
|
1362
|
+
LOCAL_ONLY=false
|
|
1363
|
+
GITHUB_ONLY=false
|
|
1364
|
+
QUERY=""
|
|
1365
|
+
MAX_RESULTS=10
|
|
1366
|
+
PROJECTS_DIR="${PROJECTS_DIR:-$HOME/Documents/projects}"
|
|
1367
|
+
|
|
1368
|
+
while [[ $# -gt 0 ]]; do
|
|
1369
|
+
case "$1" in
|
|
1370
|
+
--dry-run) DRY_RUN=true; shift ;;
|
|
1371
|
+
--local-only) LOCAL_ONLY=true; shift ;;
|
|
1372
|
+
--github-only) GITHUB_ONLY=true; shift ;;
|
|
1373
|
+
--max-results) MAX_RESULTS="$2"; shift 2 ;;
|
|
1374
|
+
--projects-dir) PROJECTS_DIR="$2"; shift 2 ;;
|
|
1375
|
+
-h|--help)
|
|
1376
|
+
cat <<'USAGE'
|
|
1377
|
+
prior-art-search.sh — Search for prior art before building
|
|
1378
|
+
|
|
1379
|
+
Usage: prior-art-search.sh [OPTIONS] <query>
|
|
1380
|
+
|
|
1381
|
+
Options:
|
|
1382
|
+
--dry-run Show what would be searched without executing
|
|
1383
|
+
--local-only Only search local projects
|
|
1384
|
+
--github-only Only search GitHub
|
|
1385
|
+
--max-results N Max results per source (default: 10)
|
|
1386
|
+
--projects-dir P Local projects directory
|
|
1387
|
+
|
|
1388
|
+
Output: Results ranked by relevance with source attribution
|
|
1389
|
+
USAGE
|
|
1390
|
+
exit 0
|
|
1391
|
+
;;
|
|
1392
|
+
-*) echo "Unknown option: $1" >&2; exit 1 ;;
|
|
1393
|
+
*) QUERY="$1"; shift ;;
|
|
1394
|
+
esac
|
|
1395
|
+
done
|
|
1396
|
+
|
|
1397
|
+
if [[ -z "$QUERY" ]]; then
|
|
1398
|
+
echo "Error: Query required" >&2
|
|
1399
|
+
echo "Usage: prior-art-search.sh <query>" >&2
|
|
1400
|
+
exit 1
|
|
1401
|
+
fi
|
|
1402
|
+
|
|
1403
|
+
echo "=== Prior Art Search ==="
|
|
1404
|
+
echo "Search query: $QUERY"
|
|
1405
|
+
echo ""
|
|
1406
|
+
|
|
1407
|
+
if [[ "$DRY_RUN" == true ]]; then
|
|
1408
|
+
echo "[dry-run] Would search:"
|
|
1409
|
+
[[ "$LOCAL_ONLY" != true ]] && echo " - GitHub repos: gh search repos '$QUERY' --limit $MAX_RESULTS"
|
|
1410
|
+
[[ "$LOCAL_ONLY" != true ]] && echo " - GitHub code: gh search code '$QUERY' --limit $MAX_RESULTS"
|
|
1411
|
+
[[ "$GITHUB_ONLY" != true ]] && echo " - Local projects: grep -rl in $PROJECTS_DIR"
|
|
1412
|
+
exit 0
|
|
1413
|
+
fi
|
|
1414
|
+
|
|
1415
|
+
# Search 1: GitHub repos
|
|
1416
|
+
if [[ "$LOCAL_ONLY" != true ]]; then
|
|
1417
|
+
echo "--- GitHub Repos ---"
|
|
1418
|
+
if command -v gh >/dev/null 2>&1; then
|
|
1419
|
+
gh search repos "$QUERY" --limit "$MAX_RESULTS" --json name,url,description,stargazersCount \
|
|
1420
|
+
--jq '.[] | "★ \(.stargazersCount) | \(.name) — \(.description // "no description") | \(.url)"' \
|
|
1421
|
+
2>/dev/null || echo " (GitHub search unavailable)"
|
|
1422
|
+
else
|
|
1423
|
+
echo " gh CLI not installed — skipping"
|
|
1424
|
+
fi
|
|
1425
|
+
echo ""
|
|
1426
|
+
|
|
1427
|
+
echo "--- GitHub Code ---"
|
|
1428
|
+
if command -v gh >/dev/null 2>&1; then
|
|
1429
|
+
gh search code "$QUERY" --limit "$MAX_RESULTS" --json repository,path \
|
|
1430
|
+
--jq '.[] | "\(.repository.nameWithOwner)/\(.path)"' \
|
|
1431
|
+
2>/dev/null || echo " (GitHub code search unavailable)"
|
|
1432
|
+
else
|
|
1433
|
+
echo " gh CLI not installed — skipping"
|
|
1434
|
+
fi
|
|
1435
|
+
echo ""
|
|
1436
|
+
fi
|
|
1437
|
+
|
|
1438
|
+
# Search 2: Local projects
|
|
1439
|
+
if [[ "$GITHUB_ONLY" != true ]]; then
|
|
1440
|
+
echo "--- Local Projects ---"
|
|
1441
|
+
if [[ -d "$PROJECTS_DIR" ]]; then
|
|
1442
|
+
grep -rl --include='*.py' --include='*.sh' --include='*.ts' --include='*.js' \
|
|
1443
|
+
"$QUERY" "$PROJECTS_DIR" 2>/dev/null | head -"$MAX_RESULTS" || echo " No local matches"
|
|
1444
|
+
else
|
|
1445
|
+
echo " Projects directory not found: $PROJECTS_DIR"
|
|
1446
|
+
fi
|
|
1447
|
+
echo ""
|
|
1448
|
+
fi
|
|
1449
|
+
|
|
1450
|
+
echo "=== Search Complete ==="
|
|
1451
|
+
```
|
|
1452
|
+
|
|
1453
|
+
**Step 4: Run tests**
|
|
1454
|
+
|
|
1455
|
+
Run: `bash scripts/tests/test-prior-art-search.sh`
|
|
1456
|
+
Expected: ALL PASSED
|
|
1457
|
+
|
|
1458
|
+
**Step 5: Commit**
|
|
1459
|
+
|
|
1460
|
+
```bash
|
|
1461
|
+
chmod +x scripts/prior-art-search.sh
|
|
1462
|
+
git add scripts/prior-art-search.sh scripts/tests/test-prior-art-search.sh
|
|
1463
|
+
git commit -m "feat: create prior-art-search.sh for GitHub and local code search"
|
|
1464
|
+
```
|
|
1465
|
+
|
|
1466
|
+
### Task 14: Create pipeline-status.sh
|
|
1467
|
+
|
|
1468
|
+
**Files:**
|
|
1469
|
+
- Create: `scripts/pipeline-status.sh`
|
|
1470
|
+
|
|
1471
|
+
**Step 1: Write the script**
|
|
1472
|
+
|
|
1473
|
+
```bash
|
|
1474
|
+
#!/usr/bin/env bash
|
|
1475
|
+
# pipeline-status.sh — Single-command view of Code Factory pipeline status
|
|
1476
|
+
#
|
|
1477
|
+
# Usage: pipeline-status.sh [--project-root <dir>]
|
|
1478
|
+
set -euo pipefail
|
|
1479
|
+
|
|
1480
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
1481
|
+
source "$SCRIPT_DIR/lib/common.sh"
|
|
1482
|
+
|
|
1483
|
+
PROJECT_ROOT="${1:-.}"
|
|
1484
|
+
|
|
1485
|
+
if [[ "$1" == "--help" || "$1" == "-h" ]]; then
|
|
1486
|
+
echo "pipeline-status.sh — Show Code Factory pipeline status"
|
|
1487
|
+
echo "Usage: pipeline-status.sh [project-root]"
|
|
1488
|
+
exit 0
|
|
1489
|
+
fi
|
|
1490
|
+
|
|
1491
|
+
echo "═══════════════════════════════════════════════"
|
|
1492
|
+
echo " Code Factory Pipeline Status"
|
|
1493
|
+
echo "═══════════════════════════════════════════════"
|
|
1494
|
+
echo "Project: $(basename "$(realpath "$PROJECT_ROOT")")"
|
|
1495
|
+
echo "Type: $(detect_project_type "$PROJECT_ROOT")"
|
|
1496
|
+
echo ""
|
|
1497
|
+
|
|
1498
|
+
# Run-plan state
|
|
1499
|
+
STATE_FILE="$PROJECT_ROOT/.run-plan-state.json"
|
|
1500
|
+
if [[ -f "$STATE_FILE" ]]; then
|
|
1501
|
+
echo "--- Run Plan ---"
|
|
1502
|
+
plan=$(jq -r '.plan_file // "unknown"' "$STATE_FILE")
|
|
1503
|
+
mode=$(jq -r '.mode // "unknown"' "$STATE_FILE")
|
|
1504
|
+
current=$(jq -r '.current_batch // 0' "$STATE_FILE")
|
|
1505
|
+
completed=$(jq -r '.completed_batches | length' "$STATE_FILE")
|
|
1506
|
+
started=$(jq -r '.started_at // "unknown"' "$STATE_FILE")
|
|
1507
|
+
echo " Plan: $(basename "$plan")"
|
|
1508
|
+
echo " Mode: $mode"
|
|
1509
|
+
echo " Progress: $completed batches completed (current: $current)"
|
|
1510
|
+
echo " Started: $started"
|
|
1511
|
+
|
|
1512
|
+
# Last quality gate
|
|
1513
|
+
gate_passed=$(jq -r '.last_quality_gate.passed // "n/a"' "$STATE_FILE")
|
|
1514
|
+
gate_tests=$(jq -r '.last_quality_gate.test_count // "n/a"' "$STATE_FILE")
|
|
1515
|
+
echo " Last gate: passed=$gate_passed, tests=$gate_tests"
|
|
1516
|
+
echo ""
|
|
1517
|
+
else
|
|
1518
|
+
echo "--- Run Plan ---"
|
|
1519
|
+
echo " No active run-plan state found"
|
|
1520
|
+
echo ""
|
|
1521
|
+
fi
|
|
1522
|
+
|
|
1523
|
+
# PRD status
|
|
1524
|
+
if [[ -f "$PROJECT_ROOT/tasks/prd.json" ]]; then
|
|
1525
|
+
echo "--- PRD ---"
|
|
1526
|
+
total=$(jq 'length' "$PROJECT_ROOT/tasks/prd.json")
|
|
1527
|
+
passing=$(jq '[.[] | select(.passes == true)] | length' "$PROJECT_ROOT/tasks/prd.json")
|
|
1528
|
+
echo " Tasks: $passing/$total passing"
|
|
1529
|
+
echo ""
|
|
1530
|
+
else
|
|
1531
|
+
echo "--- PRD ---"
|
|
1532
|
+
echo " No PRD found (tasks/prd.json)"
|
|
1533
|
+
echo ""
|
|
1534
|
+
fi
|
|
1535
|
+
|
|
1536
|
+
# Progress file
|
|
1537
|
+
if [[ -f "$PROJECT_ROOT/progress.txt" ]]; then
|
|
1538
|
+
echo "--- Progress ---"
|
|
1539
|
+
tail -5 "$PROJECT_ROOT/progress.txt" | sed 's/^/ /'
|
|
1540
|
+
echo ""
|
|
1541
|
+
fi
|
|
1542
|
+
|
|
1543
|
+
# Git status
|
|
1544
|
+
echo "--- Git ---"
|
|
1545
|
+
branch=$(git -C "$PROJECT_ROOT" branch --show-current 2>/dev/null || echo "unknown")
|
|
1546
|
+
uncommitted=$(git -C "$PROJECT_ROOT" status --porcelain 2>/dev/null | wc -l || echo 0)
|
|
1547
|
+
echo " Branch: $branch"
|
|
1548
|
+
echo " Uncommitted: $uncommitted files"
|
|
1549
|
+
|
|
1550
|
+
echo ""
|
|
1551
|
+
echo "═══════════════════════════════════════════════"
|
|
1552
|
+
```
|
|
1553
|
+
|
|
1554
|
+
**Step 2: Test manually**
|
|
1555
|
+
|
|
1556
|
+
Run: `bash scripts/pipeline-status.sh .`
|
|
1557
|
+
Expected: Shows status output without errors
|
|
1558
|
+
|
|
1559
|
+
**Step 3: Commit**
|
|
1560
|
+
|
|
1561
|
+
```bash
|
|
1562
|
+
chmod +x scripts/pipeline-status.sh
|
|
1563
|
+
git add scripts/pipeline-status.sh
|
|
1564
|
+
git commit -m "feat: create pipeline-status.sh for single-command pipeline overview"
|
|
1565
|
+
```
|
|
1566
|
+
|
|
1567
|
+
### Task 15: Wire prior-art search into auto-compound.sh
|
|
1568
|
+
|
|
1569
|
+
**Files:**
|
|
1570
|
+
- Modify: `scripts/auto-compound.sh` (add prior-art search step before PRD generation)
|
|
1571
|
+
|
|
1572
|
+
**Step 1: Add search step**
|
|
1573
|
+
|
|
1574
|
+
Insert after Step 2 (branch creation, ~line 117) and before Step 3 (PRD generation):
|
|
1575
|
+
|
|
1576
|
+
```bash
|
|
1577
|
+
# Step 2.5: Prior art search
|
|
1578
|
+
echo "🔎 Step 2.5: Searching for prior art..."
|
|
1579
|
+
if [[ "$DRY_RUN" == "true" ]]; then
|
|
1580
|
+
echo " [dry-run] Would search: $PRIORITY"
|
|
1581
|
+
else
|
|
1582
|
+
PRIOR_ART=$("$SCRIPT_DIR/prior-art-search.sh" "$PRIORITY" 2>&1 || true)
|
|
1583
|
+
echo "$PRIOR_ART" | head -20
|
|
1584
|
+
# Save for PRD context
|
|
1585
|
+
echo "$PRIOR_ART" > prior-art-results.txt
|
|
1586
|
+
echo " Saved to prior-art-results.txt"
|
|
1587
|
+
|
|
1588
|
+
# Append to progress.txt
|
|
1589
|
+
mkdir -p "$(dirname progress.txt)"
|
|
1590
|
+
echo "## Prior Art Search: $PRIORITY" >> progress.txt
|
|
1591
|
+
echo "$PRIOR_ART" | head -10 >> progress.txt
|
|
1592
|
+
echo "" >> progress.txt
|
|
1593
|
+
fi
|
|
1594
|
+
echo ""
|
|
1595
|
+
```
|
|
1596
|
+
|
|
1597
|
+
Update Step 3 (PRD generation) to include prior art context:
|
|
1598
|
+
|
|
1599
|
+
```bash
|
|
1600
|
+
# Include prior art if available
|
|
1601
|
+
local prior_art_context=""
|
|
1602
|
+
if [[ -f "prior-art-results.txt" ]]; then
|
|
1603
|
+
prior_art_context="Prior art found: $(head -20 prior-art-results.txt)"
|
|
1604
|
+
fi
|
|
1605
|
+
prd_output=$(claude --print "/create-prd $PRIORITY. Context from analysis: $(cat analysis.json). $prior_art_context" 2>&1) || {
|
|
1606
|
+
echo "WARNING: PRD generation failed:" >&2
|
|
1607
|
+
echo "$prd_output" | tail -10 >&2
|
|
1608
|
+
}
|
|
1609
|
+
```
|
|
1610
|
+
|
|
1611
|
+
**Step 2: Verify no regressions**
|
|
1612
|
+
|
|
1613
|
+
Run: `bash scripts/tests/run-all-tests.sh`
|
|
1614
|
+
Expected: ALL PASSED
|
|
1615
|
+
|
|
1616
|
+
**Step 3: Verify line count**
|
|
1617
|
+
|
|
1618
|
+
Run: `wc -l scripts/auto-compound.sh`
|
|
1619
|
+
Expected: Under 300 lines
|
|
1620
|
+
|
|
1621
|
+
**Step 4: Commit**
|
|
1622
|
+
|
|
1623
|
+
```bash
|
|
1624
|
+
git add scripts/auto-compound.sh
|
|
1625
|
+
git commit -m "feat: wire prior-art search into auto-compound.sh before PRD generation"
|
|
1626
|
+
```
|
|
1627
|
+
|
|
1628
|
+
## Batch 5: New Capabilities — Failure Digest and Structured Context
|
|
1629
|
+
|
|
1630
|
+
Add intelligent failure analysis and structured cross-batch dependencies.
|
|
1631
|
+
|
|
1632
|
+
### Task 16: Create failure-digest.sh
|
|
1633
|
+
|
|
1634
|
+
**Files:**
|
|
1635
|
+
- Create: `scripts/failure-digest.sh`
|
|
1636
|
+
- Create: `scripts/tests/test-failure-digest.sh`
|
|
1637
|
+
|
|
1638
|
+
**Step 1: Write the failing test**
|
|
1639
|
+
|
|
1640
|
+
```bash
|
|
1641
|
+
#!/usr/bin/env bash
|
|
1642
|
+
# Test failure-digest.sh
|
|
1643
|
+
set -euo pipefail
|
|
1644
|
+
|
|
1645
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
1646
|
+
DIGEST_SCRIPT="$SCRIPT_DIR/../failure-digest.sh"
|
|
1647
|
+
|
|
1648
|
+
FAILURES=0
|
|
1649
|
+
TESTS=0
|
|
1650
|
+
|
|
1651
|
+
assert_eq() {
|
|
1652
|
+
local desc="$1" expected="$2" actual="$3"
|
|
1653
|
+
TESTS=$((TESTS + 1))
|
|
1654
|
+
if [[ "$expected" != "$actual" ]]; then
|
|
1655
|
+
echo "FAIL: $desc"
|
|
1656
|
+
echo " expected: $expected"
|
|
1657
|
+
echo " actual: $actual"
|
|
1658
|
+
FAILURES=$((FAILURES + 1))
|
|
1659
|
+
else
|
|
1660
|
+
echo "PASS: $desc"
|
|
1661
|
+
fi
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
WORK=$(mktemp -d)
|
|
1665
|
+
trap "rm -rf '$WORK'" EXIT
|
|
1666
|
+
|
|
1667
|
+
# Create a fake log with errors
|
|
1668
|
+
cat > "$WORK/batch-1-attempt-1.log" << 'LOG'
|
|
1669
|
+
Some setup output...
|
|
1670
|
+
FAILED tests/test_auth.py::test_login - AssertionError: expected 200 got 401
|
|
1671
|
+
FAILED tests/test_auth.py::test_signup - KeyError: 'email'
|
|
1672
|
+
Traceback (most recent call last):
|
|
1673
|
+
File "src/auth.py", line 42, in login
|
|
1674
|
+
token = generate_token(user)
|
|
1675
|
+
TypeError: generate_token() missing 1 required argument: 'secret'
|
|
1676
|
+
3 failed, 10 passed in 5.2s
|
|
1677
|
+
LOG
|
|
1678
|
+
|
|
1679
|
+
# --- Test: extracts failed test names ---
|
|
1680
|
+
output=$(bash "$DIGEST_SCRIPT" "$WORK/batch-1-attempt-1.log")
|
|
1681
|
+
echo "$output" | grep -q "test_login" && echo "PASS: found test_login" && TESTS=$((TESTS + 1)) || {
|
|
1682
|
+
echo "FAIL: missing test_login"; TESTS=$((TESTS + 1)); FAILURES=$((FAILURES + 1))
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
echo "$output" | grep -q "test_signup" && echo "PASS: found test_signup" && TESTS=$((TESTS + 1)) || {
|
|
1686
|
+
echo "FAIL: missing test_signup"; TESTS=$((TESTS + 1)); FAILURES=$((FAILURES + 1))
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
# --- Test: extracts error types ---
|
|
1690
|
+
echo "$output" | grep -q "TypeError" && echo "PASS: found TypeError" && TESTS=$((TESTS + 1)) || {
|
|
1691
|
+
echo "FAIL: missing TypeError"; TESTS=$((TESTS + 1)); FAILURES=$((FAILURES + 1))
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
# --- Test: help flag ---
|
|
1695
|
+
bash "$DIGEST_SCRIPT" --help >/dev/null 2>&1
|
|
1696
|
+
TESTS=$((TESTS + 1))
|
|
1697
|
+
echo "PASS: --help exits cleanly"
|
|
1698
|
+
|
|
1699
|
+
# === Summary ===
|
|
1700
|
+
echo ""
|
|
1701
|
+
echo "Results: $((TESTS - FAILURES))/$TESTS passed"
|
|
1702
|
+
if [[ $FAILURES -gt 0 ]]; then
|
|
1703
|
+
echo "FAILURES: $FAILURES"
|
|
1704
|
+
exit 1
|
|
1705
|
+
fi
|
|
1706
|
+
echo "ALL PASSED"
|
|
1707
|
+
```
|
|
1708
|
+
|
|
1709
|
+
**Step 2: Run test to verify failure**
|
|
1710
|
+
|
|
1711
|
+
Run: `bash scripts/tests/test-failure-digest.sh`
|
|
1712
|
+
Expected: FAIL — script does not exist
|
|
1713
|
+
|
|
1714
|
+
**Step 3: Write implementation**
|
|
1715
|
+
|
|
1716
|
+
Create `scripts/failure-digest.sh`:
|
|
1717
|
+
|
|
1718
|
+
```bash
|
|
1719
|
+
#!/usr/bin/env bash
|
|
1720
|
+
# failure-digest.sh — Parse failed batch logs into structured failure digest
|
|
1721
|
+
#
|
|
1722
|
+
# Usage: failure-digest.sh <log-file>
|
|
1723
|
+
#
|
|
1724
|
+
# Extracts:
|
|
1725
|
+
# - Failed test names (FAILED pattern)
|
|
1726
|
+
# - Error types and messages (Traceback, Error:, Exception:)
|
|
1727
|
+
# - Test summary line (N failed, M passed)
|
|
1728
|
+
#
|
|
1729
|
+
# Output: Structured text digest suitable for retry prompts
|
|
1730
|
+
set -euo pipefail
|
|
1731
|
+
|
|
1732
|
+
LOG_FILE="${1:-}"
|
|
1733
|
+
|
|
1734
|
+
if [[ "$1" == "--help" || "$1" == "-h" ]]; then
|
|
1735
|
+
echo "failure-digest.sh — Parse batch log into structured failure digest"
|
|
1736
|
+
echo "Usage: failure-digest.sh <log-file>"
|
|
1737
|
+
exit 0
|
|
1738
|
+
fi
|
|
1739
|
+
|
|
1740
|
+
if [[ -z "$LOG_FILE" || ! -f "$LOG_FILE" ]]; then
|
|
1741
|
+
echo "Error: Log file required" >&2
|
|
1742
|
+
exit 1
|
|
1743
|
+
fi
|
|
1744
|
+
|
|
1745
|
+
echo "=== Failure Digest ==="
|
|
1746
|
+
echo "Log: $(basename "$LOG_FILE")"
|
|
1747
|
+
echo ""
|
|
1748
|
+
|
|
1749
|
+
# Extract failed test names
|
|
1750
|
+
echo "--- Failed Tests ---"
|
|
1751
|
+
grep -E '^FAILED ' "$LOG_FILE" 2>/dev/null | sed 's/^FAILED / /' || echo " (none found)"
|
|
1752
|
+
echo ""
|
|
1753
|
+
|
|
1754
|
+
# Extract error types and messages
|
|
1755
|
+
echo "--- Errors ---"
|
|
1756
|
+
grep -E '(Error|Exception|FAIL):' "$LOG_FILE" 2>/dev/null | grep -v '^FAILED ' | head -20 | sed 's/^/ /' || echo " (none found)"
|
|
1757
|
+
echo ""
|
|
1758
|
+
|
|
1759
|
+
# Extract tracebacks (last frame + error line)
|
|
1760
|
+
echo "--- Stack Traces (last frame) ---"
|
|
1761
|
+
grep -B1 -E '^\w+Error:|^\w+Exception:' "$LOG_FILE" 2>/dev/null | head -20 | sed 's/^/ /' || echo " (none found)"
|
|
1762
|
+
echo ""
|
|
1763
|
+
|
|
1764
|
+
# Extract test summary
|
|
1765
|
+
echo "--- Summary ---"
|
|
1766
|
+
grep -E '\d+ (failed|passed|error)' "$LOG_FILE" 2>/dev/null | tail -1 | sed 's/^/ /' || echo " (no summary found)"
|
|
1767
|
+
echo ""
|
|
1768
|
+
|
|
1769
|
+
echo "=== End Digest ==="
|
|
1770
|
+
```
|
|
1771
|
+
|
|
1772
|
+
**Step 4: Run tests**
|
|
1773
|
+
|
|
1774
|
+
Run: `bash scripts/tests/test-failure-digest.sh`
|
|
1775
|
+
Expected: ALL PASSED
|
|
1776
|
+
|
|
1777
|
+
**Step 5: Wire into run-plan-headless.sh**
|
|
1778
|
+
|
|
1779
|
+
In `scripts/lib/run-plan-headless.sh`, replace the naive `tail -50` in the retry prompt (the section that builds `log_tail` for attempt >= 3):
|
|
1780
|
+
|
|
1781
|
+
```bash
|
|
1782
|
+
elif [[ $attempt -ge 3 ]]; then
|
|
1783
|
+
local prev_log="$WORKTREE/logs/batch-${batch}-attempt-$((attempt - 1)).log"
|
|
1784
|
+
local log_digest=""
|
|
1785
|
+
if [[ -f "$prev_log" ]]; then
|
|
1786
|
+
log_digest=$("$SCRIPT_DIR/failure-digest.sh" "$prev_log" 2>/dev/null || tail -50 "$prev_log" 2>/dev/null || true)
|
|
1787
|
+
fi
|
|
1788
|
+
```
|
|
1789
|
+
|
|
1790
|
+
**Step 6: Commit**
|
|
1791
|
+
|
|
1792
|
+
```bash
|
|
1793
|
+
chmod +x scripts/failure-digest.sh
|
|
1794
|
+
git add scripts/failure-digest.sh scripts/tests/test-failure-digest.sh scripts/lib/run-plan-headless.sh
|
|
1795
|
+
git commit -m "feat: create failure-digest.sh, wire into retry prompts replacing naive tail -50"
|
|
1796
|
+
```
|
|
1797
|
+
|
|
1798
|
+
### Task 17: Add context_refs support to plan parser
|
|
1799
|
+
|
|
1800
|
+
**Files:**
|
|
1801
|
+
- Modify: `scripts/lib/run-plan-parser.sh` (add `get_batch_context_refs` function)
|
|
1802
|
+
- Modify: `scripts/lib/run-plan-prompt.sh` (include context_refs file contents in prompt)
|
|
1803
|
+
- Modify: `scripts/tests/test-run-plan-parser.sh` (add context_refs tests)
|
|
1804
|
+
|
|
1805
|
+
**Step 1: Add failing test**
|
|
1806
|
+
|
|
1807
|
+
Add to `scripts/tests/test-run-plan-parser.sh`:
|
|
1808
|
+
|
|
1809
|
+
```bash
|
|
1810
|
+
# === get_batch_context_refs tests ===
|
|
1811
|
+
|
|
1812
|
+
# Create a plan with context_refs
|
|
1813
|
+
cat > "$WORK/refs-plan.md" << 'PLAN'
|
|
1814
|
+
## Batch 1: Setup
|
|
1815
|
+
|
|
1816
|
+
### Task 1: Create base
|
|
1817
|
+
Content here.
|
|
1818
|
+
|
|
1819
|
+
## Batch 2: Build on base
|
|
1820
|
+
context_refs: src/auth.py, tests/test_auth.py
|
|
1821
|
+
|
|
1822
|
+
### Task 2: Extend
|
|
1823
|
+
Uses auth module from batch 1.
|
|
1824
|
+
PLAN
|
|
1825
|
+
|
|
1826
|
+
# Batch 1 has no refs
|
|
1827
|
+
val=$(get_batch_context_refs "$WORK/refs-plan.md" 1)
|
|
1828
|
+
assert_eq "get_batch_context_refs: batch 1 has no refs" "" "$val"
|
|
1829
|
+
|
|
1830
|
+
# Batch 2 has refs
|
|
1831
|
+
val=$(get_batch_context_refs "$WORK/refs-plan.md" 2)
|
|
1832
|
+
echo "$val" | grep -q "src/auth.py" && echo "PASS: batch 2 refs include src/auth.py" && TESTS=$((TESTS + 1)) || {
|
|
1833
|
+
echo "FAIL: batch 2 refs missing src/auth.py"; TESTS=$((TESTS + 1)); FAILURES=$((FAILURES + 1))
|
|
1834
|
+
}
|
|
1835
|
+
```
|
|
1836
|
+
|
|
1837
|
+
**Step 2: Run test to verify failure**
|
|
1838
|
+
|
|
1839
|
+
Run: `bash scripts/tests/test-run-plan-parser.sh`
|
|
1840
|
+
Expected: FAIL — `get_batch_context_refs` does not exist
|
|
1841
|
+
|
|
1842
|
+
**Step 3: Add function to parser**
|
|
1843
|
+
|
|
1844
|
+
Add to `scripts/lib/run-plan-parser.sh`:
|
|
1845
|
+
|
|
1846
|
+
```bash
|
|
1847
|
+
get_batch_context_refs() {
|
|
1848
|
+
local plan_file="$1" batch_num="$2"
|
|
1849
|
+
local batch_text
|
|
1850
|
+
batch_text=$(get_batch_text "$plan_file" "$batch_num")
|
|
1851
|
+
# Extract "context_refs: file1, file2, ..." line
|
|
1852
|
+
local refs_line
|
|
1853
|
+
refs_line=$(echo "$batch_text" | grep -E '^context_refs:' | head -1 || true)
|
|
1854
|
+
if [[ -z "$refs_line" ]]; then
|
|
1855
|
+
echo ""
|
|
1856
|
+
return
|
|
1857
|
+
fi
|
|
1858
|
+
# Strip "context_refs: " prefix and split on comma
|
|
1859
|
+
echo "${refs_line#context_refs: }" | tr ',' '\n' | sed 's/^[[:space:]]*//' | sed 's/[[:space:]]*$//'
|
|
1860
|
+
}
|
|
1861
|
+
```
|
|
1862
|
+
|
|
1863
|
+
**Step 4: Update prompt builder to include context_refs**
|
|
1864
|
+
|
|
1865
|
+
In `scripts/lib/run-plan-prompt.sh`, add after the `progress_tail` section:
|
|
1866
|
+
|
|
1867
|
+
```bash
|
|
1868
|
+
# Cross-batch context: referenced files from context_refs
|
|
1869
|
+
local context_refs_content=""
|
|
1870
|
+
local refs
|
|
1871
|
+
refs=$(get_batch_context_refs "$plan_file" "$batch_num")
|
|
1872
|
+
if [[ -n "$refs" ]]; then
|
|
1873
|
+
while IFS= read -r ref; do
|
|
1874
|
+
[[ -z "$ref" ]] && continue
|
|
1875
|
+
if [[ -f "$worktree/$ref" ]]; then
|
|
1876
|
+
context_refs_content+="
|
|
1877
|
+
--- $ref ---
|
|
1878
|
+
$(head -100 "$worktree/$ref")
|
|
1879
|
+
"
|
|
1880
|
+
fi
|
|
1881
|
+
done <<< "$refs"
|
|
1882
|
+
fi
|
|
1883
|
+
```
|
|
1884
|
+
|
|
1885
|
+
And add to the prompt output:
|
|
1886
|
+
```bash
|
|
1887
|
+
$(if [[ -n "$context_refs_content" ]]; then
|
|
1888
|
+
echo "
|
|
1889
|
+
Referenced files from prior batches:
|
|
1890
|
+
${context_refs_content}"
|
|
1891
|
+
fi)
|
|
1892
|
+
```
|
|
1893
|
+
|
|
1894
|
+
**Step 5: Run tests**
|
|
1895
|
+
|
|
1896
|
+
Run: `bash scripts/tests/test-run-plan-parser.sh && bash scripts/tests/test-run-plan-prompt.sh`
|
|
1897
|
+
Expected: ALL PASSED
|
|
1898
|
+
|
|
1899
|
+
**Step 6: Commit**
|
|
1900
|
+
|
|
1901
|
+
```bash
|
|
1902
|
+
git add scripts/lib/run-plan-parser.sh scripts/lib/run-plan-prompt.sh scripts/tests/test-run-plan-parser.sh
|
|
1903
|
+
git commit -m "feat: add context_refs support for cross-batch file dependencies in plans"
|
|
1904
|
+
```
|
|
1905
|
+
|
|
1906
|
+
## Batch 6: License Check and Final Wiring
|
|
1907
|
+
|
|
1908
|
+
Add license checking, wire all new gates, and ensure the full pipeline works end-to-end.
|
|
1909
|
+
|
|
1910
|
+
### Task 18: Create license-check.sh
|
|
1911
|
+
|
|
1912
|
+
**Files:**
|
|
1913
|
+
- Create: `scripts/license-check.sh`
|
|
1914
|
+
|
|
1915
|
+
**Step 1: Write the script**
|
|
1916
|
+
|
|
1917
|
+
```bash
|
|
1918
|
+
#!/usr/bin/env bash
|
|
1919
|
+
# license-check.sh — Check dependencies for license compatibility
|
|
1920
|
+
#
|
|
1921
|
+
# Usage: license-check.sh [--project-root <dir>]
|
|
1922
|
+
set -euo pipefail
|
|
1923
|
+
|
|
1924
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
1925
|
+
source "$SCRIPT_DIR/lib/common.sh"
|
|
1926
|
+
|
|
1927
|
+
PROJECT_ROOT="."
|
|
1928
|
+
|
|
1929
|
+
while [[ $# -gt 0 ]]; do
|
|
1930
|
+
case "$1" in
|
|
1931
|
+
--project-root) PROJECT_ROOT="$2"; shift 2 ;;
|
|
1932
|
+
-h|--help)
|
|
1933
|
+
echo "license-check.sh — Check dependency licenses"
|
|
1934
|
+
echo "Usage: license-check.sh [--project-root <dir>]"
|
|
1935
|
+
echo "Flags GPL/AGPL in MIT-licensed projects."
|
|
1936
|
+
exit 0 ;;
|
|
1937
|
+
*) echo "Unknown: $1" >&2; exit 1 ;;
|
|
1938
|
+
esac
|
|
1939
|
+
done
|
|
1940
|
+
|
|
1941
|
+
cd "$PROJECT_ROOT"
|
|
1942
|
+
project_type=$(detect_project_type ".")
|
|
1943
|
+
|
|
1944
|
+
echo "=== License Check ==="
|
|
1945
|
+
violations=0
|
|
1946
|
+
|
|
1947
|
+
case "$project_type" in
|
|
1948
|
+
python)
|
|
1949
|
+
if [[ -d ".venv" ]] && command -v pip-licenses >/dev/null 2>&1; then
|
|
1950
|
+
echo "Checking Python dependencies..."
|
|
1951
|
+
gpl_deps=$(.venv/bin/python -m pip-licenses --format=csv 2>/dev/null | grep -iE 'GPL|AGPL' | grep -v 'LGPL' || true)
|
|
1952
|
+
if [[ -n "$gpl_deps" ]]; then
|
|
1953
|
+
echo "WARNING: GPL/AGPL dependencies found:"
|
|
1954
|
+
echo "$gpl_deps" | sed 's/^/ /'
|
|
1955
|
+
violations=$((violations + 1))
|
|
1956
|
+
else
|
|
1957
|
+
echo " No GPL/AGPL dependencies"
|
|
1958
|
+
fi
|
|
1959
|
+
else
|
|
1960
|
+
echo " pip-licenses not available — skipping"
|
|
1961
|
+
fi
|
|
1962
|
+
;;
|
|
1963
|
+
node)
|
|
1964
|
+
if command -v npx >/dev/null 2>&1; then
|
|
1965
|
+
echo "Checking Node dependencies..."
|
|
1966
|
+
gpl_deps=$(npx license-checker --csv 2>/dev/null | grep -iE 'GPL|AGPL' | grep -v 'LGPL' || true)
|
|
1967
|
+
if [[ -n "$gpl_deps" ]]; then
|
|
1968
|
+
echo "WARNING: GPL/AGPL dependencies found:"
|
|
1969
|
+
echo "$gpl_deps" | head -10 | sed 's/^/ /'
|
|
1970
|
+
violations=$((violations + 1))
|
|
1971
|
+
else
|
|
1972
|
+
echo " No GPL/AGPL dependencies"
|
|
1973
|
+
fi
|
|
1974
|
+
else
|
|
1975
|
+
echo " license-checker not available — skipping"
|
|
1976
|
+
fi
|
|
1977
|
+
;;
|
|
1978
|
+
*)
|
|
1979
|
+
echo " No license check for project type: $project_type"
|
|
1980
|
+
;;
|
|
1981
|
+
esac
|
|
1982
|
+
|
|
1983
|
+
if [[ $violations -gt 0 ]]; then
|
|
1984
|
+
echo ""
|
|
1985
|
+
echo "license-check: $violations issue(s) found"
|
|
1986
|
+
exit 1
|
|
1987
|
+
fi
|
|
1988
|
+
|
|
1989
|
+
echo "license-check: CLEAN"
|
|
1990
|
+
exit 0
|
|
1991
|
+
```
|
|
1992
|
+
|
|
1993
|
+
**Step 2: Test manually**
|
|
1994
|
+
|
|
1995
|
+
Run: `bash scripts/license-check.sh --help`
|
|
1996
|
+
Expected: Help text, exit 0
|
|
1997
|
+
|
|
1998
|
+
**Step 3: Commit**
|
|
1999
|
+
|
|
2000
|
+
```bash
|
|
2001
|
+
chmod +x scripts/license-check.sh
|
|
2002
|
+
git add scripts/license-check.sh
|
|
2003
|
+
git commit -m "feat: create license-check.sh for dependency license auditing"
|
|
2004
|
+
```
|
|
2005
|
+
|
|
2006
|
+
### Task 19: Wire all new gates into quality-gate.sh
|
|
2007
|
+
|
|
2008
|
+
**Files:**
|
|
2009
|
+
- Modify: `scripts/quality-gate.sh` (add `--with-license` and `--quick` flags)
|
|
2010
|
+
|
|
2011
|
+
**Step 1: Add flag parsing**
|
|
2012
|
+
|
|
2013
|
+
Add `QUICK=false` and `WITH_LICENSE=false` to defaults. Add to arg parser:
|
|
2014
|
+
```bash
|
|
2015
|
+
--quick) QUICK=true; shift ;;
|
|
2016
|
+
--with-license) WITH_LICENSE=true; shift ;;
|
|
2017
|
+
```
|
|
2018
|
+
|
|
2019
|
+
**Step 2: Add conditional checks**
|
|
2020
|
+
|
|
2021
|
+
If `--quick` is set, skip lint and license checks. If `--with-license` is set, add license check after tests:
|
|
2022
|
+
|
|
2023
|
+
```bash
|
|
2024
|
+
# === Optional: License Check ===
|
|
2025
|
+
if [[ "$WITH_LICENSE" == true ]]; then
|
|
2026
|
+
echo ""
|
|
2027
|
+
echo "=== Quality Gate: License Check ==="
|
|
2028
|
+
if ! "$SCRIPT_DIR/license-check.sh" --project-root "$PROJECT_ROOT"; then
|
|
2029
|
+
echo "quality-gate: FAILED at license check"
|
|
2030
|
+
exit 1
|
|
2031
|
+
fi
|
|
2032
|
+
fi
|
|
2033
|
+
```
|
|
2034
|
+
|
|
2035
|
+
If `--quick`, wrap the lint section in:
|
|
2036
|
+
```bash
|
|
2037
|
+
if [[ "$QUICK" != true ]]; then
|
|
2038
|
+
# lint check here
|
|
2039
|
+
fi
|
|
2040
|
+
```
|
|
2041
|
+
|
|
2042
|
+
**Step 3: Run tests**
|
|
2043
|
+
|
|
2044
|
+
Run: `bash scripts/tests/run-all-tests.sh`
|
|
2045
|
+
Expected: ALL PASSED
|
|
2046
|
+
|
|
2047
|
+
**Step 4: Verify line count**
|
|
2048
|
+
|
|
2049
|
+
Run: `wc -l scripts/quality-gate.sh`
|
|
2050
|
+
Expected: Under 300 lines
|
|
2051
|
+
|
|
2052
|
+
**Step 5: Commit**
|
|
2053
|
+
|
|
2054
|
+
```bash
|
|
2055
|
+
git add scripts/quality-gate.sh
|
|
2056
|
+
git commit -m "feat: add --quick and --with-license flags to quality-gate.sh"
|
|
2057
|
+
```
|
|
2058
|
+
|
|
2059
|
+
### Task 20: Update run-all-tests.sh to discover new test files
|
|
2060
|
+
|
|
2061
|
+
**Files:**
|
|
2062
|
+
- Modify: `scripts/tests/run-all-tests.sh` (expand glob to include non-run-plan tests)
|
|
2063
|
+
|
|
2064
|
+
**Step 1: Update test discovery**
|
|
2065
|
+
|
|
2066
|
+
Change line 13 from:
|
|
2067
|
+
```bash
|
|
2068
|
+
mapfile -t TEST_FILES < <(find "$SCRIPT_DIR" -maxdepth 1 -name "test-run-plan-*.sh" -type f | sort)
|
|
2069
|
+
```
|
|
2070
|
+
to:
|
|
2071
|
+
```bash
|
|
2072
|
+
mapfile -t TEST_FILES < <(find "$SCRIPT_DIR" -maxdepth 1 -name "test-*.sh" -type f | sort)
|
|
2073
|
+
```
|
|
2074
|
+
|
|
2075
|
+
This picks up `test-common.sh`, `test-ollama.sh`, `test-telegram.sh`, `test-prior-art-search.sh`, and `test-failure-digest.sh`.
|
|
2076
|
+
|
|
2077
|
+
**Step 2: Run full test suite**
|
|
2078
|
+
|
|
2079
|
+
Run: `bash scripts/tests/run-all-tests.sh`
|
|
2080
|
+
Expected: ALL new and existing tests discovered and PASSED
|
|
2081
|
+
|
|
2082
|
+
**Step 3: Commit**
|
|
2083
|
+
|
|
2084
|
+
```bash
|
|
2085
|
+
git add scripts/tests/run-all-tests.sh
|
|
2086
|
+
git commit -m "fix: run-all-tests.sh discovers all test-*.sh files, not just run-plan tests"
|
|
2087
|
+
```
|
|
2088
|
+
|
|
2089
|
+
## Batch 7: Integration Wiring + Final Verification
|
|
2090
|
+
|
|
2091
|
+
Wire all components together and verify the full pipeline works end-to-end.
|
|
2092
|
+
|
|
2093
|
+
### Task 21: Verify all scripts under 300 lines
|
|
2094
|
+
|
|
2095
|
+
**Step 1: Check line counts**
|
|
2096
|
+
|
|
2097
|
+
Run: `wc -l scripts/*.sh scripts/lib/*.sh | sort -n`
|
|
2098
|
+
|
|
2099
|
+
Expected: Every file under 300 lines. If any violations remain, refactor.
|
|
2100
|
+
|
|
2101
|
+
**Step 2: Commit any fixes**
|
|
2102
|
+
|
|
2103
|
+
```bash
|
|
2104
|
+
git add -A && git commit -m "fix: ensure all scripts under 300-line limit"
|
|
2105
|
+
```
|
|
2106
|
+
|
|
2107
|
+
### Task 22: Run full test suite and verify
|
|
2108
|
+
|
|
2109
|
+
**Step 1: Run all tests**
|
|
2110
|
+
|
|
2111
|
+
Run: `bash scripts/tests/run-all-tests.sh`
|
|
2112
|
+
Expected: ALL PASSED with zero failures
|
|
2113
|
+
|
|
2114
|
+
**Step 2: Run quality gate on this project**
|
|
2115
|
+
|
|
2116
|
+
Run: `bash scripts/quality-gate.sh --project-root .`
|
|
2117
|
+
Expected: ALL PASSED
|
|
2118
|
+
|
|
2119
|
+
### Task 23: Run pipeline-status.sh to verify integration
|
|
2120
|
+
|
|
2121
|
+
**Step 1: Run pipeline status**
|
|
2122
|
+
|
|
2123
|
+
Run: `bash scripts/pipeline-status.sh .`
|
|
2124
|
+
Expected: Shows project status without errors
|
|
2125
|
+
|
|
2126
|
+
### Task 24: Update progress.txt with final summary
|
|
2127
|
+
|
|
2128
|
+
**Step 1: Append summary**
|
|
2129
|
+
|
|
2130
|
+
```bash
|
|
2131
|
+
echo "## Code Factory v2 — Implementation Complete
|
|
2132
|
+
|
|
2133
|
+
### Phase 1: Foundation
|
|
2134
|
+
- Created scripts/lib/common.sh (detect_project_type, strip_json_fences, check_memory_available, require_command)
|
|
2135
|
+
- Created scripts/lib/ollama.sh (ollama_query, ollama_extract_json)
|
|
2136
|
+
- Created scripts/lib/telegram.sh (extracted from run-plan-notify.sh)
|
|
2137
|
+
- Refactored: quality-gate.sh, auto-compound.sh, analyze-report.sh, entropy-audit.sh
|
|
2138
|
+
- Extracted run-plan-headless.sh from run-plan.sh (412 -> ~260 lines)
|
|
2139
|
+
|
|
2140
|
+
### Phase 2: Accuracy
|
|
2141
|
+
- Fixed test count parsing for jest, go test, and unknown formats
|
|
2142
|
+
- Added cross-batch context (git log, progress.txt, quality gate result) to prompts
|
|
2143
|
+
- Added per-batch duration tracking to state
|
|
2144
|
+
- Fixed PRD output discard in auto-compound.sh (lesson-7)
|
|
2145
|
+
|
|
2146
|
+
### Phase 3: Quality Gates
|
|
2147
|
+
- Added ruff/eslint lint step to quality-gate.sh
|
|
2148
|
+
- Created prior-art-search.sh (GitHub + local search)
|
|
2149
|
+
- Created license-check.sh
|
|
2150
|
+
- Created pipeline-status.sh
|
|
2151
|
+
- Wired prior-art search into auto-compound.sh
|
|
2152
|
+
|
|
2153
|
+
### Phase 4: New Capabilities
|
|
2154
|
+
- Created failure-digest.sh (replaces naive tail -50 in retries)
|
|
2155
|
+
- Added context_refs support for cross-batch file dependencies
|
|
2156
|
+
- All scripts under 300 lines
|
|
2157
|
+
" >> progress.txt
|
|
2158
|
+
```
|
|
2159
|
+
|
|
2160
|
+
**Step 2: Commit**
|
|
2161
|
+
|
|
2162
|
+
```bash
|
|
2163
|
+
git add progress.txt
|
|
2164
|
+
git commit -m "docs: update progress.txt with Code Factory v2 implementation summary"
|
|
2165
|
+
```
|
|
2166
|
+
|
|
2167
|
+
### Task 25: Vertical pipeline trace
|
|
2168
|
+
|
|
2169
|
+
**Step 1: End-to-end dry run**
|
|
2170
|
+
|
|
2171
|
+
Run the full auto-compound pipeline in dry-run mode to verify all components are wired:
|
|
2172
|
+
|
|
2173
|
+
```bash
|
|
2174
|
+
bash scripts/auto-compound.sh . --dry-run --report reports/daily.md
|
|
2175
|
+
```
|
|
2176
|
+
|
|
2177
|
+
Verify output shows: analyze → search → PRD → quality gate → Ralph loop → PR
|
|
2178
|
+
|
|
2179
|
+
If no report file exists, create a minimal one for the trace:
|
|
2180
|
+
```bash
|
|
2181
|
+
echo "# Test Report\n## Issue: Test pipeline integration" > /tmp/test-report.md
|
|
2182
|
+
bash scripts/auto-compound.sh . --dry-run --report /tmp/test-report.md
|
|
2183
|
+
```
|
|
2184
|
+
|
|
2185
|
+
**Step 2: Final commit**
|
|
2186
|
+
|
|
2187
|
+
```bash
|
|
2188
|
+
git add -A && git commit -m "feat: Code Factory v2 complete — all phases implemented and verified"
|
|
2189
|
+
```
|