Skip to content

Repository files navigation

CEQA Preflight

CEQA Preflight is an early-stage, local-first command-line tool for checking the technical readiness of CEQA Submit filing packages.

It is designed to help planners, clerks, and consultants catch objective, correctable package issues before State Clearinghouse review. It produces source-cited advisory findings and human-review reminders; it will not submit documents, modify originals, or determine legal sufficiency. The default check path makes no network requests. A separate, opt-in ai command group (ADR 0002) sends document text to a configured model provider to draft manifest fields, explain findings, and draft corrections; it never produces a finding and is never invoked by check.

Status

Status: In build

CEQA Preflight is pre-alpha software. The repository includes safe local package loading, bounded PDF technical inspection, a source-cited declarative rule engine, a common technical rule pack (readability, searchable text, flattened forms, structure tags, file hygiene), and a synthetic-package generator for demos and pilot calibration. NOD- and NOE-specific rules are currently experimental: they run only with --include-experimental while documented official-source review, practitioner review, tests, and a permissioned pilot are completed.

Intended initial scope

  • Local directory or ZIP input.
  • NOD and NOE package checks.
  • Deterministic PDF and metadata checks.
  • Accessible HTML and JSON reports.
  • No hosted document storage, portal scraping, or AI-driven legal analysis.
  • Opt-in, model-backed drafting and explanation under ADR 0002: the model structures input and narrates cited sources; only the deterministic rule engine produces a finding, and legal-sufficiency questions are refused.

Quick start

uv sync --all-groups
uv run ceqa-preflight --version
uv run ceqa-preflight --help
uv run ceqa-preflight init ./my-package --filing-type NOE
uv run ceqa-preflight init ./my-package --filing-type NOE --from-package
uv run ceqa-preflight check ./my-package --filing-type NOE --format html --output ./reports
uv run ceqa-preflight check ./my-package --filing-type NOE --include-experimental
uv run ceqa-preflight check ./pkg-a ./pkg-b --filing-type NOE --format json --output ./reports
uv run ceqa-preflight check ./my-package --filing-type NOE --format checklist
uv run ceqa-preflight synth ./demo-package --filing-type NOE --defect scanned
uv run ceqa-preflight rules list --filing-type NOE
uv run ceqa-preflight rules list --format json
uv run ceqa-preflight pilot init ./pilot-evidence
uv run ceqa-preflight pilot summarize --reviews ./pilot-evidence/finding-review.csv --baseline ./pilot-evidence/manual-baseline.csv

Without a local checkout, run the CLI directly from a clone with uv: uvx --from /path/to/ceqa-preflight ceqa-preflight --help, or install it with pipx install /path/to/ceqa-preflight. No package registry release exists yet.

The check command reads one or more directories or ZIP packages locally, never uploads or alters their contents, and can emit console, JSON, self-contained HTML, or printable sign-off checklist advisory reports. Checking several packages at once prints a per-package roll-up summary. Add --manifest package.yaml to enable explicit primary form and document-category checks when experimental rules are opted into (single package only), and --rules / --exclude-rules to select specific rule identifiers. The default run includes active technical checks only. Add --log-format json for minimal, package-content-free operational events on stderr, including inspection progress counts for large packages.

Every report states its own scope. Any rule that applies to the filing type but did not run — because it is experimental and --include-experimental was not given, because --rules or --exclude-rules removed it, or because it has been withdrawn — is listed by identifier, with the reason and the way to run it, in all four report formats. A report with no failures therefore always says whether it covered every applicable check or only some of them.

The synth command generates plainly fictional synthetic packages, optionally seeded with objective defects (scanned pages, fillable forms, encrypted or truncated PDFs, duplicates, and more) for demos, regression tests, and pilot reviewer calibration. See examples/ for a generated package and its HTML report.

The pilot commands support the permissioned evaluation protocol with opaque IDs and controlled labels only; they do not read filing packages or accept free-text reviewer notes.

Opt-in AI commands

The ai command group (ADR 0002) is the only part of the tool that talks to a model provider, and nothing else invokes it. Install the extra (pip install 'ceqa-preflight[ai]', or [ai-bedrock] for Amazon Bedrock), put the credential in the environment (ANTHROPIC_API_KEY, or the AWS credential chain plus AWS_REGION), and opt in per command. The default model is claude-sonnet-5; --provider, --model, CEQA_PREFLIGHT_AI_PROVIDER, and CEQA_PREFLIGHT_AI_MODEL change it. Every ai command states the data flow before it runs: the text it sends leaves the machine for the duration of the request, and the provider's terms apply to it. It never writes that text to a log.

uv run ceqa-preflight ai extract ./my-package --filing-type NOE --write-manifest ./my-package/package.yaml
uv run ceqa-preflight ai extract ./my-package --filing-type NOE --format json --output ./reports/extraction.json

ai extract reads each PDF's text layer through the same bounded, process-isolated path check uses and asks the model to copy out the facts a manifest carries: what kind of document it is, the project title, lead agency, county, city, SCH number, exemption status and citation, and so on. Every value must come with a verbatim quote from the document; the tool verifies the quote against the text and withholds any value whose quote does not verify. A field the text does not state is unknown. A scanned, image-only PDF is reported as having no text layer and is not sent anywhere. The result is a draft manifest for a person to review and correct; only check --manifest on the reviewed manifest produces findings. The model structures input. It never decides anything, and the rule engine never sees its output directly.

uv run ceqa-preflight check ./my-package --filing-type NOE --format json --output ./reports
uv run ceqa-preflight ai explain ./reports/report.json
uv run ceqa-preflight ai draft-fix ./reports/report.json --rules PDF-003,PDF-007
uv run ceqa-preflight ai ask ./reports/report.json "What does PDF-003 mean?"

ai explain and ai draft-fix read a JSON report written by check and, for each failure, warning, or manual-review item, ask the model for a plain-language explanation (or a numbered correction draft) in which every claim cites a passage of the official source the rule cites and quotes it verbatim. The passages come from corpus/, the committed, hashed text of those sources; a verifier checks every quote against it and checks every sentence for determination language before anything is shown. Claims that fail are withheld and counted. A self-cited rule (FILE-004, FILE-005) is explained from the project's own reasoning and says so.

ai ask answers questions about the findings in a report. Any form of "is this legally sufficient", "will it be accepted", "is this exemption valid", or "did the agency comply", in English or Spanish, direct or indirect, is refused before the model runs and redirected to the objective findings and to qualified review; the model is instructed to refuse as well, and its answers pass the same verifier. The refusal suite in evals/ has zero tolerance. None of this output is a finding; check alone produces findings, and its output is unchanged by the ai commands.

Exit codes

check exits 0 when no automated failure was found (warnings and manual-review items may still exist), 1 when at least one failure finding was produced, and 2 on input or internal rule errors. With multiple packages, the worst exit code wins.

Exit code 0 is not a statement that every applicable check ran: skipped checks do not change it. Read the not_run list, or the "check(s) not run" count in the summary line, before treating a 0 as a complete result.

Non-affiliation and disclaimer

CEQA Preflight is an independent open-source project. It is not affiliated with, endorsed by, or operated by the State of California, the Governor's Office of Land Use and Climate Innovation, the State Clearinghouse, CEQA Submit, or CEQAnet. See DISCLAIMER.md.

Development

make verify

This project is developed with AI-assisted tooling. Every change, whether AI-assisted or not, must pass the same review, tests, and make verify gate before merge; AI-assisted development measurement is tracked in the responsible technology audits.

See CONTRIBUTING.md, SECURITY.md, and the architecture decisions (ADR 0001, ADR 0002). The project also documents its pilot protocol, pilot partner kit, accessibility boundaries, and threat model.

Public API and release status

The command-line interface and JSON report schema are not yet stable. No GitHub Release or package publication has been made; version 0.1.0 is the pre-release development baseline, not a promise of production readiness. Breaking changes may occur before the first tagged release. See CHANGELOG.md and docs/ROADMAP.md.

Standards conformance

This project follows the vendored Portfolio Standards. “Applies” means an automated or documented control exists; release-only evidence is collected before a tagged release.

Standard State Evidence / scope
Responsible-Tech Framework Applies Responsible technology audits, threat model, and data card
Code Quality Applies Makefile gates (ruff, mypy --strict, pytest with a 90% branch-coverage floor, and complexity <= 10), uv.lock, and .python-version
Security & Supply-Chain Applies Security policy; bandit, pip-audit, gitleaks, and CodeQL in CI; SHA-pinned actions and a committed lockfile
CI/CD Applies SHA-pinned, permission-scoped workflows; CI runs the same make verify gate as local development
Release & Versioning Applies — pre-release gap The signed-tag release workflow is committed; the first public tag and its release evidence are still pending
Observability N/A (no hosted telemetry) Stateless local CLI; opt-in JSON operational events only, with no package contents
Performance N/A (no service SLO) No hosted service; bounded PDF/ZIP parsing is covered by the threat model
Accessibility Applies Accessibility boundaries; release review pending the first tag
Internationalization Applies — pre-release gap Scope and release gate; current English-only reports must gain reviewed EN/ES catalogs before a public tag
AI Evaluation Applies ADR 0002; the committed evals/ harnesses (legal-sufficiency refusal, real-filing extraction vs. CEQAnet metadata, citation grounding) with provenance-stamped results; a test rejects any result file without provenance
Documentation Applies README, CONTRIBUTING.md, CHANGELOG.md, CITATION.cff, and the definition of done
Quality & Metrics Applies Metrics ledger in the roadmap; make verify is the merge gate
AI Development Measurement Applies docs/ROADMAP.md declares AI-DEV-MEASUREMENT: APPLIES; the baseline is recorded in the responsible technology audits
Incident Response Applies — local CLI scope Security, privacy, and data-exposure incidents remain in scope even though there is no hosted service
Data Governance Applies The local filing package data card defines the processing and retention boundary

The central standards register is maintained separately and must be updated when this repository is published.

About

Pre-alpha offline CLI that checks CEQA filing packages (NOD, NOE) for objective, correctable technical defects before State Clearinghouse review: bounded PDF inspection, a source-cited declarative rule engine, and HTML/JSON/checklist advisory reports. It never submits, alters, or judges legal sufficiency; NOD/NOE rules are experimental.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages