Skip to content

feat: persona-driven skill architecture — journey engine, PR gate, new skills - #46

Closed
aanishs wants to merge 3 commits into
mainfrom
feat/persona-skill-architecture
Closed

feat: persona-driven skill architecture — journey engine, PR gate, new skills#46
aanishs wants to merge 3 commits into
mainfrom
feat/persona-skill-architecture

Conversation

@aanishs

@aanishs aanishs commented Apr 6, 2026

Copy link
Copy Markdown
Owner

Summary

  • /comply v3.0 rewritten as a journey engine: persona detection (founder/engineer/audit), Framework Advisor with 3 guided questions, 6-milestone routing, and persona-aware recommendations
  • bin/comply-check new PR gate CLI that checks git diffs for PHI patterns, sensitive data exposure, removed security controls, and BAA-requiring dependencies (exit 0/1/2)
  • /comply-policy and /comply-deal new skills for generating personalized policy documents and answering prospect security questionnaires
  • /soc2, /gdpr, /pci-dss enhanced with domain-specific onboarding questions (trust criteria, GDPR roles, SAQ types)
  • /comply-auto deprecated with wrapper pointing to /comply Autopilot mode
  • nist/baa-vendors.json curated list of 13 services requiring Business Associate Agreements

Test plan

  • bun test test/architecture.test.ts — skill count updated 14 → 16, new skills in expected array
  • bun test test/skill-validation.test.ts — template and generated file counts updated
  • bun run gen:skill-docs — all 16 skills regenerate cleanly
  • bin/comply-check --diff HEAD~1 --json — returns findings JSON with severity classification
  • Manual: run /comply in Claude Code, verify persona prompt and journey routing
  • Manual: run /comply-deal, verify questionnaire parsing flow
  • Manual: run /comply-policy, verify policy generation prompt

🤖 Generated with Claude Code

aanishs and others added 3 commits April 3, 2026 19:01
Add plain-english.json with human-readable descriptions and why_it_matters
for all HIPAA-mapped NIST controls. New /comply-explain skill looks up any
control by ID, CFR section, or keyword. Enriches comply-db control --json
with plain English context. Updates comply-assess and comply-auto to show
why_it_matters during interviews.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix journey skip/complete identical status, normalize control ID casing,
error on invalid ID in --json mode, extract findControlInCatalog helper
(DRY), merge dual DB connections, reject unknown config keys, split
getDbPath read/write, revert premature comply-auto deprecation, add
journey + config + invalid-ID tests, fix skill counts to 14, add
plain-english.test.ts to test script.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…w skills

- /comply v3.0: rewritten as journey engine with persona detection,
  Framework Advisor (3 questions), milestone routing, and persona-aware
  recommendations for founders, engineers, and compliance officers
- bin/comply-check: new PR gate CLI that checks diffs for PHI patterns,
  sensitive data, removed security controls, and BAA-requiring dependencies
- nist/baa-vendors.json: curated list of 13 services requiring BAAs
- /comply-policy: new skill for generating personalized policy documents
- /comply-deal: new skill for answering prospect security questionnaires
- /soc2 v3.0: 3 domain-specific questions (trust criteria, audit type, driver)
- /gdpr v3.0: 4 domain-specific questions (role, locations, lawful basis, DPO)
- /pci-dss v3.0: 3 domain-specific questions (card handling, CDE scope, SAQ)
- /comply-auto v3.0: deprecated, points to /comply Autopilot mode
- Updated all comply-auto references across skill templates
- Tests updated: 14 → 16 skills (added comply-deal, comply-policy)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Apr 6, 2026

Copy link
Copy Markdown

Greptile Summary

This PR introduces a persona-driven /comply v3 journey engine, two new skills (/comply-policy, /comply-deal), and a comply-check PR gate CLI. bin/comply-db gains journey milestone management, a user config store, and an enriched control --json output backed by plain-english.json.

Three correctness issues exist in bin/comply-check that should be addressed before wiring it into CI:

  • Removed lines are never scanned, contradicting the documented "removed security controls" feature.
  • A regex parse failure exits 0 rather than 2, making a broken gate indistinguishable from a clean diff.
  • The two primary PHI-detection checks (phi-identifiers, health-data-fields) carry INFO severity, which falls below the default warn threshold, so patient identifiers added to source code do not block the gate.

