All notable changes to ContextSafe are documented here. The format is based on Keep a Changelog; the project has no tagged release yet, so everything to date lives under Unreleased.
-
Dependency-update automation (SEC-14):
.github/dependabot.ymlcovering the two places this repository pins — theuvlock and the SHA-pinned GitHub Actions — weekly, with a seven-day cooldown on both ecosystems (SEC-26 asks for 72 hours) and Python updates grouped into one pull request. The repository previously had neither adependabot.ymlnor arenovate.json, so no advisory against a locked dependency could open a pull request, nothing kept the action pins current, and OpenSSF Scorecard'sDependency-Update-Toolcheck scored 0 by construction. -
CLI:
contextsafe evidence preflightnow accepts--output, matchingpack validate,plan validate, andevaluate. Previously the only way to obtain the boundary-check result was stdout, so combining--quietwithevidence preflightsilently discarded the command's only output and left nothing but the exit code.--outputwrites the same non-sensitive result document (boundary_check_status, hashes, declared scope —PreflightResultnever carries evidence content) that would otherwise print; it does not change what the command reads, copies, indexes, or logs. -
B-033 slice:
schemas/contextsafe-receipt-v0.1.schema.json, the published contract for the receipt document and its deterministic payload — the pre-1.0 shape of the receipt schema required bydocs/04-ARCHITECTURE.mdsection 8. The contract closes every object (additionalProperties: false), pins the unsigned envelope constants so a signing layer cannot relabel these documents in place, keeps the payload claim-minimal by rejecting timestamp, signature, reviewer, run-environment, and semantic-value fields, pins the mandated limitation set as a closed ordered list so a stripped, reworded, reordered, or padded disclosure fails validation (F-030) and the payload carries no unbounded free-text channel, and publishes closed status, reason, checkpoint, and concept enums. Tests enforce schema/runtime agreement on the reference document, theevaluate --outputartifact, and every Hypothesis-generated bundle; a companion test asserts that every file inschemas/is a valid, self-consistent Draft 2020-12 contract. Outcome reasons are now the typedOutcomeReasonenum, so an unpublished reason string cannot reach a receipt without a schema change. Receipt bytes are unchanged. -
B-027 slice: Hypothesis-based property tests seeding the documented property layer (
docs/09-TEST-AND-EVALUATION.mdsection 2) for the machine-checkable status-algebra invariants — no pass without exactly one affirmative evidence match, not-applicable only from a predeclared rule, fail-closed cross-concept rejection, order-independent byte-identical receipts, and value-minimized receipts that never echo generated semantic values. Invariants needing pack lifecycle, review signatures, HTML, or signature verification remain untested because those components do not exist yet. -
B-020 slice: every CLI command accepts
--quiet(suppress the stdout success payload; exit codes,--outputfiles, and stderr JSON errors unchanged) and--no-color(an explicit pin of the always-plain contract — output never contains ANSI escape sequences), and exit codes are documented and stable:0success,2fail-closed contract rejection,64command-line usage error (previously argparse's default2, which collided with contract rejections). -
V1 planning corpus (
docs/00–16): PRD, service design, architecture, data and evidence model, security/privacy threat model, governance, test strategy, operations, roadmap, backlog, risk register, and release checklist. -
Iteration 1: strict versioned case and observation-set schemas; separately typed GI, RSG, SPCU, name-to-use, and pronoun values; fail-closed cross-concept rejection; pure exact-match evaluator (missing/ambiguous evidence is indeterminate); deterministic value-minimized JSON receipts; offline
validateandevaluateCLI commands with a synthetic reference fixture. -
Iteration 2: strict pack envelope, deterministic unsigned compiler with semantic component hashes and lifecycle/withdrawal checks; strict engagement and execution-plan contracts with fail-closed non-production attestations, host allowlisting, and hash pinning.
-
Iteration 3: canonical JSON evidence boundary envelope with field allowlist, namespace pins, PHI canaries, and direct-identifier checks; read-only
evidence preflight; recoverable two-pass persistence into a SHA-256 object store with an update/delete-protected SQLite index. -
Iteration 4 (B-021 slice): receipt payload/envelope separation.
contextsafe evaluatenow emits a receipt document instead of the bare iteration-1 receipt — the byte-identical deterministic payload pluspayload_sha256over the payload only, and an untrusted envelope with caller-declaredclaimed_generated_at(optional canonical whole-second UTC, via--claimed-generated-at),signature_status: not_signed, andtrusted_time: false. Timestamps and signatures stay outside the deterministic payload (P0-14); no signing or trusted-time path exists. -
Standards-conformance baseline (2026-07-16 sweep): LICENSE (Apache-2.0), SECURITY.md, CONTRIBUTING.md, CITATION.cff, CHANGELOG, pre-commit config, Semgrep/gitleaks/pip-audit security workflow, tag-triggered release workflow, ADR log seed (existing ADRs relocated from
docs/decisions/todocs/adr/), docs/I18N.md declaration, and a README Standards Conformance table.
- The Semgrep SAST gate (SEC-07) had been red on
mainfor every one of its fourteen runs since 2026-07-17, on four blocking findings against the two evidence-index header PRAGMAs inevidence_store.py. SQLite does not accept bound parameters in a PRAGMA, so the statements are now rendered once at module scope from their integer constants with the:dconversion — which can emit only digits and an optional sign — and_publish_new_databaseexecutes those constants instead of building a string at the call site. A new test pins the exact rendered text of both statements, requires each to matchPRAGMA [a-z_]+ = -?\d+, and asserts that SQLite rejects the parameterized form. No waiver,.semgrepignore, or# nosemgrepwas added; the registry auto config now reports 0 findings over 72 targets. Store bytes and the on-disk index header are unchanged.
- The Semgrep SAST gate (SEC-07) reported a green check on every pull request
while scanning nothing.
semgrep ciresolves a diff baseline on apull_requestevent by runninggit fetch origin --force --depth=1 <head-sha>; this repository is private and the checkout setspersist-credentials: false, so the fetch failed, Semgrep aborted before scanning, and its default--suppress-errorsturned the aborted run into exit 0. The job logs carry the scan-environment banner and the fetch error with no scan summary — no rule count, no target count, no findings line. A HIGH finding introduced by a pull request would have passed the gate. Replaced withsemgrep scan --config auto --error --strict, which needs no baseline and no credential, runs the identical full scan on push, pull request, and schedule, and fails on an analysis error so a scan that cannot run can no longer report success. See ADR 0004.