Iteration comes first: run scripts/gates.sh --quick, deploy, watch the live
module, then run full gates and land in the background using the
commands below. Every change still passes
scripts/gates.sh and required CI before it lands:
scripts/gates.sh --quick # before deploy: host lint, units, launch/wire smoke
scripts/gates.sh # repository checks, fmt, clippy, unit tests, doctests
scripts/gates.sh --e2e # also builds ck-insula and runs skeleton_e2eIt stops at the first failure and exits 0 when everything passed, 1 when a
gate failed and 2 when it could not check. It runs, in order:
- repository path dependencies (
scripts/path-dependencies.py, Python 3.11+); - planted violations for every source scanner (
scripts/mutation-scans.sh); deploy-policy tests against local Git histories (scripts/deploy-tests.py); - a compile of every Python instrument and a parse of every shell script;
- the endpoint host manifest (
scripts/endpoint-hosts.py); - the CI preconditions a train depends on (
scripts/check-train-preconditions.sh); - the public
.cortexkit/layout (scripts/check-cortexkit-layout.sh); cargo fmt --check;cargo clippy --workspace --all-targets -- -D warnings, and again on the newest stable toolchain when the local one is older, because CI lints with the newest;cargo test --workspace --lib --binswithTMPDIRpointed at an empty folder that must still be empty afterwards;cargo test --workspace --doc;- with
--e2e,cargo build -p quota-module --binsandcargo test -p quota-module --test it skeleton_e2e::; - an orphan-module check: every
src/*.rsmust be declared by amodin its crate root.
If the gates rewrite Cargo.lock they say so; commit that as its own
dependency change or restore it. docs/verifying.md
covers what the gate does not run.
--quick keeps all cheap repository checks, fmt, host-toolchain workspace
clippy and the whole lib/bin suite (including manifest and Codex reset journal
tests). It also builds the module and runs the single launch/usage.get wire
smoke and consumer completeness-envelope example. It skips newest-stable
installation/lint, doctests and the long refresh integration suite; those belong
after deploy, not on the change-to-running path. See the
deploy-then-land commands.
Write commit messages with a quoted heredoc so backticked identifiers survive:
git commit -F - <<'MSG'
provider: one-line summary
What changed and why, in plain sentences. `identifiers` stay intact because
the quoted delimiter stops the shell from expanding them.
MSGWith git commit -m and a double-quoted message, the shell runs each
backtick-quoted word as a command and leaves a gap where the identifier was;
git exits 0 and nothing else notices.
master is protected: it accepts a push only when the commit being pushed
has passing Test (ubuntu) and Test (windows) checks. Maintainers land with
scripts/train.sh (after deploying locally; see the background flow above):
scripts/train.sh <name>It refuses a dirty tree, refuses while an earlier train has not landed (a
second train would move master's tip off the commit the first one is waiting
on), and refuses a commit message that looks like it lost a backticked
identifier. It then pushes HEAD to train/<name>, waits for the CI run on
that commit, confirms every check branch protection requires has passed, and
pushes the same commit to master. On failure it leaves the train branch in
place for inspection and exits non-zero.
Deployment accepts a clean local master ahead of freshly fetched
origin/master and lists the unlanded commits; it refuses behind or diverged
history. CI still gates the public push, not the local restart. If CI fails,
fix forward on local master, redeploy, and retry the same train name after the
previous run exits. The name identifies train/<name>; reusing it replaces the
failed train instead of stranding it. Never force-push protected master or
overlap trains.
Outside contributors open a pull request; CI runs the same jobs on it.
- Create
crates/quota-core/src/<provider>.rs(hyphens become underscores) implementingUsageProviderfromprovider.rs:name(),handles()(local enumeration only, never network I/O) andfetch_handle(). Map every failure to aFetchErrorvariant; the variant decides the publishederrorClassand whether the refresher treats it as transient. - Declare it with
pub mod <provider>;incrates/quota-core/src/lib.rsand add it toRegistry::with_defaults. - If it reads a vault credential, add its id family to
CREDENTIAL_FAMILIESinvault_handles.rs. A cookie provider returnstruefromis_cookie_based(), reads its deposit throughcookie_vault.rs, and needs a row in the cookie-capture table indocs/consumer-contract.md;cookie_capture_urls_match_the_providers_fetch_urlschecks that row against the source. - If the provider has a canonical models.dev slug, add it to
api_provider_nameinlib.rs. - Record each endpoint constant's host in
scripts/endpoint-hosts.py. - Add the provider to
docs/provider-matrix.mdand update itsN providers registered.line;the_documented_provider_count_matches_the_registrycompares it with the registry. - Test the normalizer against fixtures in the module's own
#[cfg(test)]block. A test that needs a real session goes incrates/quota-core/tests/it/<provider>_live.rsand is#[ignore]d.
Shared code lives in quota-core beside its callers' concerns: HTTP requests,
error excerpts and URL stripping in http.rs; environment and credential-file
reads in env.rs; money amounts in money.rs; text and JSON scanning in
text.rs and json_scan.rs; credential-vault access in credential_source.rs
and vault_handles.rs. A helper used by one provider stays in that provider's
file. Code that only the subc module needs (frame loop, vault client, health
report) goes in crates/quota-module/src/.
- Registry and cross-provider behaviour:
crates/quota-core/src/tests.rs. - One provider's parsing: that provider's
#[cfg(test)]block. - The subc wire, health report and config loading: the
testsmodule incrates/quota-module/src/main.rs. - A spawned
ck-insulaagainst an in-process daemon:crates/quota-module/tests/it/skeleton_e2e.rs. - A test that creates files uses
crate::tests::ResetTempDir, which removes its folder on drop; the gate fails when a unit test leaves anything inTMPDIR.
A test that guards a costly property (money spent, exactly-once, credential
trust, a wire field, data loss) should also get a row in mutations.toml; see
docs/mutations.md.