Confidence Score: 4/5

The journey engine, skills, comply-db additions, and test updates are safe to merge; bin/comply-check has three P1 issues that should be fixed before relying on it in CI.

Three P1 findings all live in the newly introduced bin/comply-check binary and do not affect any existing functionality. They would make the gate miss PHI leakage and security-control removals, and silently succeed when its own check-loading fails — important to fix before CI integration.

bin/comply-check requires attention on all three P1 issues before the gate is wired into CI pipelines.

Important Files Changed

Filename Overview
bin/comply-check New PR-gate CLI; three P1 issues: removed lines unscanned, zero-check failure exits 0, PHI checks below default gate threshold
bin/comply-db Adds journey engine, config store, enriched control --json output, and onboarding schema tables; logic is sound
nist/baa-vendors.json New curated BAA-vendor list with 13 entries; format is consistent with expected interface
nist/plain-english.json Plain-English translations for 59 HIPAA-mapped controls; used by comply-db --json enrichment and comply-check
test/architecture.test.ts Skill count updated 14→16 and expected skill names updated; tests align with new directory layout
test/bin-smoke.test.ts New smoke tests for journey, config, and control --json subcommands; thorough coverage of new comply-db features
test/plain-english.test.ts Validates plain-english.json coverage, orphan-free entries, and no field-copy duplication; solid
test/skill-validation.test.ts Updated to expect 16 templates and 16 generated files; consistent with two new skills added
skills/comply/SKILL.md Rewritten as journey engine with persona detection (founder/engineer/audit) and 6-milestone routing; coherent
skills/comply-deal/SKILL.md New questionnaire-response skill mapping prospect questions to NIST controls and SQLite posture
skills/comply-policy/SKILL.md New policy-document-generation skill personalising templates from vendor_registry and phi_data_flows
package.json Adds comply-check to bin scripts; minor metadata change

Sequence Diagram

sequenceDiagram
    participant CI as CI Pipeline
    participant CC as comply-check
    participant Git as git diff
    participant CR as checks-registry.ts

    CI->>CC: bun comply-check --diff HEAD~1
    CC->>CR: loadCodeChecks() via regex parse
    alt zero checks loaded (order mismatch)
        CR-->>CC: checks = []
        CC-->>CI: WARNING + exit 0 ❌ (should be exit 2)
    else checks loaded OK
        CR-->>CC: code_grep checks[]
    end
    CC->>Git: getDiffFiles() --diff-filter=ACMR
    Git-->>CC: added/modified files, addedLines only
    Note over CC,Git: ❌ deleted lines (-) never collected
    CC->>CC: runCodeChecks(addedLines, checks)
    CC->>CC: checkNewDependencies(package.json addedLines)
    CC->>CC: filter by severity threshold
    Note over CC: ❌ phi-identifiers (INFO) < default warn (HIGH)
    alt gatedFindings.length > 0
        CC-->>CI: findings + exit 1 (blocks PR)
    else
        CC-->>CI: clean + exit 0
    end
Loading

Reviews (1): Last reviewed commit: "feat: persona-driven skill architecture ..." | Re-trigger Greptile

