mishkan-harness 0.1.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/LICENSE +21 -0
- package/README.md +205 -0
- package/bin/mishkan.js +221 -0
- package/docs/design/MISHKAN_agent_aliases.md +140 -0
- package/docs/design/MISHKAN_decisions.md +172 -0
- package/docs/design/MISHKAN_harness_design.md +820 -0
- package/docs/design/MISHKAN_ontology.md +87 -0
- package/docs/design/MISHKAN_token_optimisation.md +181 -0
- package/docs/engineer/README.md +37 -0
- package/docs/engineer/profile.example.md +79 -0
- package/docs/usage/01-installation.md +178 -0
- package/docs/usage/02-project-init.md +151 -0
- package/docs/usage/03-orchestration.md +218 -0
- package/docs/usage/04-memory-layer.md +201 -0
- package/docs/usage/05-selective-ingest.md +177 -0
- package/docs/usage/06-llm-providers.md +195 -0
- package/docs/usage/07-troubleshooting.md +316 -0
- package/docs/usage/08-glossary.md +154 -0
- package/docs/usage/09-workflows.md +123 -0
- package/docs/usage/README.md +77 -0
- package/package.json +43 -0
- package/payload/install/settings.hooks.json +47 -0
- package/payload/mishkan/AGENT_SPEC.md +154 -0
- package/payload/mishkan/agents/ahikam.md +58 -0
- package/payload/mishkan/agents/aholiab.md +68 -0
- package/payload/mishkan/agents/asaph.md +73 -0
- package/payload/mishkan/agents/baruch.md +88 -0
- package/payload/mishkan/agents/benaiah.md +76 -0
- package/payload/mishkan/agents/bezalel.md +83 -0
- package/payload/mishkan/agents/caleb.md +74 -0
- package/payload/mishkan/agents/deborah.md +63 -0
- package/payload/mishkan/agents/elasah.md +58 -0
- package/payload/mishkan/agents/eliashib.md +68 -0
- package/payload/mishkan/agents/ezra.md +69 -0
- package/payload/mishkan/agents/hanun.md +64 -0
- package/payload/mishkan/agents/hiram.md +68 -0
- package/payload/mishkan/agents/hizkiah.md +76 -0
- package/payload/mishkan/agents/huldah.md +59 -0
- package/payload/mishkan/agents/huram.md +66 -0
- package/payload/mishkan/agents/hushai.md +59 -0
- package/payload/mishkan/agents/igal.md +58 -0
- package/payload/mishkan/agents/ira.md +86 -0
- package/payload/mishkan/agents/jahaziel.md +71 -0
- package/payload/mishkan/agents/jakin.md +66 -0
- package/payload/mishkan/agents/jehonathan.md +62 -0
- package/payload/mishkan/agents/jehoshaphat.md +68 -0
- package/payload/mishkan/agents/joab.md +71 -0
- package/payload/mishkan/agents/joah.md +62 -0
- package/payload/mishkan/agents/maaseiah.md +61 -0
- package/payload/mishkan/agents/meremoth.md +65 -0
- package/payload/mishkan/agents/meshullam.md +67 -0
- package/payload/mishkan/agents/nathan.md +70 -0
- package/payload/mishkan/agents/nehemiah.md +93 -0
- package/payload/mishkan/agents/obed.md +60 -0
- package/payload/mishkan/agents/oholiab.md +67 -0
- package/payload/mishkan/agents/palal.md +63 -0
- package/payload/mishkan/agents/phinehas.md +73 -0
- package/payload/mishkan/agents/rehum.md +60 -0
- package/payload/mishkan/agents/salma.md +69 -0
- package/payload/mishkan/agents/seraiah.md +73 -0
- package/payload/mishkan/agents/shallum.md +66 -0
- package/payload/mishkan/agents/shaphan.md +64 -0
- package/payload/mishkan/agents/shemaiah.md +67 -0
- package/payload/mishkan/agents/shevna.md +58 -0
- package/payload/mishkan/agents/uriah.md +70 -0
- package/payload/mishkan/agents/zaccur.md +58 -0
- package/payload/mishkan/agents/zadok.md +67 -0
- package/payload/mishkan/agents/zerubbabel.md +69 -0
- package/payload/mishkan/cognee/.env.curated.example +61 -0
- package/payload/mishkan/cognee/.env.example +165 -0
- package/payload/mishkan/cognee/Dockerfile +50 -0
- package/payload/mishkan/cognee/README.md +129 -0
- package/payload/mishkan/cognee/docker-compose.curated-ui.yml +61 -0
- package/payload/mishkan/cognee/docker-compose.curated.yml +85 -0
- package/payload/mishkan/cognee/docker-compose.hardening.yml +16 -0
- package/payload/mishkan/cognee/docker-compose.selfhosted.yml +114 -0
- package/payload/mishkan/cognee/docker-compose.ui.yml +70 -0
- package/payload/mishkan/cognee/docker-compose.yml +71 -0
- package/payload/mishkan/cognee/ingest-curated.py +92 -0
- package/payload/mishkan/commands/dep-audit.md +24 -0
- package/payload/mishkan/commands/mishkan-init.md +25 -0
- package/payload/mishkan/commands/mishkan-resume.md +21 -0
- package/payload/mishkan/commands/promote.md +19 -0
- package/payload/mishkan/commands/sefer-pull.md +19 -0
- package/payload/mishkan/commands/sprint-close.md +21 -0
- package/payload/mishkan/config/curated-library.yaml +113 -0
- package/payload/mishkan/config/improvement-queries.md +29 -0
- package/payload/mishkan/config/model-routing.yaml +87 -0
- package/payload/mishkan/config/projects.yaml +38 -0
- package/payload/mishkan/evals/baruch/README.md +93 -0
- package/payload/mishkan/evals/baruch/fixtures/invalid/bad-outcome-enum.json +15 -0
- package/payload/mishkan/evals/baruch/fixtures/invalid/bad-sprint-pattern.json +15 -0
- package/payload/mishkan/evals/baruch/fixtures/invalid/bad-trigger-enum.json +15 -0
- package/payload/mishkan/evals/baruch/fixtures/invalid/malformed-json.json +7 -0
- package/payload/mishkan/evals/baruch/fixtures/invalid/missing-required-field.json +14 -0
- package/payload/mishkan/evals/baruch/fixtures/valid/blocked-vendor.json +15 -0
- package/payload/mishkan/evals/baruch/fixtures/valid/curated-shortcircuit.json +15 -0
- package/payload/mishkan/evals/baruch/fixtures/valid/partial-no-write.json +14 -0
- package/payload/mishkan/evals/baruch/fixtures/valid/resolved-cross-harness.json +15 -0
- package/payload/mishkan/evals/baruch/golden_case/expected.yaml +35 -0
- package/payload/mishkan/evals/baruch/golden_case/input.yaml +47 -0
- package/payload/mishkan/evals/baruch/golden_case/produced.json +15 -0
- package/payload/mishkan/evals/baruch/run.sh +129 -0
- package/payload/mishkan/hooks/model-route.py +96 -0
- package/payload/mishkan/hooks/post-tool-observe.sh +45 -0
- package/payload/mishkan/hooks/pre-tool-security.sh +150 -0
- package/payload/mishkan/hooks/session-start.sh +20 -0
- package/payload/mishkan/hooks/stop-reporter.sh +29 -0
- package/payload/mishkan/ontology.md +87 -0
- package/payload/mishkan/rules/backend/yasad.md +23 -0
- package/payload/mishkan/rules/common/dependencies.md +53 -0
- package/payload/mishkan/rules/common/quality.md +16 -0
- package/payload/mishkan/rules/common/security.md +20 -0
- package/payload/mishkan/rules/documentation/sefer.md +19 -0
- package/payload/mishkan/rules/frontend/panim.md +21 -0
- package/payload/mishkan/rules/infrastructure/migdal.md +22 -0
- package/payload/mishkan/scripts/dependency-audit.sh +171 -0
- package/payload/mishkan/scripts/ensure-curated-box.sh +66 -0
- package/payload/mishkan/scripts/mishkan-ingest.sh +92 -0
- package/payload/mishkan/scripts/observability-aggregate.sh +57 -0
- package/payload/mishkan/scripts/seed-curated-library.sh +62 -0
- package/payload/mishkan/scripts/sync-profile.sh +65 -0
- package/payload/mishkan/scripts/validate-research-log.sh +108 -0
- package/payload/mishkan/skills/asaph-a11y-seo-craft/SKILL.md +289 -0
- package/payload/mishkan/skills/baruch-research-reporting-craft/SKILL.md +460 -0
- package/payload/mishkan/skills/benaiah-devsecops-craft/SKILL.md +329 -0
- package/payload/mishkan/skills/bezalel-cto-craft/SKILL.md +391 -0
- package/payload/mishkan/skills/caleb-web-research-craft/SKILL.md +306 -0
- package/payload/mishkan/skills/cognee-promote/SKILL.md +40 -0
- package/payload/mishkan/skills/cognee-quickstart/SKILL.md +66 -0
- package/payload/mishkan/skills/context-compress/SKILL.md +36 -0
- package/payload/mishkan/skills/deborah-ux-craft/SKILL.md +295 -0
- package/payload/mishkan/skills/dependency-audit/SKILL.md +59 -0
- package/payload/mishkan/skills/dependency-vetting/SKILL.md +59 -0
- package/payload/mishkan/skills/documentation-craft/SKILL.md +468 -0
- package/payload/mishkan/skills/ezra-research-formulation-craft/SKILL.md +319 -0
- package/payload/mishkan/skills/hanun-observability-craft/SKILL.md +312 -0
- package/payload/mishkan/skills/hiram-ui-craft/SKILL.md +334 -0
- package/payload/mishkan/skills/hizkiah-implementation-craft/SKILL.md +701 -0
- package/payload/mishkan/skills/hushai-security-advisor-craft/SKILL.md +282 -0
- package/payload/mishkan/skills/ira-code-security-craft/SKILL.md +553 -0
- package/payload/mishkan/skills/jakin-intent-clarification-craft/SKILL.md +299 -0
- package/payload/mishkan/skills/jehonathan-publication-craft/SKILL.md +262 -0
- package/payload/mishkan/skills/joab-app-security-craft/SKILL.md +266 -0
- package/payload/mishkan/skills/meremoth-devops-craft/SKILL.md +298 -0
- package/payload/mishkan/skills/meshullam-infra-design-craft/SKILL.md +302 -0
- package/payload/mishkan/skills/mishkan-ingest/SKILL.md +65 -0
- package/payload/mishkan/skills/mishkan-init/SKILL.md +65 -0
- package/payload/mishkan/skills/nathan-architecture-craft/SKILL.md +547 -0
- package/payload/mishkan/skills/nehemiah-pm-craft/SKILL.md +484 -0
- package/payload/mishkan/skills/obed-asset-pipeline-craft/SKILL.md +286 -0
- package/payload/mishkan/skills/oholiab-design-system-craft/SKILL.md +334 -0
- package/payload/mishkan/skills/palal-systems-craft/SKILL.md +281 -0
- package/payload/mishkan/skills/qa-evaluation-craft/SKILL.md +406 -0
- package/payload/mishkan/skills/rehum-sre-advisor-craft/SKILL.md +228 -0
- package/payload/mishkan/skills/reporter-discipline-craft/SKILL.md +351 -0
- package/payload/mishkan/skills/research-pipeline/SKILL.md +55 -0
- package/payload/mishkan/skills/salma-frontend-implementation-craft/SKILL.md +369 -0
- package/payload/mishkan/skills/sefer-pull/SKILL.md +37 -0
- package/payload/mishkan/skills/shallum-database-craft/SKILL.md +347 -0
- package/payload/mishkan/skills/shaphan-summarisation-craft/SKILL.md +271 -0
- package/payload/mishkan/skills/shemaiah-evaluation-craft/SKILL.md +342 -0
- package/payload/mishkan/skills/sprint-report/SKILL.md +28 -0
- package/payload/mishkan/skills/team-lead-craft/SKILL.md +457 -0
- package/payload/mishkan/skills/zadok-contract-craft/SKILL.md +520 -0
- package/payload/mishkan/templates/case-node.schema.json +22 -0
- package/payload/mishkan/templates/mcp.json +22 -0
- package/payload/mishkan/templates/observability-log.schema.json +24 -0
- package/payload/mishkan/templates/project-CLAUDE.md +47 -0
- package/payload/mishkan/templates/research-log.schema.json +40 -0
- package/payload/mishkan/templates/settings.json +12 -0
- package/payload/mishkan/templates/settings.local.json +6 -0
- package/payload/mishkan/templates/sprint-state.schema.json +47 -0
- package/payload/mishkan/templates/team-report.schema.json +50 -0
- package/payload/mishkan/templates/user-CLAUDE.md +62 -0
- package/payload/mishkan/workflows/README.md +88 -0
- package/payload/mishkan/workflows/mishkan-architecture-panel.js +156 -0
- package/payload/mishkan/workflows/mishkan-codebase-audit.js +188 -0
- package/payload/mishkan/workflows/mishkan-deep-research.js +251 -0
- package/payload/mishkan/workflows/mishkan-init.js +156 -0
- package/payload/mishkan/workflows/mishkan-migration-wave.js +180 -0
- package/payload/mishkan/workflows/mishkan-release-readiness.js +163 -0
- package/payload/mishkan/workflows/mishkan-sprint-close.js +112 -0
- package/payload/user/CLAUDE.md +62 -0
- package/payload/user/rules/engineer-standards.md +66 -0
- package/payload/user/rules/y4nn-standards.md +167 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "MISHKAN project-level settings template. Seeded by /mishkan-init into <project>/.claude/settings.json. Committed. Default-deny: agents get explicit allow via their frontmatter tools: field. Hooks are inherited from the user-level ~/.claude/settings.json.",
|
|
3
|
+
"permissions": {
|
|
4
|
+
"defaultMode": "auto",
|
|
5
|
+
"deny": [
|
|
6
|
+
"Bash(git push:*)",
|
|
7
|
+
"Bash(ssh:*)",
|
|
8
|
+
"Bash(sudo:*)",
|
|
9
|
+
"Bash(docker exec:*)"
|
|
10
|
+
]
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "mishkan://templates/sprint-state.schema.json",
|
|
4
|
+
"title": "MISHKAN Sprint State",
|
|
5
|
+
"description": "Project sprint state, loaded on session resume. Lives in project ./CLAUDE.md frontmatter or a sidecar; mirrored to Cognee.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["project", "sprint", "mode", "tasks", "blockers"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"project": { "type": "string" },
|
|
11
|
+
"cognee_namespace": { "type": "string", "description": "Cognee project graph ID" },
|
|
12
|
+
"sprint": { "type": "string", "pattern": "^S[0-9]+$" },
|
|
13
|
+
"milestone": { "type": "string", "pattern": "^M[0-9]+$" },
|
|
14
|
+
"mode": { "type": "string", "enum": ["exploration", "execution"] },
|
|
15
|
+
"tasks": {
|
|
16
|
+
"type": "array",
|
|
17
|
+
"items": {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"additionalProperties": false,
|
|
20
|
+
"required": ["id", "description", "status"],
|
|
21
|
+
"properties": {
|
|
22
|
+
"id": { "type": "string", "pattern": "^T-[0-9]+$" },
|
|
23
|
+
"description": { "type": "string" },
|
|
24
|
+
"status": { "type": "string", "enum": ["pending", "in_progress", "blocked", "completed"] },
|
|
25
|
+
"team": { "type": "string" },
|
|
26
|
+
"agent": { "type": "string" }
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"blockers": {
|
|
31
|
+
"type": "array",
|
|
32
|
+
"items": {
|
|
33
|
+
"type": "object",
|
|
34
|
+
"additionalProperties": false,
|
|
35
|
+
"required": ["task", "reason"],
|
|
36
|
+
"properties": {
|
|
37
|
+
"task": { "type": "string" },
|
|
38
|
+
"reason": { "type": "string" },
|
|
39
|
+
"raised_by": { "type": "string" },
|
|
40
|
+
"severity": { "type": "string", "enum": ["low", "medium", "high", "critical"] }
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"open_flags": { "type": "array", "items": { "type": "string" } },
|
|
45
|
+
"last_updated": { "type": "string", "format": "date-time" }
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "mishkan://templates/team-report.schema.json",
|
|
4
|
+
"title": "MISHKAN Team Reporter Milestone Output",
|
|
5
|
+
"description": "Structured summary surfaced by a Team Reporter at milestone (design §15).",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": [
|
|
9
|
+
"team",
|
|
10
|
+
"sprint",
|
|
11
|
+
"milestone",
|
|
12
|
+
"tasks_completed",
|
|
13
|
+
"tasks_in_progress",
|
|
14
|
+
"tasks_blocked",
|
|
15
|
+
"research_calls",
|
|
16
|
+
"decisions_made",
|
|
17
|
+
"security_findings",
|
|
18
|
+
"cognee_writes",
|
|
19
|
+
"flags"
|
|
20
|
+
],
|
|
21
|
+
"properties": {
|
|
22
|
+
"team": { "type": "string" },
|
|
23
|
+
"sprint": { "type": "string", "pattern": "^S[0-9]+$" },
|
|
24
|
+
"milestone": { "type": "string", "pattern": "^M[0-9]+$" },
|
|
25
|
+
"reporter": { "type": "string", "description": "Reporter agent alias" },
|
|
26
|
+
"tasks_completed": { "type": "array", "items": { "type": "string" } },
|
|
27
|
+
"tasks_in_progress": { "type": "array", "items": { "type": "string" } },
|
|
28
|
+
"tasks_blocked": { "type": "array", "items": { "type": "string" } },
|
|
29
|
+
"research_calls": {
|
|
30
|
+
"type": "object",
|
|
31
|
+
"additionalProperties": false,
|
|
32
|
+
"required": ["resolved", "partial", "blocked", "library_hits"],
|
|
33
|
+
"properties": {
|
|
34
|
+
"resolved": { "type": "integer", "minimum": 0 },
|
|
35
|
+
"partial": { "type": "integer", "minimum": 0 },
|
|
36
|
+
"blocked": { "type": "integer", "minimum": 0 },
|
|
37
|
+
"library_hits": { "type": "integer", "minimum": 0 }
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"decisions_made": { "type": "array", "items": { "type": "string" } },
|
|
41
|
+
"security_findings": { "type": "array", "items": { "type": "string" } },
|
|
42
|
+
"cognee_writes": { "type": "integer", "minimum": 0 },
|
|
43
|
+
"flags": {
|
|
44
|
+
"type": "array",
|
|
45
|
+
"items": { "type": "string" },
|
|
46
|
+
"description": "Items needing Nehemiah/Bezalel attention"
|
|
47
|
+
},
|
|
48
|
+
"timestamp": { "type": "string", "format": "date-time" }
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# MISHKAN — User-Level Harness Identity
|
|
2
|
+
|
|
3
|
+
> מִשְׁכָּן — *dwelling place*. The persistent place where engineering work lives.
|
|
4
|
+
> This file installs to `~/.claude/CLAUDE.md`. It loads on every session.
|
|
5
|
+
> Keep it lean — detailed standards live in the rules files it points to.
|
|
6
|
+
|
|
7
|
+
You are operating inside **MISHKAN**, a personal virtual software engineering
|
|
8
|
+
organisation built around one engineer. Who that engineer is — their identity,
|
|
9
|
+
stack, standards, and how they work — is defined in
|
|
10
|
+
`~/.claude/mishkan/profile.md`. Load it as canonical context.
|
|
11
|
+
|
|
12
|
+
## Default mode
|
|
13
|
+
|
|
14
|
+
Sessions start in **exploration mode**: free conversation. **Nehemiah** (PM) and
|
|
15
|
+
**Bezalel** (CTO) lead. Other agents are available on demand and do not interject
|
|
16
|
+
unless called. No structure is imposed until intent is clear or `/mishkan-init`
|
|
17
|
+
runs. Shift to **execution mode** when a spec converges or a project initialises.
|
|
18
|
+
|
|
19
|
+
## The non-negotiables
|
|
20
|
+
|
|
21
|
+
> Full detail in two layers: `~/.claude/rules/y4nn-standards.md` (harness-maintained
|
|
22
|
+
> defaults) and `~/.claude/rules/engineer-standards.md` (your customizable layer,
|
|
23
|
+
> which overrides the defaults on conflict). The digest below is drawn from them.
|
|
24
|
+
|
|
25
|
+
- **Asymmetric AI delegation.** Generative work (UI, config, boilerplate) may be
|
|
26
|
+
done freely. Stateful operations — `git push`, SSH to production, `docker exec`
|
|
27
|
+
on production, `sudo`, schema migration execution, log forensics execution —
|
|
28
|
+
are **never executed by AI**. Analyse; Y4NN runs.
|
|
29
|
+
- **Sequence before implementation.** PRD → SRS → CONTRACT → ARCHITECTURE →
|
|
30
|
+
MODELING → implementation. Never skip to code without prior spec artifacts.
|
|
31
|
+
- **Verify before fix.** No guess-based reasoning. Exact stacktrace / status /
|
|
32
|
+
log line before any proposed solution. Two root causes on non-trivial failures.
|
|
33
|
+
- **Durable solutions only.** No workarounds. If it won't work in production from
|
|
34
|
+
landing, it does not land.
|
|
35
|
+
- **No scope expansion.** The fix is the fix. Refactoring is a separate scoped
|
|
36
|
+
decision. The approved plan is the scope contract.
|
|
37
|
+
- **No fabricated facts.** State uncertainty explicitly; invoke the research
|
|
38
|
+
pipeline when unknown.
|
|
39
|
+
- **Explanations before implementation.** Surface trade-offs; gate on approval
|
|
40
|
+
for consequential decisions.
|
|
41
|
+
- **Stop pending actions immediately when Y4NN speaks** mid-task.
|
|
42
|
+
- **Commit format:** `type(scope) short description` + 5–15 line body. No emojis.
|
|
43
|
+
No `Co-Authored-By`. Lowercase subject. No terminating period.
|
|
44
|
+
- **Language:** English for all artifacts, code, commands. Do not imitate French.
|
|
45
|
+
|
|
46
|
+
## Layout
|
|
47
|
+
|
|
48
|
+
- Agents: `~/.claude/mishkan/agents/` (45 agents — orchestration, research, 6 teams)
|
|
49
|
+
- Rules: `~/.claude/rules/y4nn-standards.md` + `~/.claude/mishkan/rules/`
|
|
50
|
+
- Skills: `~/.claude/mishkan/skills/`
|
|
51
|
+
- Commands: `/mishkan-init`, `/mishkan-resume`, `/sprint-close`, `/promote`, `/sefer-pull`
|
|
52
|
+
- Engineer profile: `~/.claude/mishkan/profile.md` (runtime copy of the canonical `docs/engineer/profile.md`; loaded as engineer context)
|
|
53
|
+
- Knowledge graph: Cognee (local Docker), grows through working sessions
|
|
54
|
+
|
|
55
|
+
## Routing
|
|
56
|
+
|
|
57
|
+
Everything routes through Nehemiah (scope, delivery, sprint state) and Bezalel
|
|
58
|
+
(technical standards, architecture, quality bar). Team Leads coordinate within
|
|
59
|
+
teams. QA and Team Reporters are structurally separate from the agents producing
|
|
60
|
+
work — no agent judges its own output.
|
|
61
|
+
|
|
62
|
+
<!-- Project-specific state is injected below by ./CLAUDE.md when a project is initialised. -->
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# MISHKAN workflows
|
|
2
|
+
|
|
3
|
+
Seven dynamic-workflow scripts that codify the orchestrations where
|
|
4
|
+
parallel scale + repeatability justify a script. Each maps to one or
|
|
5
|
+
more of the nine canonical workflow patterns from
|
|
6
|
+
[Anthropic's reference](https://code.claude.com/docs/en/workflows) and the
|
|
7
|
+
[community patterns catalogue](https://github.com/ray-amjad/claude-code-workflow-creator/blob/main/references/patterns.md).
|
|
8
|
+
|
|
9
|
+
These run from the **main session only** (subagents cannot invoke
|
|
10
|
+
`Workflow`). Skills wire the invocation paths.
|
|
11
|
+
|
|
12
|
+
## Catalogue
|
|
13
|
+
|
|
14
|
+
| Script | Pattern combo | Real problem it solves | When to invoke |
|
|
15
|
+
|---|---|---|---|
|
|
16
|
+
| [`mishkan-sprint-close.js`](mishkan-sprint-close.js) | barrier `parallel()` + aggregator | The six Team Reporters need to emit at once and Nehemiah needs the complete set to surface cross-team handoffs | At `/sprint-close` |
|
|
17
|
+
| [`mishkan-deep-research.js`](mishkan-deep-research.js) | pipeline + adversarial verify + barrier | The research pipeline's six stages with 3-vote refute per finding; replaces sequential Task delegation when false-confident answers are costly | Any unknown where verification matters |
|
|
18
|
+
| [`mishkan-codebase-audit.js`](mishkan-codebase-audit.js) | multi-modal sweep + fan-out + dedup + adversarial verify | Bug / security / hardening / perf / a11y / contract sweep across a whole project; one auditor lens per file class | Periodic audits; pre-release reviews; post-incident hardening |
|
|
19
|
+
| [`mishkan-migration-wave.js`](mishkan-migration-wave.js) | pipeline (find → transform → review → verify) + worktree isolation + judge panel on review | Refactors / framework swaps / contract renames across many files; per-file independence with 2-reviewer accept gate | Refactors, deprecations, framework swaps |
|
|
20
|
+
| [`mishkan-architecture-panel.js`](mishkan-architecture-panel.js) | judge panel + impact-fanout + synthesis | Architecture decisions with a wide answer space; 3 Nathan runs from cost/scale/simplicity priors; Zadok+Phinehas+Shallum score; Bezalel synthesises | High-leverage architecture decisions |
|
|
21
|
+
| [`mishkan-release-readiness.js`](mishkan-release-readiness.js) | barrier `parallel()` + structured pass/fail + nested workflow | Pre-deploy gate: tests + security + dependency + SLO + pipeline shape, all at once → single go/no-go | Before every staging-to-prod deploy |
|
|
22
|
+
| [`mishkan-init.js`](mishkan-init.js) | pipeline with overlap (PRD → SRS → CONTRACT+ARCH in parallel → THREAT+C4 in parallel → settle) | Cut project init from hours to minutes without violating the sequence rule | Once per project at `/mishkan-init` |
|
|
23
|
+
|
|
24
|
+
## The patterns each script uses
|
|
25
|
+
|
|
26
|
+
| Pattern | Used by |
|
|
27
|
+
|---|---|
|
|
28
|
+
| Fan-out → synthesize | `codebase-audit`, `release-readiness`, `architecture-panel` |
|
|
29
|
+
| Pipeline (overlapping) | `deep-research`, `migration-wave`, `init` |
|
|
30
|
+
| Barrier `parallel()` | `sprint-close`, `release-readiness`, `architecture-panel` (Vote stage) |
|
|
31
|
+
| Adversarial verification (3-vote refute) | `deep-research`, `codebase-audit` |
|
|
32
|
+
| Judge panel | `architecture-panel`, `migration-wave` (2-reviewer accept) |
|
|
33
|
+
| Nested workflow | `release-readiness` → `codebase-audit` |
|
|
34
|
+
| Loop-until-X | — (mechanism inside scripts when needed; no top-level loop workflows yet) |
|
|
35
|
+
|
|
36
|
+
## Cost discipline
|
|
37
|
+
|
|
38
|
+
Workflows are expensive. The community baseline is **3–6 workflows per
|
|
39
|
+
production team**. Seven is the working ceiling — adding more
|
|
40
|
+
typically means the new use case is better served by Task delegation
|
|
41
|
+
or a skill.
|
|
42
|
+
|
|
43
|
+
Cost expectations per run (subagent-tokens, rough orders of
|
|
44
|
+
magnitude):
|
|
45
|
+
|
|
46
|
+
| Script | Typical cost | Notes |
|
|
47
|
+
|---|---|---|
|
|
48
|
+
| `mishkan-sprint-close` | low | 6 reporters; bounded by team count |
|
|
49
|
+
| `mishkan-deep-research` | medium | 6 stages × per-sub-question fan-out; verify multiplies |
|
|
50
|
+
| `mishkan-codebase-audit` | high | `files × lenses` audits, plus 3-vote per finding |
|
|
51
|
+
| `mishkan-migration-wave` | very high | `files × (1 transformer + N reviewers + verify)`; the Bun-shape |
|
|
52
|
+
| `mishkan-architecture-panel` | medium | 3 proposals × 3 reviewers + synthesis |
|
|
53
|
+
| `mishkan-release-readiness` | low-medium | 7–8 parallel checks; nested audit if enabled |
|
|
54
|
+
| `mishkan-init` | medium | 6 artefacts pipelined |
|
|
55
|
+
|
|
56
|
+
Run on a small slice first (one directory; one phase) before
|
|
57
|
+
committing to a full wave.
|
|
58
|
+
|
|
59
|
+
## Where these get invoked from
|
|
60
|
+
|
|
61
|
+
The Skills that wire main-session invocation paths:
|
|
62
|
+
|
|
63
|
+
- `nehemiah-pm-craft` — `mishkan-sprint-close`, `mishkan-release-readiness`
|
|
64
|
+
- `bezalel-cto-craft` — `mishkan-architecture-panel`, `mishkan-release-readiness`
|
|
65
|
+
- `phinehas-cto-craft` (via `team-lead-craft`) — `mishkan-codebase-audit`
|
|
66
|
+
- `eliashib-cto-craft` (via `team-lead-craft`) — `mishkan-release-readiness`
|
|
67
|
+
- `jehoshaphat-cto-craft` (via `team-lead-craft`) — `mishkan-init`
|
|
68
|
+
- The research pipeline agents reach the deep-research workflow via the orchestrator path
|
|
69
|
+
|
|
70
|
+
## Adding a workflow
|
|
71
|
+
|
|
72
|
+
Don't, unless:
|
|
73
|
+
|
|
74
|
+
1. The task runs ≥ 10 times per quarter (justifies codification).
|
|
75
|
+
2. The parallel agent count is ≥ 6 (justifies the workflow runtime over Task delegation).
|
|
76
|
+
3. The repeatability is real (same orchestration each time, just different inputs).
|
|
77
|
+
|
|
78
|
+
Otherwise: Task fan-out from the main session is the right answer.
|
|
79
|
+
The seven workflows above were picked precisely because each clears
|
|
80
|
+
all three bars.
|
|
81
|
+
|
|
82
|
+
## Constraints (reminders)
|
|
83
|
+
|
|
84
|
+
- Workflows are main-session-only.
|
|
85
|
+
- Nesting limit: one level. `release-readiness` can call `codebase-audit`; `codebase-audit` cannot call another workflow.
|
|
86
|
+
- Concurrency cap: `min(16, cpu-2)` per run; shared across nested.
|
|
87
|
+
- Cap of 1,000 agents per run (Anthropic platform limit).
|
|
88
|
+
- Date/random functions disabled inside scripts. Pass timestamps via `args` if needed.
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// mishkan-architecture-panel — judge panel for architecture decisions.
|
|
2
|
+
//
|
|
3
|
+
// Draft three independent architecture proposals from different priors
|
|
4
|
+
// (cost-first, scale-first, simplicity-first), have impact-checker
|
|
5
|
+
// specialists (Zadok contract, Phinehas threat, Shallum data) score each
|
|
6
|
+
// in parallel, synthesise the winner while grafting the runners-up best
|
|
7
|
+
// ideas. This is the "hard plan drafted from several angles" pattern
|
|
8
|
+
// Anthropic names as a canonical workflow use case.
|
|
9
|
+
//
|
|
10
|
+
// Patterns: judge panel (3 independent proposals → independent scoring) +
|
|
11
|
+
// fan-out (parallel impact reviews) + synthesis.
|
|
12
|
+
//
|
|
13
|
+
// Args: {
|
|
14
|
+
// decision: "<one sentence: what is being decided>",
|
|
15
|
+
// context: "<paragraph: forces, constraints, horizon, current state>",
|
|
16
|
+
// horizon: "3-months" | "12-months" | "3-years"
|
|
17
|
+
// }
|
|
18
|
+
|
|
19
|
+
export const meta = {
|
|
20
|
+
name: 'mishkan-architecture-panel',
|
|
21
|
+
description: 'Judge panel for an architecture decision: 3 Nathan runs from different priors propose alternatives; Zadok/Phinehas/Shallum score impact in parallel; main session synthesises the winner.',
|
|
22
|
+
whenToUse: 'High-leverage architecture decisions where the answer space is genuinely wide (service boundaries, consistency model, data ownership, contract evolution shape). Skip for decisions with a known single best answer.',
|
|
23
|
+
phases: [
|
|
24
|
+
{ title: 'Draft', detail: '3 parallel Nathan runs with distinct priors' },
|
|
25
|
+
{ title: 'Impact', detail: 'Zadok / Phinehas / Shallum score each proposal' },
|
|
26
|
+
{ title: 'Vote', detail: 'rank proposals by impact-score sum + Bezalel sanity' },
|
|
27
|
+
{ title: 'Synthesise',detail: 'main session writes the chosen architecture, grafting runner-up insights' },
|
|
28
|
+
],
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!args?.decision || !args?.context) {
|
|
32
|
+
throw new Error('mishkan-architecture-panel requires: { decision, context, horizon? }')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const HORIZON = args.horizon || '12-months'
|
|
36
|
+
|
|
37
|
+
const PROPOSAL_SCHEMA = {
|
|
38
|
+
type: 'object',
|
|
39
|
+
required: ['title', 'shape', 'rationale', 'trade_offs', 'out_of_scope'],
|
|
40
|
+
properties: {
|
|
41
|
+
title: { type: 'string' },
|
|
42
|
+
shape: { type: 'string', description: 'one paragraph; the bounded contexts, service boundaries, data ownership, consistency story' },
|
|
43
|
+
rationale: { type: 'string' },
|
|
44
|
+
trade_offs: {
|
|
45
|
+
type: 'object',
|
|
46
|
+
required: ['accepts', 'gives_up'],
|
|
47
|
+
properties: {
|
|
48
|
+
accepts: { type: 'string' },
|
|
49
|
+
gives_up: { type: 'string' },
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
out_of_scope: { type: 'array', items: { type: 'string' }, minItems: 3 },
|
|
53
|
+
confidence: { enum: ['high', 'medium', 'low'] },
|
|
54
|
+
},
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const IMPACT_SCHEMA = {
|
|
58
|
+
type: 'object',
|
|
59
|
+
required: ['proposal_title', 'reviewer', 'score', 'concerns', 'unblockers'],
|
|
60
|
+
properties: {
|
|
61
|
+
proposal_title: { type: 'string' },
|
|
62
|
+
reviewer: { type: 'string' },
|
|
63
|
+
score: { type: 'integer', minimum: 0, maximum: 10 },
|
|
64
|
+
concerns: { type: 'array', items: { type: 'string' } },
|
|
65
|
+
unblockers: { type: 'array', items: { type: 'string' } },
|
|
66
|
+
},
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// --- Stage 1: Three Nathan runs with distinct priors ------------------
|
|
70
|
+
phase('Draft')
|
|
71
|
+
const PRIORS = [
|
|
72
|
+
{ key: 'cost', prior: 'Optimise for lowest delivery cost and lowest operational complexity. Favour fewer services, fewer external dependencies, simpler consistency models.' },
|
|
73
|
+
{ key: 'scale', prior: 'Optimise for horizon-stretching scale. Assume traffic 100× current; assume team grows 5×. Favour bounded contexts that survive Conway-Law re-org.' },
|
|
74
|
+
{ key: 'simplicity', prior: 'Optimise for legibility and reversibility. Favour the shape an engineer can reconstruct in an afternoon; reject anything where the failure mode is hidden.' },
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
const proposals = await parallel(PRIORS.map(({ key, prior }) => () => agent(
|
|
78
|
+
`Act as Nathan. Apply nathan-architecture-craft. Propose an architecture for: ${args.decision}. ` +
|
|
79
|
+
`Context: ${args.context}. Horizon: ${HORIZON}. ` +
|
|
80
|
+
`Your specific prior for this proposal: ${prior} ` +
|
|
81
|
+
`Commit fully to this prior. Name the force-tension your proposal resolves. List three out-of-scope items.`,
|
|
82
|
+
{ label: `nathan:${key}`, phase: 'Draft', agentType: 'nathan', schema: PROPOSAL_SCHEMA }
|
|
83
|
+
)))
|
|
84
|
+
|
|
85
|
+
const valid = proposals.filter(Boolean)
|
|
86
|
+
if (valid.length < 2) {
|
|
87
|
+
return { outcome: 'blocked', reason: 'fewer than 2 proposals survived', proposals: valid }
|
|
88
|
+
}
|
|
89
|
+
log(`Drafted ${valid.length} proposals: ${valid.map(p => p.title).join(' / ')}`)
|
|
90
|
+
|
|
91
|
+
// --- Stage 2: Impact review — Zadok / Phinehas / Shallum on each ------
|
|
92
|
+
phase('Impact')
|
|
93
|
+
const IMPACT_REVIEWERS = ['zadok', 'phinehas', 'shallum']
|
|
94
|
+
const impactTasks = []
|
|
95
|
+
for (const proposal of valid) {
|
|
96
|
+
for (const reviewer of IMPACT_REVIEWERS) {
|
|
97
|
+
impactTasks.push({ proposal, reviewer })
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const impacts = await parallel(impactTasks.map(({ proposal, reviewer }) => () => agent(
|
|
102
|
+
`Act as ${reviewer}. Apply your craft skill. Score this architecture proposal on a 0-10 scale ` +
|
|
103
|
+
`from your specialty perspective. Then list concerns and unblockers. ` +
|
|
104
|
+
`Proposal: ${JSON.stringify(proposal)}.`,
|
|
105
|
+
{
|
|
106
|
+
label: `impact:${reviewer}:${proposal.title.slice(0, 24)}`,
|
|
107
|
+
phase: 'Impact',
|
|
108
|
+
agentType: reviewer,
|
|
109
|
+
schema: IMPACT_SCHEMA,
|
|
110
|
+
}
|
|
111
|
+
)))
|
|
112
|
+
|
|
113
|
+
// --- Stage 3: Aggregate scores per proposal ---------------------------
|
|
114
|
+
phase('Vote')
|
|
115
|
+
const scoresByProposal = {}
|
|
116
|
+
for (const i of impacts.filter(Boolean)) {
|
|
117
|
+
const key = i.proposal_title
|
|
118
|
+
scoresByProposal[key] = scoresByProposal[key] || { total: 0, reviews: [] }
|
|
119
|
+
scoresByProposal[key].total += i.score
|
|
120
|
+
scoresByProposal[key].reviews.push(i)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const ranked = valid
|
|
124
|
+
.map(p => ({
|
|
125
|
+
proposal: p,
|
|
126
|
+
total_score: (scoresByProposal[p.title]?.total) || 0,
|
|
127
|
+
reviews: scoresByProposal[p.title]?.reviews || [],
|
|
128
|
+
}))
|
|
129
|
+
.sort((a, b) => b.total_score - a.total_score)
|
|
130
|
+
|
|
131
|
+
const winner = ranked[0]
|
|
132
|
+
const runners_up = ranked.slice(1)
|
|
133
|
+
log(`Winner: "${winner.proposal.title}" (${winner.total_score} pts). Runners-up: ${runners_up.map(r => `"${r.proposal.title}" (${r.total_score})`).join(', ')}`)
|
|
134
|
+
|
|
135
|
+
// --- Stage 4: Synthesise — Bezalel writes the final architecture -----
|
|
136
|
+
phase('Synthesise')
|
|
137
|
+
const synthesis = await agent(
|
|
138
|
+
`Act as Bezalel. Synthesise the final architecture for: ${args.decision}. ` +
|
|
139
|
+
`The chosen base is "${winner.proposal.title}" (top-scored, ${winner.total_score} points). ` +
|
|
140
|
+
`Chosen proposal: ${JSON.stringify(winner.proposal)}. ` +
|
|
141
|
+
`Impact reviews of the winner: ${JSON.stringify(winner.reviews)}. ` +
|
|
142
|
+
`Runners-up to consider grafting from: ${JSON.stringify(runners_up.map(r => r.proposal))}. ` +
|
|
143
|
+
`Produce the final architecture in MADR-shaped sections: Context, Decision Drivers, Considered Options ` +
|
|
144
|
+
`(name all three with their scores), Decision Outcome (name the force the choice resolves), Consequences ` +
|
|
145
|
+
`(Positive/Negative/Neutral), Out of Scope (three items minimum), Open Questions.`,
|
|
146
|
+
{ label: 'bezalel:synthesise', phase: 'Synthesise', agentType: 'bezalel' }
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
decision: args.decision,
|
|
151
|
+
winner: winner.proposal.title,
|
|
152
|
+
ranked_proposals: ranked.map(r => ({ title: r.proposal.title, score: r.total_score })),
|
|
153
|
+
final_architecture: synthesis,
|
|
154
|
+
all_proposals: valid,
|
|
155
|
+
all_impacts: impacts.filter(Boolean),
|
|
156
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
// mishkan-codebase-audit — fan-out + adversarial verify across a codebase.
|
|
2
|
+
//
|
|
3
|
+
// The MISHKAN-shaped equivalent of the "codebase-wide bug sweep" and
|
|
4
|
+
// "security audit" use cases Anthropic documents. Spawns the right
|
|
5
|
+
// auditor per file-class in parallel, then 3-vote adversarially verifies
|
|
6
|
+
// each finding to drop confident hallucinations before reporting.
|
|
7
|
+
//
|
|
8
|
+
// Patterns: multi-modal sweep (one auditor lens per file class) +
|
|
9
|
+
// fan-out + adversarial verify (2/3-refute kills) + barrier (dedup before
|
|
10
|
+
// the verify stage so duplicate findings across auditors merge first).
|
|
11
|
+
//
|
|
12
|
+
// Args: {
|
|
13
|
+
// project_root: "/path/to/project",
|
|
14
|
+
// lenses: ["security", "bugs", "perf", "a11y", "contract"] // pick subset
|
|
15
|
+
// target_glob: "src/**/*.{ts,tsx,py,php}" // optional; default: src/**
|
|
16
|
+
// max_files: 200 // cap for cost control
|
|
17
|
+
// }
|
|
18
|
+
|
|
19
|
+
export const meta = {
|
|
20
|
+
name: 'mishkan-codebase-audit',
|
|
21
|
+
description: 'Multi-lens sweep across a codebase. Per-file findings from the relevant Mishmar / Yasad / Panim specialists, then 3-vote adversarial verify before surfacing.',
|
|
22
|
+
whenToUse: 'Periodic project-wide audits, pre-release reviews, post-incident hardening passes. Not for routine PR review (use mishkan-release-readiness or Task delegation).',
|
|
23
|
+
phases: [
|
|
24
|
+
{ title: 'Discover', detail: 'enumerate files matching target_glob' },
|
|
25
|
+
{ title: 'Audit', detail: 'one auditor per lens, parallel per file' },
|
|
26
|
+
{ title: 'Dedup', detail: 'merge findings hitting the same file:line across lenses' },
|
|
27
|
+
{ title: 'Verify', detail: '3-vote adversarial; 2/3 refute → drop' },
|
|
28
|
+
{ title: 'Report', detail: 'aggregate by severity + anchor' },
|
|
29
|
+
],
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!args?.project_root || !args?.lenses || args.lenses.length === 0) {
|
|
33
|
+
throw new Error('mishkan-codebase-audit requires args: { project_root, lenses: [...], target_glob?, max_files? }')
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const MAX_FILES = args.max_files || 200
|
|
37
|
+
const GLOB = args.target_glob || 'src/**/*'
|
|
38
|
+
|
|
39
|
+
// Each lens maps to the right auditor agent and anchor vocabulary.
|
|
40
|
+
const LENSES = {
|
|
41
|
+
security: { agent: 'ira', anchor: 'OWASP / CWE', craft: 'ira-code-security-craft' },
|
|
42
|
+
bugs: { agent: 'uriah', anchor: 'CONTRACT invariants / tests', craft: 'qa-evaluation-craft' },
|
|
43
|
+
perf: { agent: 'shallum', anchor: 'EXPLAIN / Core Web Vitals', craft: 'shallum-database-craft' },
|
|
44
|
+
a11y: { agent: 'asaph', anchor: 'WCAG 2.2 SC', craft: 'asaph-a11y-seo-craft' },
|
|
45
|
+
contract: { agent: 'zadok', anchor: 'CONTRACT.md', craft: 'zadok-contract-craft' },
|
|
46
|
+
surface: { agent: 'joab', anchor: 'OWASP API Top 10', craft: 'joab-app-security-craft' },
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const FINDING_SCHEMA = {
|
|
50
|
+
type: 'object',
|
|
51
|
+
required: ['location', 'severity', 'rule_violated', 'remediation', 'lens'],
|
|
52
|
+
properties: {
|
|
53
|
+
location: { type: 'string', description: 'file:line or file:line-range' },
|
|
54
|
+
severity: { enum: ['critical', 'high', 'medium', 'low'] },
|
|
55
|
+
rule_violated: { type: 'string' },
|
|
56
|
+
remediation: { type: 'string' },
|
|
57
|
+
lens: { type: 'string' },
|
|
58
|
+
confidence: { enum: ['high', 'medium', 'low'] },
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const FINDINGS_LIST_SCHEMA = {
|
|
63
|
+
type: 'object',
|
|
64
|
+
required: ['findings'],
|
|
65
|
+
properties: {
|
|
66
|
+
findings: { type: 'array', items: FINDING_SCHEMA },
|
|
67
|
+
},
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const VERDICT_SCHEMA = {
|
|
71
|
+
type: 'object',
|
|
72
|
+
required: ['refuted', 'reason'],
|
|
73
|
+
properties: { refuted: { type: 'boolean' }, reason: { type: 'string' } },
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// --- Stage 1: Discover -------------------------------------------------
|
|
77
|
+
phase('Discover')
|
|
78
|
+
const discovery = await agent(
|
|
79
|
+
`List files under ${args.project_root} matching glob ${JSON.stringify(GLOB)}. ` +
|
|
80
|
+
`Return up to ${MAX_FILES} files; if more match, prefer files modified most recently. ` +
|
|
81
|
+
`Output a JSON array of relative paths.`,
|
|
82
|
+
{
|
|
83
|
+
label: 'discover:files',
|
|
84
|
+
phase: 'Discover',
|
|
85
|
+
agentType: 'Explore',
|
|
86
|
+
schema: { type: 'object', required: ['files'], properties: { files: { type: 'array', items: { type: 'string' } } } },
|
|
87
|
+
}
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
const files = (discovery?.files || []).slice(0, MAX_FILES)
|
|
91
|
+
log(`${files.length} files in scope across ${args.lenses.length} lenses → ${files.length * args.lenses.length} audit calls`)
|
|
92
|
+
|
|
93
|
+
if (files.length === 0) {
|
|
94
|
+
return { findings: [], stats: { files: 0, lenses: args.lenses }, note: 'no files matched' }
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// --- Stage 2: Audit ----------------------------------------------------
|
|
98
|
+
// Pipeline by file: each file goes through all selected lenses in parallel;
|
|
99
|
+
// audits land into the dedup barrier as they arrive.
|
|
100
|
+
phase('Audit')
|
|
101
|
+
const auditTasks = []
|
|
102
|
+
for (const file of files) {
|
|
103
|
+
for (const lensName of args.lenses) {
|
|
104
|
+
const lens = LENSES[lensName]
|
|
105
|
+
if (!lens) continue
|
|
106
|
+
auditTasks.push({ file, lensName, lens })
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const rawFindings = await parallel(auditTasks.map(({ file, lensName, lens }) => () =>
|
|
111
|
+
agent(
|
|
112
|
+
`Act as ${lens.agent}. Apply ${lens.craft}. Audit ${args.project_root}/${file} for ${lensName} issues. ` +
|
|
113
|
+
`Anchor every finding to ${lens.anchor}. Anchorless findings are not raised. ` +
|
|
114
|
+
`Return a (possibly empty) findings list — empty is a valid outcome.`,
|
|
115
|
+
{
|
|
116
|
+
label: `${lens.agent}:${lensName}:${file.slice(-32)}`,
|
|
117
|
+
phase: 'Audit',
|
|
118
|
+
agentType: lens.agent,
|
|
119
|
+
schema: FINDINGS_LIST_SCHEMA,
|
|
120
|
+
}
|
|
121
|
+
)
|
|
122
|
+
))
|
|
123
|
+
|
|
124
|
+
const allFindings = rawFindings
|
|
125
|
+
.filter(Boolean)
|
|
126
|
+
.flatMap(r => r.findings || [])
|
|
127
|
+
.map((f, i) => ({ ...f, _id: i }))
|
|
128
|
+
|
|
129
|
+
log(`Audit produced ${allFindings.length} raw findings`)
|
|
130
|
+
|
|
131
|
+
// --- Stage 3: Dedup ----------------------------------------------------
|
|
132
|
+
// Two lenses can flag the same file:line for related reasons. Merge by
|
|
133
|
+
// (file:line, anchor) — keep the highest-severity version, append cross-lens
|
|
134
|
+
// note.
|
|
135
|
+
phase('Dedup')
|
|
136
|
+
const key = (f) => `${f.location}::${(f.rule_violated || '').slice(0, 40)}`
|
|
137
|
+
const grouped = {}
|
|
138
|
+
for (const f of allFindings) {
|
|
139
|
+
const k = key(f)
|
|
140
|
+
if (!grouped[k]) { grouped[k] = { ...f, lenses: [f.lens] }; continue }
|
|
141
|
+
grouped[k].lenses.push(f.lens)
|
|
142
|
+
const sevOrder = { critical: 0, high: 1, medium: 2, low: 3 }
|
|
143
|
+
if (sevOrder[f.severity] < sevOrder[grouped[k].severity]) {
|
|
144
|
+
grouped[k].severity = f.severity
|
|
145
|
+
grouped[k].rule_violated = f.rule_violated
|
|
146
|
+
grouped[k].remediation = f.remediation
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const deduped = Object.values(grouped)
|
|
150
|
+
log(`After dedup: ${deduped.length} unique findings (collapsed ${allFindings.length - deduped.length})`)
|
|
151
|
+
|
|
152
|
+
// --- Stage 4: Adversarial verify --------------------------------------
|
|
153
|
+
// 3 independent skeptics per finding; 2/3 refute → drop.
|
|
154
|
+
phase('Verify')
|
|
155
|
+
const verified = await parallel(deduped.map((f) => async () => {
|
|
156
|
+
const votes = await parallel([0, 1, 2].map((v) => () => agent(
|
|
157
|
+
`Adversarial verifier ${v + 1}/3. Try to REFUTE this audit finding. Default to refuted=true if uncertain. ` +
|
|
158
|
+
`Read ${args.project_root}/${f.location.split(':')[0]} to confirm. Finding: ${JSON.stringify(f)}. ` +
|
|
159
|
+
`Refute if: (a) the anchor does not actually apply at this location, (b) the failure mode is not concretely ` +
|
|
160
|
+
`describable, or (c) the remediation does not address the named anchor.`,
|
|
161
|
+
{ label: `verify:${f.location.slice(-32)}:v${v + 1}`, phase: 'Verify',
|
|
162
|
+
agentType: 'hushai', schema: VERDICT_SCHEMA }
|
|
163
|
+
)))
|
|
164
|
+
const refuted = votes.filter(Boolean).filter(v => v.refuted).length
|
|
165
|
+
return refuted >= 2 ? null : f
|
|
166
|
+
}))
|
|
167
|
+
|
|
168
|
+
const confirmed = verified.filter(Boolean)
|
|
169
|
+
const killed = deduped.length - confirmed.length
|
|
170
|
+
log(`Verify: ${confirmed.length} confirmed, ${killed} refuted by adversarial vote`)
|
|
171
|
+
|
|
172
|
+
// --- Stage 5: Report --------------------------------------------------
|
|
173
|
+
phase('Report')
|
|
174
|
+
const bySeverity = { critical: [], high: [], medium: [], low: [] }
|
|
175
|
+
for (const f of confirmed) (bySeverity[f.severity] || bySeverity.low).push(f)
|
|
176
|
+
|
|
177
|
+
return {
|
|
178
|
+
findings_by_severity: bySeverity,
|
|
179
|
+
totals: {
|
|
180
|
+
files_audited: files.length,
|
|
181
|
+
lenses: args.lenses,
|
|
182
|
+
raw_findings: allFindings.length,
|
|
183
|
+
after_dedup: deduped.length,
|
|
184
|
+
after_verify: confirmed.length,
|
|
185
|
+
refuted_by_verify: killed,
|
|
186
|
+
},
|
|
187
|
+
next_actions: confirmed.filter(f => f.severity === 'critical' || f.severity === 'high'),
|
|
188
|
+
}
|