Guidance for Claude Code sessions working in this repo. Keep this concise — it's operating doctrine, not a spec.
ceproof is an EU Cyber Resilience Act (CRA) compliance-posture scanner
(npx ceproof scan) and, as of M2, a document-generation tool
(npx ceproof report). It was bootstrapped from a sibling tool, licenseproof
(a dependency-license scanner), by reusing its dependency/SBOM/license
resolution engine and stripping licenseproof-specific concepts (license
"conflict" detection) that don't apply here. ceproof's own compliance
concept — a per-CRA-requirement gap checklist (DERIVED / PARTIAL /
NEEDS-HUMAN-INPUT), built against data/cra-structure.json's real,
traceable extraction from the regulation text — is structurally unrelated to
conflict severity.
src/cli— commander CLI.scan,init,report,activate, anddeactivateare all registered (M1 + M2 + M3).report's narrative formats (annex7/annex7-pdf/doc/doc-pdf) require a valid license key (src/cli/report.ts'sGATED_FORMATS);sbom-cyclonedx/sbom-spdxstay free.src/ecosystems— npm/PyPI dependency scanning (lockfile parsing, installed-package disk verification).src/resolve— license resolution: SPDX expression parsing, license-text fingerprinting, category classification.src/detect— repo-fact detectors for CRA-relevant signals (security disclosure, project license, ecosystems, runtimes, VCS facts, update mechanism) — composed intoRepoFactsbysrc/detect/index.ts.src/gap— the CRA gap-checklist engine (computeGapChecklist): resolves everydata/cra-structure.jsonitem to DERIVED/PARTIAL/NEEDS-HUMAN-INPUT againstRepoFacts, resolved packages, and an optionalceproof.config.src/cra—CraStructureItem/CraStructureFiletypes and thedata/cra-structure.jsonloader. Four derivability values:derivable,partial(both evidence-key-driven),human-required(needs aceproof.configanswer),boilerplate(fixed regulatory text, always DERIVED, no evidence or config needed — e.g. Annex V point 3).src/config—CeproofConfig(ceproof.config.json) types, hand-rolled validation, andceproof init's interview-file generation.src/report—provenance.ts(release-identifier + tool/data-version/ timestamp stamping for every rendered artifact — see foundations_addendum §3),annexContent.ts(turns a gap checklist +ceproof.configinto renderer-agnostic Annex VII/Annex V section content, including theannex7.7-composed-from-Annex-V special case),html.ts/pdf.ts(the M2 Annex VII pack / Annex V DoC renderers),sbom/{cyclonedx,spdx}.ts(SBOM formats — CycloneDX 1.6, SPDX 2.3 pending a future 3.0.1 rewrite, seemilestone2_brief.md),reportData.ts(the reused dependency/SBOM pipeline, no conflict concept).data/— vendored reference data: SPDX license list, license category mappings, license texts,cra-structure.json(the real, traceable CRA extraction — seetests/cra/traceability.test.ts), plusmanifest.json({dataVersion, spdxListVersion, craSourceVersion, generatedAt}).data/sources/(the full vendored primary-source text) is build-time-only and never ships in the published package (verify-pack.shasserts this).
- Fully offline at runtime. No network calls, no telemetry, no "phone
home," ever — in
scan, in any futurereport/gapcommand, anywhere. - Honest UNKNOWNs. Never silently guess or omit. If a signal can't be
resolved confidently, say so (
UNKNOWN, a warning, an explicit "unspecified") — never fabricate a value to fill a gap. - "Informational — not legal advice" appears on every user-facing
output (terminal, JSON, and any future HTML/PDF/notices artifact). Use the
DISCLAIMERconstant insrc/branding.ts, don't re-word it per surface. - Never-silent assumptions. Any assumption made on the user's behalf (e.g. treating an unspecified project license as a default) must be printed prominently in the output, never silent.
- Never commit failing tests.
npm testmust be green before any commit. If a test can't legitimately be made to pass (e.g. it depends on signing infrastructure that doesn't exist yet), skip it explicitly with a comment explaining why — never delete it just to silence a failure, and never leave it red. - Verify
git remote -vbefore any push. Never push without checking what remote you're pushing to. - Work that exists only in an ephemeral sandbox or an unpushed local
folder does not exist. Two real, expensive incidents in this portfolio:
ceproof's original Ed25519 signing keypair was generated inside an
ephemeral build sandbox and was lost outright when that sandbox was
reclaimed — recoverable only because no key had ever been sold yet, so
regenerating before first sale was still an option. That was luck, not
design. Separately, the live Stripe-fulfillment Worker's entire source
(
licenseproof-fulfillment) survived for a while only as an unpushed local folder on a Mac — invisible to any other environment or session working on this portfolio — and had to be pushed to GitHub before it could be extended by anyone else. Push to a verified real remote before any session or work session ends, and confirm the push actually landed by runninggit remote -vagain after pushing, not only before. Not "eventually," not "when convenient" — before the session ends. - A release gate must be re-run whenever the thing it guards changes, not
just when the gate itself changes.
verify-pack.sh'sdata/sources/exclusion check was written and genuinely passed during M1's T11 task — butdata/sources/didn't exist yet at that point, so the pass was truthful but vacuous (nothing to catch).data/sources/was populated for real one task later (T9), and the script was never re-run before M1 was declared done — so a real packaging bug (data/sources/shipping in the npm tarball,package.json'sfilesarray never got the exclusion it needed) went undetected until M2. The check's logic was never buggy; the discipline gap was trusting an old pass after the repo state the check depends on had moved. Any milestone that adds/changes whatdata/,scripts/, or the packed tarball contains (a new vendored directory, a new build artifact, a newfiles-array entry) must re-runverify-pack.shbefore that milestone is declared done — a stale pass is worse than no check at all.
src/branding.ts is the single source of truth for product name, URL, config
directory, env var name, disclaimer text, and pricing constants. Import from
there — don't hardcode "ceproof", a URL, or a price as a literal.