Comment thread bin/comply-check
Comment on lines +163 to +177
// Parse added lines (lines starting with +, excluding +++ header)
const addedLines: { num: number; text: string }[] = [];
let lineNum = 0;
for (const line of diffOutput.split('\n')) {
const hunkMatch = line.match(/^@@ -\d+(?:,\d+)? \+(\d+)/);
if (hunkMatch) {
lineNum = parseInt(hunkMatch[1]) - 1;
continue;
}
if (line.startsWith('+') && !line.startsWith('+++')) {
lineNum++;
addedLines.push({ num: lineNum, text: line.slice(1) });
} else if (!line.startsWith('-')) {
lineNum++;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Removed lines not scanned

getDiffFiles() filters to --diff-filter=ACMR and only parses lines starting with + (added). Lines starting with - are never collected, so runCodeChecks never sees them. The PR description lists "removed security controls" as a checked feature, but deleting an ensureEncrypted() call, a requireMFA() guard, or any audit-logging statement is completely invisible to the gate — it will exit 0 as if nothing was removed.

Comment thread bin/comply-check
Comment on lines +60 to +78
function loadCodeChecks(): CodeCheck[] {
const registryPath = path.join(import.meta.dir, '..', 'frameworks', 'checks-registry.ts');
const content = fs.readFileSync(registryPath, 'utf-8');

// Extract code_grep checks by parsing the array literals
const checks: CodeCheck[] = [];
const checkRegex = /\{\s*id:\s*"([^"]+)"[\s\S]*?category:\s*"([^"]+)"[\s\S]*?description:\s*"([^"]+)"[\s\S]*?type:\s*"code_grep"[\s\S]*?pattern:\s*"([^"]+)"[\s\S]*?severity_default:\s*"([^"]+)"/g;

let match;
while ((match = checkRegex.exec(content)) !== null) {
checks.push({
id: match[1],
category: match[2],
description: match[3],
pattern: match[4],
severity: match[5],
});
}
return checks;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Regex parser silently exits 0 on check-loading failure

loadCodeChecks() parses checks-registry.ts with a regex that requires object properties in a fixed order: id → category → description → type:"code_grep" → pattern → severity_default. If any future code_grep check is written with fields in a different order, the regex produces zero matches. The tool prints a warning but continues and exits 0 — the gate passes every PR as if it were clean, with no way to distinguish this from a real clean run in CI.

The checks.length === 0 guard should exit 2 instead:

Suggested change
function loadCodeChecks(): CodeCheck[] {
const registryPath = path.join(import.meta.dir, '..', 'frameworks', 'checks-registry.ts');
const content = fs.readFileSync(registryPath, 'utf-8');
// Extract code_grep checks by parsing the array literals
const checks: CodeCheck[] = [];
const checkRegex = /\{\s*id:\s*"([^"]+)"[\s\S]*?category:\s*"([^"]+)"[\s\S]*?description:\s*"([^"]+)"[\s\S]*?type:\s*"code_grep"[\s\S]*?pattern:\s*"([^"]+)"[\s\S]*?severity_default:\s*"([^"]+)"/g;
let match;
while ((match = checkRegex.exec(content)) !== null) {
checks.push({
id: match[1],
category: match[2],
description: match[3],
pattern: match[4],
severity: match[5],
});
}
return checks;
if (checks.length === 0) {
console.error('ERROR: No code_grep checks loaded from checks-registry.ts. Cannot run gate.');
process.exit(2);
}

Comment thread bin/comply-check
Comment on lines +41 to +48

const SEVERITY_ORDER: Record<string, number> = {
'INFO': 0, 'LOW': 1, 'MEDIUM': 2, 'HIGH': 3, 'CRITICAL': 4
};
const THRESHOLD: Record<string, string> = {
'info': 'INFO', 'warn': 'HIGH', 'block': 'CRITICAL'
};
const thresholdSeverity = THRESHOLD[severity] || 'HIGH';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 PHI identifier checks carry INFO severity — invisible at the default threshold

phi-identifiers and health-data-fields both have severity_default: "INFO" (order value 0). The default --severity is warn, which maps to a threshold of HIGH (order value 3). Those checks are added to allFindings but are filtered out of gatedFindings, so adding ssn, dob, mrn, or patient names to source code exits 0 with the default invocation. For a HIPAA compliance gate, the two most basic PHI-detection checks should block by default.

Comment thread bin/comply-check
Comment on lines +180 to +191
// Read full file content for context
let content = '';
try {
content = fs.readFileSync(name, 'utf-8');
} catch {
// File might not exist (deleted)
}

if (addedLines.length > 0) {
files.push({ path: name, content, addedLines });
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 DiffFile.content loaded but never used

fs.readFileSync(name, 'utf-8') reads the entire current file into DiffFile.content on every changed file, but neither runCodeChecks nor checkNewDependencies ever accesses file.content — both iterate only over file.addedLines. On a large PR that touches generated files or lock files this is unnecessary I/O. Consider removing the field or deferring the read until a check actually needs full-file context.

@aanishs

aanishs commented Jul 10, 2026

Copy link
Copy Markdown
Owner Author

Closing as duplicate of #44 — both branches point at the identical commits (b5ca1a8, 095c8c1, b99d51c), so the full plain-English + persona content lands via #44. Part of the Trust Sprint hygiene pass.

@aanishs aanishs closed this Jul 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant