When a subagent or tri-lane lane reports back, attaches git's own account of the worktrees and branches the report names (commits ahead, diff size, uncommitted…

The complete skill library that Cure Consulting Group uses to build apps, platforms, and products. These skills encode our standards, frameworks, and processes — so every project ships with the same level of rigor.
Now available as a Claude Code Plugin — install once, get auto-updates across all projects.
ProductEngineeringSkills/
├── .claude-plugin/ # Plugin manifest
│ └── plugin.json
├── skills/{domain}/ # 80 skills, organized by domain (engineering/platform/product/business/marketing/security/legal)
│ ├── sdlc/
│ ├── android-feature-scaffold/
│ ├── incident-response/ # NEW
│ ├── accessibility-audit/ # NEW
│ ├── performance-review/ # NEW
│ ├── database-architect/ # NEW
│ ├── infrastructure-scaffold/ # NEW
│ ├── project-bootstrap/
│ ├── e2e-testing/
│ ├── test-accounts/
│ ├── uat/
│ ├── compliance-architect/
│ ├── data-migration/
│ ├── feature-flags/
│ ├── release-management/
│ ├── observability/
│ ├── client-handoff/
│ ├── llmops/
│ ├── disaster-recovery/
│ ├── dora-metrics/
│ ├── design-system/
│ ├── client-communication/
│ ├── i18n/
│ ├── notification-architect/
│ ├── offline-first/
│ ├── chaos-engineering/
│ ├── edge-computing/
│ ├── finops/
│ ├── micro-frontends/
│ ├── growth-engineering/
│ ├── green-software/
│ ├── proposal-generator/
│ ├── api-gateway/
│ ├── ... (75 total — see docs/OVERVIEW.md for full inventory)
│ └── legal-doc-scaffold/
├── agents/ # 35 custom subagent definitions
├── personas/ # 4 cross-domain engagement archetypes
│ ├── code-reviewer.md # Security + quality review agent
│ ├── project-bootstrapper.md # New project setup agent
│ ├── test-runner.md # Execute test suites, report coverage
│ ├── pr-reviewer.md # Automated PR diff review
│ ├── refactor-assistant.md # Safe refactoring with test validation
│ ├── ci-debugger.md # Diagnose failed CI/CD runs
│ ├── release-coordinator.md # Version bump, changelog, deploy validation
│ ├── doc-generator.md # API docs, ADRs, changelogs from code
│ ├── codebase-explainer.md # Onboarding — explain architecture, trace flows
│ ├── migration-validator.md # Database migration safety checks
│ ├── deployment-validator.md # Pre-deployment checklist validation
│ ├── dependency-auditor.md # Vulnerability and outdated package audit
│ ├── api-validator.md # OpenAPI spec and contract validation
│ ├── product-analyst.md # Feature adoption, analytics instrumentation
│ ├── ux-researcher.md # Usability analysis, friction mapping
│ ├── roadmap-strategist.md # RICE scoring, dependency mapping, roadmaps
│ ├── competitive-intel.md # Feature matrices, positioning, moat analysis
│ ├── content-strategist.md # Editorial calendars, SEO, content briefs
│ ├── campaign-analyst.md # Attribution, funnel analysis, channel ROI
│ ├── brand-guardian.md # Voice/tone, visual identity, microcopy audit
│ ├── growth-analyst.md # Activation, retention, viral mechanics
│ ├── financial-analyst.md # Revenue forecasts, unit economics, scenarios
│ ├── market-intelligence.md # TAM/SAM/SOM, trends, market timing
│ ├── investor-relations.md # Board updates, KPIs, fundraising narratives
│ ├── contract-reviewer.md # SOW/contract risk, terms, IP review
│ ├── data-analyst.md # Schema exploration, queries, data quality
│ ├── metrics-dashboard.md # KPI definitions, SLOs, dashboard wireframes
│ ├── ab-test-analyst.md # Experiment design, statistical analysis
│ ├── qa-engineer.md # Test planning, edge cases, regression, quality gates
│ ├── accessibility-checker.md # WCAG 2.2 automated compliance
│ └── firebase-security-auditor.md # Firestore rules and Functions audit
├── hooks/ # Multi-layer automated enforcement
│ └── hooks.json # Command + Prompt hooks (9 event types)
├── rules/ # 11 path-specific coding standards
│ ├── android.md # Loads for *.kt files
│ ├── ios.md # Loads for *.swift files
│ ├── web.md # Loads for *.ts/*.tsx files
│ ├── firebase.md # Loads for functions/**
│ ├── python.md # Loads for *.py files
│ ├── go.md # Loads for *.go files
│ ├── rust.md # Loads for *.rs files
│ ├── sql.md # Loads for *.sql, migrations/**
│ ├── docker.md # Loads for Dockerfile, *.dockerfile
│ ├── terraform.md # Loads for *.tf, *.tfvars
│ └── cicd.md # Loads for .github/workflows/**
├── output-styles/ # 9 custom output formatting styles
│ ├── prd/ # Product docs (PRDs, GTM, research)
│ ├── code-generation/ # Code scaffolds and implementations
│ ├── financial-analysis/ # Cost models, SaaS metrics
│ ├── audit-report/ # Audits, reviews, compliance
│ ├── api-specification/ # OpenAPI specs, endpoint docs
│ ├── architecture-decision/ # ADRs, RFCs, trade-off matrices
│ ├── runbook/ # Incident runbooks, DR procedures
│ ├── test-plan/ # Test plans, coverage reports
│ └── monitoring-alert/ # Alert definitions, thresholds
├── .mcp.json # MCP server configs (GitHub, Sentry, Firestore, PostgreSQL)
├── .lsp.json # LSP server configs (TypeScript, Python/Pyright)
├── marketplace.json # Plugin marketplace manifest
├── settings.json # Default permission rules
├── claude-commands/ # Legacy format (backwards compat, 64 files)
├── gemini skills/ # Google Gemini skills (.skill ZIP)
├── CLAUDE.md # Project instructions (Claude)
├── GEMINI.md # Project instructions (Gemini CLI)
├── AGENT-GUIDE.md # How to structure prompts for agents & skills
├── setup.sh # Setup script for Antigravity & other projects
└── README.md
Install the plugin as an npm package from GitHub Packages. This is the easiest way to keep all your projects up to date.
1. Authenticate with GitHub Packages (one-time setup):
# Create a Personal Access Token (PAT) with read:packages scope at
# https://github.com/settings/tokens, then:
npm login --scope=@cure-consulting-group --registry=https://npm.pkg.github.com
Or add to your project's .npmrc:
@cure-consulting-group:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
2. Install in your project:
npm install @cure-consulting-group/product-engineering-skills
The postinstall script automatically:
~/.claude/plugins/ProductEngineeringSkills~/.claude/settings.jsonAll 80 skills, 39 agents, 4 personas, hooks, rules, and output styles are immediately available.
3. Enable auto-updates with Dependabot (recommended):
Add .github/dependabot.yml to your project (or run setup.sh which does this automatically):
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"
allow:
- dependency-name: "@cure-consulting-group/product-engineering-skills"
labels:
- "dependencies"
- "skills-update"
commit-message:
prefix: "chore"
include: "scope"
Dependabot will open a PR in your project whenever a new version is published. Merge it and every agent on that project gets the updated skills.
4. Manual update:
npm update @cure-consulting-group/product-engineering-skills
# Load the plugin during a session
claude --plugin-dir /path/to/ProductEngineeringSkills
# Or for development/testing
claude --plugin-dir ./ProductEngineeringSkills
Once loaded, all skills are available as namespaced commands:
/cure-product-engineering:sdlc
/cure-product-engineering:feature-audit
/cure-product-engineering:android-feature-scaffold
/cure-product-engineering:incident-response
/cure-product-engineering:accessibility-audit
Hooks, agents, rules, output styles, and MCP servers are all included automatically.
The fastest way to onboard any project:
# From the target project directory
/path/to/ProductEngineeringSkills/setup.sh
# Or specify the project path
/path/to/ProductEngineeringSkills/setup.sh /path/to/antigravity-app
# Install globally for ALL projects
/path/to/ProductEngineeringSkills/setup.sh --global
# Legacy mode (just copy skills, no hooks/agents)
/path/to/ProductEngineeringSkills/setup.sh --legacy
The setup script will:
~/.claude/plugins/.claude/settings.local.json to .gitignore# Add the Cure Consulting marketplace
claude marketplace add https://github.com/Cure-Consulting-Group/ProductEngineeringSkills/marketplace.json
# Install the plugin
claude plugin install cure-product-engineering
Copy the claude-commands/ files into your project's .claude/commands/ directory:
cp claude-commands/*.md /path/to/your/project/.claude/commands/
Then use them as slash commands:
/sdlc — Generate SDLC artifacts
/android-feature-scaffold — Scaffold an Android feature module
/feature-audit — Audit a completed feature
Import the .skill files from gemini skills/ into your Gemini workspace. Each .skill file is a ZIP archive containing:
SKILL.md — The main skill definitionreferences/ — Supporting documents and templates| Skill | What It Does | Auto-Invoked? |
|---|---|---|
| product-manager | OKRs, roadmaps, RICE prioritization, feature briefs | Yes |
| product-design | Apple HIG, Material Design 3, design tokens, accessibility-first | Yes |
| market-research | TAM/SAM/SOM, competitive analysis, ICP definition (read-only) | Yes |
| go-to-market | GTM plans, launch strategy, channel selection, growth playbooks | Yes |
| product-marketing | Brand strategy, messaging frameworks, campaigns | Yes |
| customer-onboarding | Activation flows, empty states, email sequences, retention | Yes |
| seo-content-engine | Technical SEO, structured data, content strategy | Yes |
| Skill | What It Does | Auto-Invoked? |
|---|---|---|
| sdlc | PRDs, ADRs, RFCs, Epics, Stories, Task specs — full SDLC | Yes |
| android-feature-scaffold | Clean Architecture Android scaffolding (MVI, Compose, Hilt) | Yes |
| ios-architect | Swift/SwiftUI Clean Architecture, MVVM, structured concurrency | Yes |
| nextjs-feature-scaffold | App Router, Server/Client components, Tailwind patterns | Yes |
| firebase-architect | Firestore schema, security rules, Cloud Functions | Yes |
| api-architect | REST/GraphQL design, versioning, auth, rate limiting | Yes |
| api-gateway | API gateway and BFF layers, rate limiting, GraphQL federation | Yes |
| stripe-integration | Stripe payments + subscriptions via Firebase Functions | Yes |
| ai-feature-builder | LLM integration, RAG pipelines, prompt engineering | Yes |
| llmops | LLM operationalization — prompt versioning, eval pipelines, cost optimization, guardrails | Yes |
| database-architect | Schema design, migrations, indexing for Firestore/PostgreSQL/SQLite | Yes |
| data-migration | ETL pipelines, zero-downtime cutover, validation, rollback strategies | Yes |
| infrastructure-scaffold | Cloud infra configs for Firebase, GCP, Vercel, Docker | Yes |
| edge-computing | Edge functions, CDN strategies, cache invalidation, edge middleware | Yes |
| micro-frontends | Module federation, monorepo management, independent deployments | Yes |
| offline-first | Offline-first architecture, sync strategies, conflict resolution, optimistic UI | Yes |
| i18n | Internationalization — string extraction, RTL, locale-aware formatting, translation workflows | Yes |
| notification-architect | Push (FCM/APNs), in-app messaging, email, preference management | Yes |
| Skill | What It Does | Auto-Invoked? |
|---|---|---|
| feature-audit | 5-phase post-completion audit with scored gap report | Yes (read-only, forked) |
| testing-strategy | Testing pyramid, platform standards, coverage rules | Yes |
| e2e-testing | E2E test suites with page objects, visual regression, CI integration | Yes |
| test-accounts | Test user personas, seed data scripts, environment credentials | Yes |
| uat | UAT plans, acceptance criteria checklists, go/no-go release gates | Yes |
| security-review | OWASP checklist, auth/data/API/mobile/web security | Yes (read-only, forked) |
| compliance-architect | HIPAA, COPPA, GDPR, PCI compliance frameworks, consent flows, audit trails | Yes |
| accessibility-audit | WCAG 2.2 compliance, screen readers, inclusive design | Yes (read-only, forked) |
| performance-review | Performance budgets, load testing, optimization strategies | Yes |
| chaos-engineering | Resilience testing, failure injection, graceful degradation, game days | Yes |
| green-software | Sustainable software practices, carbon-aware computing, energy efficiency | Yes |
| Skill | What It Does | Auto-Invoked? |
|---|---|---|
| project-bootstrap | Bootstrap repo with CLAUDE.md + STATE.md via codebase inspection and developer interview | Yes |
| project-manager | Sprint planning, RACI, risk registers, retrospectives | Yes |
| ci-cd-pipeline | GitHub Actions, build/test/deploy, environments, secrets | Yes |
| release-management | App store submissions, staged rollouts, versioning, ASO, changelogs | Yes |
| feature-flags | Progressive rollouts, A/B testing, kill switches, experimentation frameworks | Yes |
| observability | Structured logging, distributed tracing, alerting, SLO/SLI, dashboards | Yes |
| dora-metrics | DORA and SPACE metrics — deployment frequency, lead time, MTTR, developer experience | Yes |
| analytics-implementation | Event taxonomy, tracking plans, funnels, dashboards | Yes |
| incident-response | Runbooks, severity classification, post-mortems, escalation | Yes |
| disaster-recovery | DR and business continuity — RTO/RPO, backup strategies, failover, DR testing | Yes |
| growth-engineering | Activation funnels, referral programs, lifecycle automation, PLG patterns | Yes |
| design-system | Design tokens, component libraries, Storybook/Catalog, cross-platform consistency | Yes |
| Skill | What It Does | Auto-Invoked? |
|---|---|---|
| engineering-cost-model | Project estimates, infrastructure costs, build vs buy | Yes (read-only) |
| saas-financial-model | Unit economics, MRR/ARR, pricing tiers, break-even | Yes (read-only) |
| finops | Cloud cost optimization, budget alerts, resource right-sizing, FinOps practices | Yes |
| investor-reporting | Investor updates, board decks, portfolio financials, cap table, runway modeling | Yes |
| fundraising-materials | Pitch decks, data rooms, investor updates, cap table scenarios, fundraising pipeline | Yes |
| burn-rate-tracker | Burn rates, runway scenarios, break-even analysis, cash flow projections | Yes |
| legal-doc-scaffold | ToS, Privacy Policy, SOW, NDA scaffolds | No (manual only) |
| Skill | What It Does | Auto-Invoked? |
|---|---|---|
| portfolio-registry | Product portfolio registry — single source of truth for all products, stacks, teams, stages | Yes |
| technology-radar | ThoughtWorks-style technology radar — Adopt/Trial/Assess/Hold across the portfolio | Yes |
| Skill | What It Does | Auto-Invoked? |
|---|---|---|
| client-handoff | Handoff packages, runbooks, credential transfers, maintenance SLAs, knowledge transfer | Yes |
| client-communication | Sprint demo scripts, stakeholder updates, risk escalation, executive summaries | Yes |
| proposal-generator | Consulting proposals, SOWs, milestone pricing, engagement structure | No (manual only) |
| Skill | What It Does | Auto-Invoked? |
|---|---|---|
| android-design-expert | Material Design 3 — dynamic color, component tokens, adaptive layouts, motion, Compose patterns | Yes |
| ios-design-expert | Apple HIG — SF Symbols, Dynamic Type, navigation patterns, SwiftUI components | Yes |
| web-design-expert | Responsive design, CSS architecture, design tokens, container queries, accessibility-first, Tailwind | Yes |
| stitch-design | AI-native UI design via Stitch MCP — vibe design, mockups, screen generation, design tokens, component export | Yes |
The library ships a standard maintenance loop (self-provisioned to .claude/loop.md on first session start — run bare /loop to use it), Recurring Mode sections in the goal-shaped skills (finops, burn-rate-tracker, investor-reporting, security-review, and others), and copy-paste cloud-routine recipes in docs/AUTOMATION.md. Library upkeep cadence: docs/MAINTENANCE.md. Mechanism selection and unattended-run guardrails: /cure-product-engineering:engagement-automation.
The plugin ships command and prompt hooks across 9 event types: SessionStart, PreCompact, PostCompact, ConfigChange, PostToolUseFailure, UserPromptSubmit, PreToolUse, Stop, SubagentStop. Highlights: a Stop-hook quality gate (blocks "done" without verification), a PreToolUse static security guard on skill/agent/persona files, and a ConfigChange audit trigger when skill files change mid-session.
New in v4.0: Hooks now suggest and auto-trigger agents based on context. Every code edit, test run, deployment, and PR action recommends the most relevant agent(s).
| Hook | Event | What It Does | Agent Integration |
|---|---|---|---|
| Welcome | SessionStart | Confirms plugin loaded with counts; full inventory stays in docs/OVERVIEW.md | Points to inventory |
| Git status | SessionStart | Reports current branch, uncommitted changes, last commit | — |
| Dependency check | SessionStart | Detects outdated packages | Suggests dependency-auditor |
| Code edit advisor | PostToolUse (Edit/Write) | Context-aware suggestions based on file type (.kt, .swift, .ts, .sql, .tf, etc.) | Suggests code-reviewer, test-runner, brand-guardian, migration-validator |
| Command advisor | PostToolUse (Bash) | Post-action guidance for tests, installs, deploys, PRs, releases | Suggests ci-debugger, dependency-auditor, pr-reviewer, release-coordinator |
| Failure recovery | PostToolUseFailure | Diagnoses failure type and suggests fix approach | Auto-suggests ci-debugger, deployment-validator, dependency-auditor |
| Destructive prompt guard | UserPromptSubmit | Detects destructive operations in prompts | Blocks and confirms |
| Protected files | PreToolUse (Edit/Write) | Blocks edits to .env, lock files, credentials, tfstate | — |
| Dangerous commands | PreToolUse (Bash) | Blocks force push, destructive rm, DROP TABLE, prod deploys | — |
| Context re-injection | PreCompact | Re-injects all 80 skills, 39 agents, 4 personas, and Cure standards | Full inventory preserved |
| Post-compact restore | PostCompact | Confirms context restored with agent availability | — |
| Subagent start banner | SubagentStart | Announces agent with role, standards, and companion agents | Lists companion agents |
| Subagent completion | SubagentStop | Suggests follow-up agents (test-runner, code-reviewer, pr-reviewer) | Agent chaining |
| Task quality check | TaskCompleted | Validates tests, security, docs, brand consistency | Suggests test-runner, code-reviewer, doc-generator, brand-guardian |
| Hook | Event | What It Does | Agent Integration |
|---|---|---|---|
| Code quality gate | PreToolUse (Edit/Write) | Haiku validates: no secrets, no debug logs, no disabled tests, no any types | — |
| Deployment safety | PreToolUse (Bash) | Haiku validates: blocks production deployments outside CI/CD | — |
| Intent classifier | UserPromptSubmit | Haiku classifies prompt intent and suggests the most relevant agent(s) from all 30 | Maps prompts → agents with confidence scores |
| Hook | Event | What It Does | Agent Integration |
|---|---|---|---|
| Completion validator | Stop | Validates: tests for new code, security review for sensitive changes, rollback for migrations, docs for features, brand consistency for UI, analytics for events, API contracts | Suggests specific agents for each gap found |
Pre-configured MCP servers in .mcp.json:
| Server | Type | What It Does |
|---|---|---|
| GitHub | HTTP | PR management, issue tracking, code search |
| Sentry | HTTP | Error monitoring, issue tracking, release health |
| Firestore | stdio | Direct database queries, schema inspection |
| PostgreSQL | stdio | Database queries, schema inspection, migrations |
Pre-configured LSP servers in .lsp.json:
| Server | Language | What It Provides |
|---|---|---|
| TypeScript | .ts, .tsx, .js | Type checking, auto-imports, refactoring, go-to-definition |
| Python (Pyright) | *.py | Static type analysis, import resolution, error diagnostics |
Custom output formatting for different artifact types:
| Style | Used By | Key Rules |
|---|---|---|
| prd | Product skills (PRDs, GTM, research) | Numbered sections, decision matrices, executive summaries |
| code-generation | Engineering skills (scaffolds) | File tree first, dependency order, complete runnable code |
| financial-analysis | Business skills (costs, models) | ASCII tables, explicit assumptions, sensitivity analysis |
| audit-report | Quality skills (audits, reviews) | Severity scoring, checklists, remediation with effort estimates |
| api-specification | API design skills | OpenAPI 3.0 blocks, endpoint tables, request/response examples |
| architecture-decision | ADR and RFC skills | Context/decision/consequences format, trade-off matrices |
| runbook | Incident response, disaster recovery | Numbered steps, command blocks, decision trees, escalation paths |
| test-plan | Testing strategy, QA skills | Coverage tables, test case templates, pass/fail criteria |
| monitoring-alert | Observability, incident response | Alert definition tables, threshold rationale, runbook links |
| Agent | Purpose | Tools | Auto-Triggered By |
|---|---|---|---|
| code-reviewer | Security + quality review against Cure standards | Read-only | Stop hook, SubagentStop |
| project-bootstrapper | Set up new projects with correct architecture |
hooks/register.ts 127 lines1import type { On } from 'claude-code'
2
3import { countDirty, extractClaims, formatVerdict, isEmptyCompletion, type Claims, type Evidence } from './claims'
4
5/**
6 * cure-lane-verifier: when a subagent or tri-lane lane reports back, attach
7 * git's own account of the worktrees and branches the report names, so the
8 * report is read against evidence. An empty diff behind a "complete" is
9 * flagged loudly, to the model and to the person.
10 *
11 * Where reports arrive:
12 * - a foreground Agent call's result (`tool.call` for `Agent`): the note
13 * rides as `context`, which the model reads after the result;
14 * - a background agent's hand-back, delivered into the conversation as an
15 * engine attachment or a peer message (`prompt.attachment`,
16 * `session.receive`): the note is appended to the text.
17 *
18 * Read-only: it runs git `rev-parse`, `rev-list`, `diff --shortstat` and
19 * `status`, nothing that writes. Fails open: anything git cannot answer is
20 * reported as "could not verify" and the report passes through unchanged.
21 */
22
23const HANDBACK = /\[Subagent hand-back\]|<task-notification>|<agent-message\b|final report of a subagent/i
24
25export function register(on: On) {
26 on('tool.call', async ($, e, next) => {
27 if (e.tool !== 'Agent') return next(e)
28 const answer = await next(e)
29 if (answer.deny !== undefined || e.run_in_background === true) return answer
30 const text = typeof answer.text === 'string' ? answer.text : safeJson(answer.result)
31 const note = await verify($, text)
32 if (!note) return answer
33 return { ...answer, context: [...(answer.context ?? []), note] }
34 })
35
36 on('prompt.attachment', async ($, e, next) => {
37 if (e.origin.kind !== 'engine' || !HANDBACK.test(e.text)) return next(e)
38 const note = await verify($, e.text)
39 return note ? next({ ...e, text: `${e.text}\n\n${note}` }) : next(e)
40 })
41
42 on('session.receive', async ($, e, next) => {
43 if (!HANDBACK.test(e.text)) return next(e)
44 const note = await verify($, e.text)
45 return note ? next({ ...e, text: `${e.text}\n\n${note}` }) : next(e)
46 })
47}
48
49const safeJson = (v: unknown) => {
50 try {
51 return typeof v === 'string' ? v : JSON.stringify(v) ?? ''
52 } catch {
53 return ''
54 }
55}
56
57/** The verification note for a report's text, '' when it names nothing git can check. */
58async function verify($: any, text: string): Promise<string> {
59 const claims = extractClaims(text)
60 if (claims.paths.length === 0 && claims.branches.length === 0) return ''
61 const evidence = await gather($, claims)
62 const note = formatVerdict(claims, evidence)
63 if (note && isEmptyCompletion(claims.claimsDone, evidence)) {
64 $.ui.toast('cure-lane-verifier: a report claims completion but its branch is empty', { timeoutMs: 10000 })
65 }
66 if (note) $.ui.log(note.split('\n')[0])
67 return note
68}
69
70async function git($: any, cwd: string, ...args: string[]) {
71 return $.process.run(['git', '-C', cwd, ...args], { timeoutMs: 10000 })
72}
73
74async function baseOf($: any, cwd: string): Promise<string> {
75 const head = await git($, cwd, 'rev-parse', '--abbrev-ref', 'origin/HEAD')
76 if (head.exitCode === 0 && head.stdout.trim()) return head.stdout.trim()
77 for (const b of ['origin/main', 'origin/master', 'main', 'master']) {
78 if ((await git($, cwd, 'rev-parse', '--verify', '--quiet', b)).exitCode === 0) return b
79 }
80 return 'HEAD'
81}
82
83async function measure($: any, cwd: string, ref: string, target: string, withDirty: boolean): Promise<Evidence> {
84 const base = await baseOf($, cwd)
85 const ahead = await git($, cwd, 'rev-list', '--count', `${base}..${ref}`)
86 const stat = await git($, cwd, 'diff', '--shortstat', `${base}...${ref}`)
87 if (ahead.exitCode !== 0 || stat.exitCode !== 0) {
88 return { target, ahead: 0, shortstat: '', dirty: 0, base, error: (ahead.stderr || stat.stderr).trim().split('\n')[0] || 'git failed' }
89 }
90 const dirty = withDirty ? countDirty((await git($, cwd, 'status', '--porcelain')).stdout) : 0
91 return { target, ahead: Number(ahead.stdout.trim()) || 0, shortstat: stat.stdout.trim(), dirty, base }
92}
93
94async function gather($: any, claims: Claims): Promise<Evidence[]> {
95 const out: Evidence[] = []
96 const seenTops = new Set<string>()
97 for (const path of claims.paths) {
98 try {
99 if (!(await $.fs.exists(path))) continue
100 const top = await git($, path, 'rev-parse', '--show-toplevel')
101 if (top.exitCode !== 0) continue
102 const root = top.stdout.trim()
103 if (seenTops.has(root)) continue
104 seenTops.add(root)
105 out.push({ ...(await measure($, root, 'HEAD', root, true)), kind: 'path' })
106 } catch (err) {
107 out.push({ target: path, ahead: 0, shortstat: '', dirty: 0, base: '?', error: String(err) })
108 }
109 }
110 if (claims.branches.length > 0) {
111 const cwd = await $.session.root()
112 for (const branch of claims.branches) {
113 try {
114 const exists = await git($, cwd, 'rev-parse', '--verify', '--quiet', branch)
115 if (exists.exitCode !== 0) {
116 out.push({ target: branch, ahead: 0, shortstat: '', dirty: 0, base: '?', error: `no branch \`${branch}\` in ${cwd.split('/').pop()}` })
117 continue
118 }
119 out.push({ ...(await measure($, cwd, branch, branch, false)), kind: 'branch' })
120 } catch (err) {
121 out.push({ target: branch, ahead: 0, shortstat: '', dirty: 0, base: '?', error: String(err) })
122 }
123 }
124 }
125 return out
126}
127hooks/claims.ts 100 lines1/**
2 * What a subagent or lane report claims about where its work landed, and the
3 * verdict once git has been asked. Pure: no I/O here, so it is all testable.
4 *
5 * The defect this exists for (memory: verify-lane-output-by-mutation): a lane
6 * reported `complete` with 42 passing tests while its branch diff was empty.
7 * A report is a claim; the branch is the evidence.
8 */
9
10export type Claims = {
11 /** Absolute paths the report names that may be worktrees or repos. */
12 paths: string[]
13 /** Branch names the report names (`lane/<task>`, or "branch `x`"). */
14 branches: string[]
15 /** The report says the work is finished. */
16 claimsDone: boolean
17}
18
19const MAX_TARGETS = 5
20
21/** Paths, branches and a completion claim, read off a report's text. */
22export function extractClaims(text: string): Claims {
23 const paths = new Set<string>()
24 for (const m of text.matchAll(/(?:^|[\s'"`(\[])(\/(?:Users|private|tmp|home|var|opt|workspace|srv)\/[^\s'"`)\]]+)/g)) {
25 const p = m[1].replace(/[.,:;]+$/, '').replace(/\/+$/, '')
26 // A file path's directory is what git can answer for.
27 paths.add(/\.[A-Za-z0-9]{1,6}$/.test(p.split('/').pop() ?? '') ? p.slice(0, p.lastIndexOf('/')) : p)
28 }
29 const branches = new Set<string>()
30 for (const m of text.matchAll(/\b(lane\/[A-Za-z0-9._\/-]+[A-Za-z0-9])/g)) branches.add(m[1])
31 for (const m of text.matchAll(/\bbranch(?:es)?\s*[:=]?\s*`([A-Za-z0-9._\/-]+)`/gi)) branches.add(m[1])
32 for (const m of text.matchAll(/\bon branch\s+([A-Za-z0-9._\/-]*[A-Za-z0-9])/gi)) branches.add(m[1])
33 for (const b of ['main', 'master', 'HEAD']) branches.delete(b)
34
35 const claimsDone =
36 /\b(?:status\s*[:=]\s*)?(?:complete|completed|done|finished|implemented|fixed|shipped|merged-ready)\b/i.test(text) ||
37 /\ball (?:\d+ )?tests? pass(?:ed|es)?\b/i.test(text) ||
38 /\bGAPS:\s*none\b/i.test(text)
39
40 return {
41 paths: [...paths].slice(0, MAX_TARGETS),
42 branches: [...branches].slice(0, MAX_TARGETS),
43 claimsDone,
44 }
45}
46
47export type Evidence = {
48 /** What was checked: a worktree path, or a branch name. */
49 target: string
50 /** A named branch, or a path's checkout (whose dirt may be unrelated work). */
51 kind?: 'branch' | 'path'
52 /** Commits on the target that its base does not have. */
53 ahead: number
54 /** `git diff --shortstat base...target`, '' when there is no diff. */
55 shortstat: string
56 /** Uncommitted files in a worktree (0 for a branch-only target). */
57 dirty: number
58 /** The base compared against (`origin/main`). */
59 base: string
60 /** Set when git could not answer for this target. */
61 error?: string
62}
63
64const isEmpty = (e: Evidence) => e.ahead === 0 && e.dirty === 0 && e.shortstat === ''
65
66/**
67 * Whether the evidence contradicts a completion claim.
68 *
69 * A branch the report names is the strongest claim: if any named branch git
70 * knows is empty, the claim is contradicted, whatever else the report names
71 * (a path to the main checkout can be dirty with someone else's work). With
72 * no branch named, every checked path must be empty.
73 */
74export function isEmptyCompletion(claimsDone: boolean, evidence: readonly Evidence[]): boolean {
75 if (!claimsDone) return false
76 const answered = evidence.filter(e => !e.error)
77 const branches = answered.filter(e => e.kind === 'branch')
78 if (branches.length > 0) return branches.some(isEmpty)
79 return answered.length > 0 && answered.every(isEmpty)
80}
81
82/** The verification note: one line per target, and a loud first line on an empty completion. */
83export function formatVerdict(claims: Claims, evidence: readonly Evidence[]): string {
84 if (evidence.length === 0) return ''
85 const lines = evidence.map(e =>
86 e.error
87 ? `- ${e.target}: could not verify (${e.error})`
88 : `- ${e.target}: ${e.ahead} commit(s) ahead of ${e.base}; ${e.shortstat || 'no committed diff'}${e.dirty ? `; ${e.dirty} uncommitted file(s)` : ''}`,
89 )
90 const head = isEmptyCompletion(claims.claimsDone, evidence)
91 ? 'cure-lane-verifier: EMPTY COMPLETION [ALARM]. The report claims completion, but git shows 0 commits and no diff on what it names. HALT WORKFLOW — inspect the target branch/worktree before proceeding.'
92 : 'cure-lane-verifier: git evidence for what this report names (read this, not the report, for what landed):'
93 return [head, ...lines].join('\n')
94}
95
96/** Uncommitted file count from `git status --porcelain` output. */
97export function countDirty(porcelain: string): number {
98 return porcelain.split('\n').filter(l => l.trim() !== '').length
99}
100