[#227] feat: assess-security capability + deterministic secret-scanning layer#341
Conversation
…nt (PR #341 review) Critical: this repo's own dogfood .pair/knowledge/ tree was missing secret-scanning.md + gitleaks-example.toml + the README index entry, mirroring only existed in packages/knowledge-hub/dataset/ — the live skills' relative links resolved to nothing in this repo's own adoption tree. Mirrored all three, byte-identical to dataset. Minor: way-of-working.md (root + dataset) still said "35 skills", bumped to 36 in both copies.
… lock it in with a conformance test (PR #341 round-3 review) Root mirror had spurious extra blank lines vs dataset (retyped, not copied). Re-copied for true byte parity, and added a conformance test scoped to this story's 3 mirrored files (secret-scanning.md, security README.md, gitleaks-example.toml) so neither this regression nor the round-2 missing-mirror one can recur silently.
… fixture-test scope in ADR-015 (PR #341 round-4 review) - Regenerated .pair/llms.txt (via existing apps/pair-cli/src/registry/llms-generation.ts logic) to close repo-wide staleness, including the missing secret-scanning.md entry. - Backfilled quality-model.md into the root .pair/knowledge/ mirror (was dataset-only, breaking relative links from secret-scanning.md). - Clarified in ADR-015 + secret-scanning.md that the gitleaks Verification section is a documented, manually-reproduced check by design, not a committed automated fixture test (external-binary dependency; would need a smoke test per the gate-tooling-code ADL, not a vitest unit test, and this repo doesn't dogfood gitleaks into its own CI yet).
…ue-on-error check (PR #341 review round 5) MIRRORED_FILES was missing quality-model.md, backfilled in round 4 but left unprotected against future re-divergence. Tightened the continue-on-error assertion to match only the YAML key form (continue-on-error:), not prose mentioning the option by name.
…s CI wiring (PR #341 review, nonActionable findings now resolved) Per corrected policy (nonActionable is not a scope filter — a real fixable gap gets fixed, not deferred): resolves all 5 previously-accepted findings from the last review round. - Records ADL 2026-07-18-d22-classification-verdict-collapsed-details.md: D22 (1-line verdict + collapsed findings, never a table inline) was cited 5+ times with no decision record anywhere. - quality-model.md (root + dataset): backlink from the Security pillar to assess-security's 5-layer rule-resolution cascade. - adr-015-security-deterministic-layer-not-a-skill.md: H1 corrected to "# ADR-015: ..." per the already-tracked repo-wide fast-follow (numbering is now final). - Real gitleaks CI wiring, the actual point of this story: root .gitleaks.toml allowlisting the guideline's own worked example (AKIAABCDEFGHIJKLMNOP) plus 2 unrelated pre-existing placeholder tokens found during verification (usr_abc123def456, sess_xyz789uvw012 in observability/sensitive-data-protection.md) via precise literal regexes, not a broad pattern — a different secret in the same files still trips the scan (verified). secret-scan job added to ci.yml. Full-history scan verified clean (283 commits, 0 leaks) before wiring. ADR-015 updated to record this is no longer deferred.
…ng layer Add pair-capability-assess-security (review verdict + one-shot OWASP audit, D22) and wire it into pair-process-review Phase 2 (AC1/AC4: introduced red findings drive CHANGES-REQUESTED). Provision the deterministic secret-scanning CI layer (gitleaks default, required + fail-closed, .gitleaks.toml allowlist) via pair-capability-setup-gates (R6.5/D24) — never a skill, never an LLM judgment call; assess-security explicitly never re-implements it. Record the skill/CI-layer boundary as ADR-015. Verified the secret-scanning guideline's own reproducibility claim with gitleaks 8.x and fixed it: a bare `gitleaks detect` scans full history (a leak keeps failing forever), the CI-equivalent scoped invocation (`--log-opts`) is what actually demonstrates the allowlist working. Updates skill counts (35->36) across docs site + skills-guide.md (catalog subsection was under-counted) + eval trigger-prompts.json. Refs: #227
…nt (PR #341 review) Critical: this repo's own dogfood .pair/knowledge/ tree was missing secret-scanning.md + gitleaks-example.toml + the README index entry, mirroring only existed in packages/knowledge-hub/dataset/ — the live skills' relative links resolved to nothing in this repo's own adoption tree. Mirrored all three, byte-identical to dataset. Minor: way-of-working.md (root + dataset) still said "35 skills", bumped to 36 in both copies.
… lock it in with a conformance test (PR #341 round-3 review) Root mirror had spurious extra blank lines vs dataset (retyped, not copied). Re-copied for true byte parity, and added a conformance test scoped to this story's 3 mirrored files (secret-scanning.md, security README.md, gitleaks-example.toml) so neither this regression nor the round-2 missing-mirror one can recur silently.
… fixture-test scope in ADR-015 (PR #341 round-4 review) - Regenerated .pair/llms.txt (via existing apps/pair-cli/src/registry/llms-generation.ts logic) to close repo-wide staleness, including the missing secret-scanning.md entry. - Backfilled quality-model.md into the root .pair/knowledge/ mirror (was dataset-only, breaking relative links from secret-scanning.md). - Clarified in ADR-015 + secret-scanning.md that the gitleaks Verification section is a documented, manually-reproduced check by design, not a committed automated fixture test (external-binary dependency; would need a smoke test per the gate-tooling-code ADL, not a vitest unit test, and this repo doesn't dogfood gitleaks into its own CI yet).
…ue-on-error check (PR #341 review round 5) MIRRORED_FILES was missing quality-model.md, backfilled in round 4 but left unprotected against future re-divergence. Tightened the continue-on-error assertion to match only the YAML key form (continue-on-error:), not prose mentioning the option by name.
…s CI wiring (PR #341 review, nonActionable findings now resolved) Per corrected policy (nonActionable is not a scope filter — a real fixable gap gets fixed, not deferred): resolves all 5 previously-accepted findings from the last review round. - Records ADL 2026-07-18-d22-classification-verdict-collapsed-details.md: D22 (1-line verdict + collapsed findings, never a table inline) was cited 5+ times with no decision record anywhere. - quality-model.md (root + dataset): backlink from the Security pillar to assess-security's 5-layer rule-resolution cascade. - adr-015-security-deterministic-layer-not-a-skill.md: H1 corrected to "# ADR-015: ..." per the already-tracked repo-wide fast-follow (numbering is now final). - Real gitleaks CI wiring, the actual point of this story: root .gitleaks.toml allowlisting the guideline's own worked example (AKIAABCDEFGHIJKLMNOP) plus 2 unrelated pre-existing placeholder tokens found during verification (usr_abc123def456, sess_xyz789uvw012 in observability/sensitive-data-protection.md) via precise literal regexes, not a broad pattern — a different secret in the same files still trips the scan (verified). secret-scan job added to ci.yml. Full-history scan verified clean (283 commits, 0 leaks) before wiring. ADR-015 updated to record this is no longer deferred.
7ac5836 to
2326030
Compare
…e-scope-filter ADL (PR #341 round-7 review) Per round-7 review of PR #341: - adr-011..014 H1s corrected to "# ADR-NNN: <Title>" (the fast-follow 2026-07-12-adr-sequential-naming-convention.md tracked, deferred until adr-009/adr-010 landed on main - both now merged). adr-014 (created after that ADL, still unprefixed) folded into the same fix. adr-015 (this story) already had a numbered H1 from round 7 - now all of adr-001..015 are consistent, closing the "adr-015 as a lone exception" gap the review flagged. ADL addendum records scope + completion. - Recorded ADL 2026-07-18-nonactionable-findings-still-get-fixed-unless- design-disagreement.md: the review flagged an undocumented "corrected policy" (nonActionable is not a scope filter) cited in commit 7ac5836's message but never recorded anywhere - and the same standard is applied again by this very fix round's own instructions, confirming it's a real, repeated standard rather than a one-off. Recorded per CLAUDE.md's "record decisions" rule so future reviewers don't re-litigate nonActionable calls PR-by-PR. Does not touch .claude/agents/reviewer.md's broader existing definition - left as a noted follow-up, out of this story's scope. Refs #227
…icense), dual-form KB template, stale skill count Verify review (Major, confirmed in live CI run 29638207943 — secret-scan fail 5s): gitleaks-action@v2 requires a GITLEAKS_LICENSE for org-owned repos (foomakers is an org), so the round-8 dogfood job failed before scanning — a red-but-meaningless gate. My earlier "verified" was the local binary, not the CI action. - ci.yml: run the MIT-licensed gitleaks binary directly (pinned 8.30.1), no org-license gate; fail-closed via --exit-code 1. - secret-scanning.md (root + dataset, byte-identical): document BOTH forms — Option A action + GITLEAKS_LICENSE (org note, matches github-actions-implementation.md), Option B binary (no license). - secret-scanning.test.ts: assert the template surfaces the org-license requirement + the binary option. - getting-started.md (root + dataset): stale "35 skills (25 capability)" -> "36 (26 capability)" — the drift class this PR fixes elsewhere. - ADR-015: correct the 2026-07-18 update to reflect binary-not-action.
0d82c14 to
9885f58
Compare
…e-scope-filter ADL (PR #341 round-7 review) Per round-7 review of PR #341: - adr-011..014 H1s corrected to "# ADR-NNN: <Title>" (the fast-follow 2026-07-12-adr-sequential-naming-convention.md tracked, deferred until adr-009/adr-010 landed on main - both now merged). adr-014 (created after that ADL, still unprefixed) folded into the same fix. adr-015 (this story) already had a numbered H1 from round 7 - now all of adr-001..015 are consistent, closing the "adr-015 as a lone exception" gap the review flagged. ADL addendum records scope + completion. - Recorded ADL 2026-07-18-nonactionable-findings-still-get-fixed-unless- design-disagreement.md: the review flagged an undocumented "corrected policy" (nonActionable is not a scope filter) cited in commit 7ac5836's message but never recorded anywhere - and the same standard is applied again by this very fix round's own instructions, confirming it's a real, repeated standard rather than a one-off. Recorded per CLAUDE.md's "record decisions" rule so future reviewers don't re-litigate nonActionable calls PR-by-PR. Does not touch .claude/agents/reviewer.md's broader existing definition - left as a noted follow-up, out of this story's scope. Refs #227
d6d38ee to
9885f58
Compare
…ify A/B scan-scope in secret-scanning template - README.md: stale "35 Agent Skills" -> 36 (assess-security is the 26th capability skill); getting-started.md was already bumped, this is the root repo README that also carries the count. - secret-scanning.md (root + dataset, byte-identical): the two CI-template forms are not "equivalent" in scan scope — reworded to state they share the fail-closed guarantee but differ in scope (Action = event commit range; binary = full history).
Code Review — PR #341PR Number: #341 Review SummaryOverall Assessment
The change is solid and the four acceptance criteria are genuinely met (not just claimed): This is an adversarial re-review against an author claim of zero outstanding findings. That claim Business Value ValidationCloses the two gaps in Epic #208 as specified: a judgment-call security assessment skill, and a Detailed Review CommentsMinor Issues
Nits
Positive Feedback
Security ReviewDeterministic Testing Review
Risk AssessmentLow. Findings are Minor/Nit and confined to eval fixture data + PR-hygiene; no runtime or interface risk. Decision: Approved with Comments. The "zero findings" claim is disproved (AD-N3 is a real defect), |
…anning Verification prose with full-history CI (verify-review) - eval/trigger-prompts.json AD-N3 was self-contradictory (should-not-trigger + expected assess-security). The prompt "Assess the security posture of our login flow" is a direct assess-security trigger -> should-trigger. - secret-scanning.md Verification (root + dataset): the prose claimed full-history scanning "is not what the CI job is testing" and that an allowlist wouldn't help — both now false since the dogfooded CI uses `gitleaks detect --source .` (full history) + the allowlist. Reworded to match: reproduction uses --log-opts=-1 for isolation; CI scans full history against the allowlist.
Remediation — all requested findings addressedFixed (this round):
Remaining — non-closable:
Gates green (vitest 182, skills:conformance 36, docs:staleness, check:links; CI incl. secret-scan); rebased on Mirror reconciliation (post-merge): the root↔dataset KB mirror drift is intentionally not hand-fixed in-PR (a blind byte-sync would break the intentional dual-naming convention |
…ply-chain integrity) The secret-scan job (and the shipped Option-B template) fetched the gitleaks binary version-pinned but without an integrity check. Added sha256sum -c against the release's published checksum (551f6fc… for 8.30.1 linux_x64), fail-closed — if the release asset were swapped, the job fails instead of running an unverified binary. Addresses the reviewer's supply-chain hardening note.
Remediation follow-up — supply-chain hardening appliedReconsidered the "by-design" gitleaks-download note as a real weakness and fixed it: the |
PR Information
PR Title: [#227] feat: assess-security capability + deterministic secret-scanning CI layer
Story/Epic: #227 (epic #208 — quality/security review)
Type: Feature (skill + KB + CI config — no runtime application code)
Priority: Medium
Assignee: Gianluca Carucci
Reviewers: independent blind review (2-lens: process + adversarial nit)
Labels: feature, security
Summary
What Changed
Delivers story #227's two halves:
pair-capability-assess-security(skill, dataset + installed mirror) — review-mode 1-line verdict + collapsed findings feeding the risk matrix's security dimension (D22); audit-mode OWASP scan writing a report and proposing project rules viarecord-decision. Rules resolve through a 5-layer cascade (global KB → per-service → per-web-app → adoption → package-scoped). It never scans for secrets itself (D24).secret-scanning.mdguideline +gitleaks-example.tomlasset (root + dataset, byte-identical, byte-parity-locked by a conformance test), provisioned by/setup-gates. Dogfooded into this repo's own.github/workflows/ci.ymlvia the license-free gitleaks binary (pinned 8.30.1) — notgitleaks-action@v2, which requires aGITLEAKS_LICENSEfor org-owned repos and fails before scanning. Verified green in live CI (secret-scanjob). Root.gitleaks.tomlallowlists exactly the documented synthetic strings via precise regexes.Supporting: D22 recorded as an ADL; quality-model Security pillar backlinks the 5-layer cascade; ADR-015 records "deterministic layer is CI, not a skill";
/pair-process-reviewStep 2.4 composes assess-security (CHANGES-REQUESTED on introduced vulnerabilities).Why This Change
Security findings now surface in every review against an objective, resolved rule set, and a committed secret mechanically blocks the build with no LLM involved (R6.5) — closing the two gaps in epic #208.
Story Context
User Story: I want
pair-capability-assess-security(review + audit) plus a deterministic secret-scanning layer, so security findings surface in every review with objective rules and secrets mechanically block the build.Acceptance Criteria:
$mode: audit→ report in.pair/working/reports/security/+ proposes project rules to record. ✅Changes Made
Implementation Details
assess-securitySKILL.md (dataset +.claude/mirror) — review/audit modes, 5-layer cascade, D22 output, D24 boundary.secret-scanning.mdguideline +gitleaks-example.toml(root + dataset, byte-identical) — dual CI-template forms (Action +GITLEAKS_LICENSEfor orgs; binary for no-license), fail-closed, allowlist mechanism.ci.ymlsecret-scanjob (gitleaks binary) + root.gitleaks.toml;/setup-gatesprovisions the layer;/pair-process-reviewStep 2.4 composes assess-security.Files Changed (42 files — see the PR Files tab for the authoritative list)
.claude/skills/pair-capability-assess-security/SKILL.md(+ dataset mirror);.pair/knowledge/guidelines/quality-assurance/security/secret-scanning.md(+ dataset mirror);.pair/knowledge/assets/gitleaks-example.toml(+ dataset mirror);.gitleaks.toml;.pair/adoption/tech/adr/adr-015-…md;.pair/adoption/decision-log/2026-07-18-d22-…md;packages/knowledge-hub/src/conformance/{assess-security,secret-scanning}.test.ts..github/workflows/ci.yml;pair-process-review+pair-capability-setup-gates+pair-nextSKILLs (+ mirrors);quality-assurance/quality-model.md(+ mirror);README.md;.pair/knowledge/getting-started.md+way-of-working.md(+ mirrors, 35→36 count);.pair/llms.txt(regenerated);packages/knowledge-hub/eval/trigger-prompts.json; severalapps/website/content/docs/**pages.Database Changes / API Changes
Testing
Test Coverage
assess-security.test.ts(20 — modes, D22, D24, review composition, setup-gates provisioning) +secret-scanning.test.ts(16 — dual-form template, org-license documented, fail-closed, byte-parity lock for this story's mirrored files).secret-scanjob runs the gitleaks binary on this repo — verified green in live CI.Test Results
Testing Strategy
set -euo pipefail,--exit-code 1, nocontinue-on-error).Quality Assurance
assess-securityis output-only (audit report is the one exception, D14); adoption writes go throughrecord-decision.Deployment Information
secret-scanjob becomes a required CI check on this repo; KB/skill content ships with the dataset. Config: the binary form needs no secret; the Action form (for adopters) needsGITLEAKS_LICENSEfor orgs. Rollback: revert the PR.Breaking Changes
Documentation
secret-scanning.mdguideline, ADR-015, D22 ADL, quality-model backlink..pair/llms.txtwas regenerated and swept in ~9 previously-unindexed pre-existing files (azure-devops-, context-map-, canonical-states, ddr-template, subdomain-context-template) — harmless generator output, unrelated to this story, noted here for transparency.Performance Impact
secret-scanbinary job adds ~8s to CI.Security Considerations
Security Review
--exit-code 1, nocontinue-on-error, pinned 8.30.1 — no LLM in the path (R6.5, D24)..gitleaks.tomlallowlists exactly the documented synthetic strings (AKIAABCDEFGHIJKLMNOP,usr_abc123def456,sess_xyz789uvw012); no un-allowlisted secret-shaped string introduced; a different secret still fails.Security Testing
secret-scanjob itself; live-CI green; full-history scan clean.Accessibility
Risk Assessment
Technical Risks
Reviewer Guide
Review Focus Areas
secret-scanCI job actually goes green (binary, no license).Testing the Changes
Dependencies & Related Work
/setup-gates(provisions the layer);/pair-process-review(composes assess-security). ADR-011..016 H1 numbering + the ADR-016 rename are owned by PR chore: process/tooling cleanup — conformance-test convention, ADR naming fix, agent-execution-layer ADL amendment, KB fresh-checkout note #342 (kept out of this PR to avoid a cross-PR conflict; this PR owns only its ownadr-015).setup-gatesKB refs not mirrored to root (Verify + fix: @pair/content-ops devDependency may break published pair-cli's KB install/update #345 tracks the content-ops devDependency risk).Pre-Submission Checklist
main