mirror of
https://github.com/affaan-m/ECC.git
synced 2026-08-17 21:15:40 +02:00
* feat(workflows): re-land orch-review workflow + add /orch-review command Re-lands #2363 (reverted by #2393 to unbreak main's lint) and fixes the root cause so it stays green: - Restore workflows/orch-review.workflow.js + workflows/README.md. - eslint.config.js: ignore 'workflows/**/*.workflow.*' and '.claude/workflows/**' per the maintainer's note in #2393. Workflow DSL scripts use both top-level export (ESM) and top-level return (the runtime wraps them in an async fn), which no single eslint sourceType can parse — they must be excluded, not lint-fixed. 'npx eslint .' is green with this ignore. - Add commands/orch-review.md (the /orch-review surface) + regenerate docs/COMMAND-REGISTRY.json. Supersedes #2397 (command-only), which referenced the reverted workflow. * fix(workflows): address orch-review bot review findings - Verifier uncertainty no longer demotes blockers (Greptile P1 + CodeRabbit): isReal=false only refutes when confidence >= 0.8; low-confidence 'false' is treated as uncertain and kept blocking (fail closed). - Treat the diff (and finding text) as untrusted input in both review and verify prompts; ignore embedded directives (prompt-injection hardening). - Validate changedFiles entries are strings, not just that it is an array. - Enforce proof for HIGH/CRITICAL in FINDINGS_SCHEMA, not only in the prompt. - Remove in-place mutation in dimension build + dedup merge (immutable). - /orch-review: extract & validate a numeric PR id before shelling out to gh. - Docs: complete the stats example, soften wording, refresh follow-up list. * style(workflows): apply formatter to orch-review assembly * fix(plan-orchestrate): detect ecc@ecc marketplace + emit ecc: agent prefix (#2316) (#2409) * fix(plan-orchestrate): detect ecc@ecc marketplace + emit ecc: agent prefix (#2316) Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(ci): resync lockfiles with package.json (eslint 10) + migrate yarn.lock to Yarn 4 format package.json requires eslint@^10.6.0 but the committed locks pinned 9.39.2, so npm ci aborted and Yarn 4 hardened mode rejected the stale v1-classic yarn.lock (YN0028). Regenerate package-lock.json and rewrite yarn.lock in Yarn 4 (berry) format so npm ci and immutable yarn installs both pass. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(ci): require clean probe exit for Windows shell/bash detection; add pyyaml dev dep Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: affaan <affaan@itomarkets.com> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * refactor: consolidate duplicated hook-root resolver into shared resolveEccRoot() (#2368) (#2410) * fix(ci): resync lockfiles with package.json (eslint 10) + migrate yarn.lock to Yarn 4 format package.json requires eslint@^10.6.0 but the committed locks pinned 9.39.2, so npm ci aborted and Yarn 4 hardened mode rejected the stale v1-classic yarn.lock (YN0028). Regenerate package-lock.json and rewrite yarn.lock in Yarn 4 (berry) format so npm ci and immutable yarn installs both pass. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(ci): require clean probe exit for Windows shell/bash detection; add pyyaml dev dep Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * refactor: consolidate duplicated hook-root resolver into shared resolveEccRoot() (#2368) The inline node -e resolver blob was duplicated ~60x across hooks.json, command docs, and translations. Each copy inlined the full ~700-char plugin-root search using a spread over nested array literals (p.join(d,'plugins',...s) over [['ecc'],...]), which breaks Windows hook execution due to shell quoting (#2368). Collapse every copy to a 250-char locator that loads the committed resolve-ecc-root module and delegates to resolveEccRoot() — no spread, no nested array literals, no escaped double quotes. The real search logic now lives in one tested module. Also route session-start-bootstrap.js through resolveEccRoot() instead of its own duplicated reimplementation, and fix the auto-update.md 'marketplace' (singular) typo along the way. Guard tests updated: discovery behavior is asserted against resolveEccRoot(); the inline is asserted to delegate and to contain no Windows-fragile constructs. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(resolve-ecc-root): restore full env-unset discovery in inline resolver Address Greptile review on #2410: when CLAUDE_PLUGIN_ROOT is unset the delegating inline could only load the resolver module from ~/.claude, returning ~/.claude without ever reaching the plugin/cache search. Restore the old inline's discovery breadth (exact plugin roots + versioned cache) Windows-safely (no spread, nested arrays, or escaped quotes), then delegate the authoritative decision to resolveEccRoot(). Add regression tests for plugin-subdir and versioned-cache bootstrap with env unset. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: affaan <affaan@itomarkets.com> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix: docs/COMMAND-REGISTRY.json check fails on fresh Windows clone (missing .gitattributes) (#2437) * fix: add .gitattributes to force LF line endings for text files npm run command-registry:check (part of npm test) fails on a fresh clone on Windows with the common core.autocrlf=true setting: git checks out docs/COMMAND-REGISTRY.json with CRLF, but generate-command-registry.js always writes LF, so the strict string comparison in checkRegistry() never matches. Forcing LF via .gitattributes makes checkouts consistent across platforms regardless of a contributor's local autocrlf setting. * fix: normalize CRLF line endings to LF per .gitattributes pyproject.toml, src/llm/__init__.py, src/llm/prompt/builder.py, src/llm/providers/claude.py, and tests/test_builder.py had CRLF line endings committed to the repo, inconsistent with the rest of the codebase. Renormalized via 'git add --renormalize .' now that .gitattributes enforces eol=lf. --------- Co-authored-by: Affaan Mustafa <me@affaanmustafa.com> * chore(catalog): sync command counts (92->93) + register orch-review in agent.yaml surface --------- Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: affaan <affaan@itomarkets.com> Co-authored-by: Boube <109886533+Cb2i@users.noreply.github.com> Co-authored-by: Affaan Mustafa <me@affaanmustafa.com>
67 lines
4.3 KiB
Markdown
67 lines
4.3 KiB
Markdown
# ECC native workflows (pilot)
|
|
|
|
Scripts in this directory are [Claude Code **Workflow** tool](https://docs.claude.com/en/docs/claude-code) scripts — deterministic, multi-agent orchestration that runs in the background and fans out to subagents.
|
|
|
|
This is a **pilot**: ECC's orchestration (`orch-*`, `multi-*`, GAN/Santa loops) is currently hand-rolled on top of the `Task`/Agent tool. These scripts port the autonomous, fan-out-heavy segments to the native engine, which gives us barrier-free pipelining, automatic concurrency capping, structured-output validation, and resumability for free.
|
|
|
|
## `orch-review.workflow.js`
|
|
|
|
A native port of **orch-pipeline Phase 5 (Review)**.
|
|
|
|
The gated outer loop (Gate 1 after Plan, Gate 2 before Commit) **stays in the main conversation** — native workflows run autonomously in the background and cannot pause for interactive approval. This script owns only the segment *between* the gates:
|
|
|
|
1. **Review** — one reviewer agent per dimension, in parallel:
|
|
- `ecc:code-reviewer` (correctness & quality) — always
|
|
- the matching `ecc:<language>-reviewer` — when `args.language` maps to one
|
|
- `ecc:security-reviewer` — only when the orch-pipeline security trigger matches the diff/paths
|
|
2. **Dedup** — independent reviewers routinely flag the same line, so findings are merged across dimensions keyed on the normalized `evidence` snippet (titles and line numbers drift per reviewer; the offending code does not). Each surviving finding records which `dimensions` reported it and keeps the strictest severity.
|
|
3. **Verify** — every *unique* `CRITICAL`/`HIGH` finding is handed to an independent adversarial verifier that defaults to *refuted* on uncertainty. `MEDIUM`/`LOW` pass through as advisory.
|
|
|
|
The Review→Verify barrier is deliberate: deduping before verification is exactly the case the Workflow guidance calls a justified barrier — it stops the verifier running N times on the same bug (in local testing, 11 raw findings collapsed to 4 unique, roughly halving verifier cost).
|
|
|
|
### Invocation
|
|
|
|
The main loop computes the diff, then calls the Workflow tool:
|
|
|
|
```jsonc
|
|
Workflow({
|
|
scriptPath: "workflows/orch-review.workflow.js",
|
|
args: {
|
|
diff: "<unified git diff text>", // required
|
|
language: "typescript", // optional — selects a language reviewer
|
|
changedFiles: ["src/auth.ts"] // optional — feeds the security trigger
|
|
}
|
|
})
|
|
```
|
|
|
|
Invalid input throws (the gate **fails closed**): a missing/empty `diff`, malformed JSON, or a non-array `changedFiles` is rejected with a clear error rather than silently approving an unreviewed payload.
|
|
|
|
### Returns
|
|
|
|
```jsonc
|
|
{
|
|
"verdict": "APPROVE" | "CHANGES_REQUESTED", // CHANGES_REQUESTED if any blocker OR a dimension failed
|
|
"incomplete": false, // true when one or more review dimensions failed to run
|
|
"failedDimensions": [ /* { dimension, error } — error is a bounded label, never raw subagent text:
|
|
"agent returned null (terminal failure or skip)" | "review agent failed" */ ],
|
|
"blocking": [ /* confirmed CRITICAL/HIGH + unverifiable ones — must clear before Gate 2 */ ],
|
|
"advisory": [ /* MEDIUM/LOW + adversarially-refuted findings */ ],
|
|
"stats": { "dimensions": 3, "failed": 0, "raw": 11, "unique": 4, "confirmed": 4, "unverified": 0, "refuted": 0 }
|
|
}
|
|
```
|
|
|
|
The main loop presents `blocking` at Gate 2; the human still approves the commit. The gate fails closed at every stage: if a reviewer dies the dimension is recorded in `failedDimensions` (verdict never a clean `APPROVE`), and if a *verifier* dies or returns null the blocker is kept in `blocking` (tagged "could not be verified") rather than demoted to advisory — an unreviewed security dimension or an unverifiable CRITICAL must not pass as approved.
|
|
|
|
## Invoking it
|
|
|
|
`/orch-review` (`commands/orch-review.md`) is the command surface: it gathers the
|
|
diff (local uncommitted changes or a GitHub PR), calls this workflow, and reports
|
|
the blocking/advisory split at Gate 2.
|
|
|
|
## Not in this PR (follow-ups)
|
|
|
|
- i18n mirrors (`docs/<locale>/commands/orch-review.md`) for the `/orch-review` command (not CI-enforced; only a subset of commands are translated today).
|
|
- Wiring `/orch-review` into the `orch-pipeline` Review phase as the native option.
|
|
- Installer / manifest wiring so the script ships to `~/.claude/` on install.
|
|
- Porting the **Research** sweep and **Plan** judge-panel segments next.
|