Rust implementation of the repo's enforcement hooks (ported from .githooks/{pre-commit,commit-msg} via the script-scan pipeline). pre-commit: Gate 0.5 workspace-first compliance + custom-crypto/raw-identity-SQL warnings + AI-attribution file-body block + secure-media /static content-image block, over the staged diff. commit-msg: strips AI-attribution trailers and stamps CONTRACT-STATUS: UNVERIFIED on completion-claiming messages that declare none. The .githooks files are exec shims into these bins; behavior is pinned by githooks-verify's 36 integration tests.
| Tier | tools |
| Role | unclassified (baselined) |
| Path | crates/tools/githooks |
| Edition | 2021 |
| Targets | githook-commit-msg, githook-fix, githook-pre-commit, lexicon-audit, verify-pins, tools_githooks |
| Public items | 65 across 7 modules |
| Tests | 122 |
What it is for
Pure logic for the repo's enforcement hooks, ported line-for-line from .githooks/pre-commit and .githooks/commit-msg (bash). The bash files remain as exec shims so git (and tools-githooks-verify's harness, which runs bash <hook>) keeps working unchanged — those 36 integration tests are the port's behavioral oracle and pin every message substring and exit code asserted below.
Everything here is pure (staged file contents in, messages + violation count out); the bins do the git subprocess I/O with strict argument vectors.
Capabilities
crate root
Pure logic for the repo's enforcement hooks, ported line-for-line from
| Item |
|---|
fn capability_candidates(index_tsv : & str, matched_line : & str) -> Vec <String> |
fn pre_commit_report(files : & StagedFile) -> PreCommitReport |
fn pre_commit_report_with_index(files : & StagedFile, capability_index : & str,) -> PreCommitReport |
fn pre_commit_report_full(files : & StagedFile, capability_index : & str, lexicon_tsv : & str,) -> PreCommitReport |
fn pre_commit_report_full_with_clap_facts(files : & StagedFile, capability_index : & str, lexicon_tsv : & str, clap_facts : & ToolCrateClapFacts,) -> PreCommitReport |
fn migration_path(path : & str) -> bool |
fn parse_ledger(stdout : & str) -> Vec <String> |
fn applied_migration(path : & str, staged_digest : & str, ledger : & String) -> bool |
fn pre_commit_report_full_with_ledger(files : & StagedFile, capability_index : & str, lexicon_tsv : & str, clap_facts : & ToolCrateClapFacts, immutable_paths : & String,) -> PreCommitReport |
fn commit_msg_lexicon_findings(message : & str, lexicon_tsv : & str) -> Vec <vocabulary::Finding> |
CommitMsgResult
Pure logic for the repo's enforcement hooks, ported line-for-line from
| Item |
|---|
pub struct CommitMsgResult |
fn rewrite_commit_message(input : & str) -> CommitMsgResult |
PreCommitReport
Pure logic for the repo's enforcement hooks, ported line-for-line from
| Item |
|---|
pub struct PreCommitReport |
StagedFile
Pure logic for the repo's enforcement hooks, ported line-for-line from
| Item |
|---|
pub struct StagedFile |
fn touched_tool_crate_names(files : & StagedFile) -> Vec <String> |
fn migrations_staged(files : & StagedFile) -> bool |
ToolCrateClapFacts
Pure logic for the repo's enforcement hooks, ported line-for-line from
| Item |
|---|
pub struct ToolCrateClapFacts |
artifact_path (other)
T3a — where a gate artifact is written, and why the branch name cannot be
| Item |
|---|
fn branch_slug(branch : & str) -> String |
fn artifact_path(branch : & str, gate_id : & str) -> String |
authz (other)
Write authorization — the state transition the agent cannot silently skip.
| Item |
|---|
fn authorize_write(auth : Option <& Authorization>, path : & str, surfaced : & SurfacedCandidate, observed : & ObservedDigests,) -> WriteVerdict |
Authorization
Write authorization — the state transition the agent cannot silently skip.
| Item |
|---|
pub struct Authorization |
Authorization :: fn substrate_verdict(& self) -> CheckStatus |
CandidateObligation
Write authorization — the state transition the agent cannot silently skip.
| Item |
|---|
pub struct CandidateObligation |
CandidateObligation :: fn is_open(& self, declaring_file : & str, observed : Option <& str>) -> bool |
Disposition
Write authorization — the state transition the agent cannot silently skip.
| Item |
|---|
pub enum Disposition |
Inspection
Write authorization — the state transition the agent cannot silently skip.
| Item |
|---|
pub struct Inspection |
ObservedDigests
Write authorization — the state transition the agent cannot silently skip.
| Item |
|---|
pub type ObservedDigests: std::collections::BTreeMap <String, String> |
ResolvedDisposition
Write authorization — the state transition the agent cannot silently skip.
| Item |
|---|
pub struct ResolvedDisposition |
ResolvedDisposition :: fn discharges(& self, candidate_file : & str, observed_digest : & str) -> bool |
SubstrateEntry
Write authorization — the state transition the agent cannot silently skip.
| Item |
|---|
pub struct SubstrateEntry |
SubstrateEntry :: fn status(& self) -> CheckStatus |
SubstrateState
Write authorization — the state transition the agent cannot silently skip.
| Item |
|---|
pub enum SubstrateState |
SurfacedCandidate
Write authorization — the state transition the agent cannot silently skip.
| Item |
|---|
pub struct SurfacedCandidate |
WriteVerdict
Write authorization — the state transition the agent cannot silently skip.
| Item |
|---|
pub enum WriteVerdict |
WriteVerdict :: fn is_allowed(& self) -> bool |
Enforcement
Gate contracts — the hand-written half of the split.
| Item |
|---|
pub enum Enforcement |
Enforcement :: fn as_str(self) -> & 'static str |
GateContract
Gate contracts — the hand-written half of the split.
| Item |
|---|
pub struct GateContract |
fn parse_contract(markdown : & str) -> Result <GateContract, String> |
fn parse_catalogue(docs : & (String, String),) -> Result <BTreeMap <String, GateContract>, String> |
NextStep
Gate contracts — the hand-written half of the split.
| Item |
|---|
pub struct NextStep |
Recipe
Gate contracts — the hand-written half of the split.
| Item |
|---|
pub struct Recipe |
evidence (other)
T4 — evidence that can be re-executed, from a fixed allowlist.
| Item |
|---|
pub const ALLOWED_RECIPES: & str; 4 |
fn audit(record : & EvidenceRecord, observed : Option <& str>) -> AuditOutcome |
fn select_sample(n : usize, seed : & str, k : usize) -> Vec <usize> |
AuditOutcome
T4 — evidence that can be re-executed, from a fixed allowlist.
| Item |
|---|
pub enum AuditOutcome |
AuditOutcome :: fn is_ok(& self) -> bool |
EvidenceRecord
T4 — evidence that can be re-executed, from a fixed allowlist.
| Item |
|---|
pub struct EvidenceRecord |
ArtifactIdentity
T3 — what a gate artifact was computed against, and when it goes stale.
| Item |
|---|
pub struct ArtifactIdentity |
Freshness
T3 — what a gate artifact was computed against, and when it goes stale.
| Item |
|---|
pub enum Freshness |
Freshness :: fn is_fresh(& self) -> bool |
fn freshness(recorded : & ArtifactIdentity, current : & ArtifactIdentity) -> Freshness |
Scope
T3 — what a gate artifact was computed against, and when it goes stale.
| Item |
|---|
pub struct Scope |
partial_stage (other)
Refuse a commit whose staged set contains a file that also has unstaged edits.
| Item |
|---|
pub const OPT_OUT: & str |
fn opted_out(env_value : Option <& str>) -> bool |
PartiallyStaged
Refuse a commit whose staged set contains a file that also has unstaged edits.
| Item |
|---|
pub struct PartiallyStaged |
fn partially_staged(staged : & str, unstaged : & str) -> Vec <PartiallyStaged> |
fn refusal(found : & PartiallyStaged) -> String |
pin (other)
T11 — pin the enforcement surface so the constrained party cannot quietly
| Item |
|---|
fn verify(manifest : & PinManifest, observed : & BTreeMap <String, String>) -> PinReport |
PinManifest
T11 — pin the enforcement surface so the constrained party cannot quietly
| Item |
|---|
pub struct PinManifest |
fn parse_manifest(text : & str) -> Result <PinManifest, String> |
PinReport
T11 — pin the enforcement surface so the constrained party cannot quietly
| Item |
|---|
pub struct PinReport |
PinReport :: fn is_clean(& self) -> bool |
PinReport :: fn lines(& self) -> Vec <String> |
ProtectedPrefix
T11 — pin the enforcement surface so the constrained party cannot quietly
| Item |
|---|
pub struct ProtectedPrefix |
How to use it
No examples/ target and no doctest in this crate's rustdoc. The tests listed under Verification are the closest executable usage.
Module structure
tools_githooks
artifact_pathauthzcontractevidenceidentitypartial_stagepin
flowchart TD n_tools_githooks["tools_githooks"] n_tools_githooks --> n_artifact_path["artifact_path"] n_tools_githooks --> n_authz["authz"] n_tools_githooks --> n_contract["contract"] n_tools_githooks --> n_evidence["evidence"] n_tools_githooks --> n_identity["identity"] n_tools_githooks --> n_partial_stage["partial_stage"] n_tools_githooks --> n_pin["pin"]
Public surface
`crate root`
| Item | What it is |
|---|---|
pub struct StagedFile | One staged file: its repo-relative path and the ADDED lines of its staged diff |
pub struct PreCommitReport | Outcome of the pre-commit scan: everything to print, and the violation count that decides the exit code. |
fn capability_candidates(index_tsv : & str, matched_line : & str) -> Vec <String> | Look up existing implementations of whatever matched_line appears to be reimplementing, from the generated capability index |
fn pre_commit_report(files : & StagedFile) -> PreCommitReport | The whole pre-commit scan, pure |
fn pre_commit_report_with_index(files : & StagedFile, capability_index : & str,) -> PreCommitReport | As pre_commit_report, but consults the generated capability index so a suspected reimplementation is reported with the existing implementations. |
fn pre_commit_report_full(files : & StagedFile, capability_index : & str, lexicon_tsv : & str,) -> PreCommitReport | pre_commit_report_with_index, plus the Gate 1.3 lexicon (docs/reference/lexicon-policy.tsv) |
pub struct ToolCrateClapFacts | Per-crates/tools/<name> facts the bin gathers by reading the crate's Cargo.toml and source tree: whether it declares a clap dependency, and whether any file in the crate contains the string assert_conforms |
fn touched_tool_crate_names(files : & StagedFile) -> Vec <String> | The distinct crates/tools/<name> crates with a staged file under their own src/, sorted and deduplicated |
fn pre_commit_report_full_with_clap_facts(files : & StagedFile, capability_index : & str, lexicon_tsv : & str, clap_facts : & ToolCrateClapFacts,) -> PreCommitReport | pre_commit_report_full, plus the crates/tools/* clap-conformance gate: a touched crate whose Cargo.toml depends on clap must have some file that calls assert_conforms, or the commit is blocked |
fn migration_path(path : & str) -> bool | Is path a migration file: a .sql directly under a migrations/ directory |
fn migrations_staged(files : & StagedFile) -> bool | Does this commit stage a migration? Only then is the ledger worth a database round trip (B-111). |
fn parse_ledger(stdout : & str) -> Vec <String> | corpus applied-migrations output: one source digest per line, blanks dropped. |
fn applied_migration(path : & str, staged_digest : & str, ledger : & String) -> bool | Are these staged bytes the ones the ledger applied? Keyed on the source digest — B-017's own key — so a filename shared across modules cannot collide, and an edited applied migration (digest changed) is NOT immutable-equal and blocks like any other file |
fn pre_commit_report_full_with_ledger(files : & StagedFile, capability_index : & str, lexicon_tsv : & str, clap_facts : & ToolCrateClapFacts, immutable_paths : & String,) -> PreCommitReport | pre_commit_report_full_with_clap_facts, plus the set of staged paths whose bytes the migration ledger holds (decided by the bin through applied_migration): a Gate 1.3 finding in one of them is reported, not counted, because the only compliant edit is one B-017 forbids (B-111) |
pub struct CommitMsgResult | Outcome of the commit-msg rewrite. |
fn commit_msg_lexicon_findings(message : & str, lexicon_tsv : & str) -> Vec <vocabulary::Finding> | Gate 1.3 lexicon check for a commit message — the author's own prose, at a gate that already fires |
fn rewrite_commit_message(input : & str) -> CommitMsgResult | The whole commit-msg rewrite, pure: strip attribution lines, trim trailing blanks, then stamp CONTRACT-STATUS: UNVERIFIED on a completion-claiming message that declares no status |
`artifact_path`
| Item | What it is |
|---|---|
fn branch_slug(branch : & str) -> String | A filesystem-safe directory name for branch |
fn artifact_path(branch : & str, gate_id : & str) -> String | Repo-relative path of one gate's artifact |
`authz`
| Item | What it is |
|---|---|
pub struct Inspection | Proof that a file was read: its path, and the digest the reader observed. |
pub enum Disposition | What a reuse candidate resolved to. |
pub struct ResolvedDisposition | A discharged obligation. |
ResolvedDisposition :: fn discharges(& self, candidate_file : & str, observed_digest : & str) -> bool | Whether this discharges an obligation on candidate_file |
pub struct CandidateObligation | A reuse candidate surfaced by the capability index, and its resolution. |
pub struct SurfacedCandidate | A candidate as the runner issued it: identity bound to declaring file |
CandidateObligation :: fn is_open(& self, declaring_file : & str, observed : Option <& str>) -> bool | Whether this still blocks |
pub enum SubstrateState | The outcome of searching for an existing implementation of one capability. |
pub struct SubstrateEntry | One capability the task needs, and what was found. |
SubstrateEntry :: fn status(& self) -> CheckStatus | Collapse to a verdict |
pub struct Authorization | An open authorization to write source, scoped to paths. |
Authorization :: fn substrate_verdict(& self) -> CheckStatus | Roll the substrate inventory up to one verdict |
pub enum WriteVerdict | Whether a write may proceed. |
WriteVerdict :: fn is_allowed(& self) -> bool | Whether the write may proceed. |
pub type ObservedDigests: std::collections::BTreeMap <String, String> | Current digest of each declaring file, as the runner sees it. |
fn authorize_write(auth : Option <& Authorization>, path : & str, surfaced : & SurfacedCandidate, observed : & ObservedDigests,) -> WriteVerdict | Decide whether a source write to path is authorized |
`contract`
| Item | What it is |
|---|---|
pub enum Enforcement | Whether a gate can refuse a write |
Enforcement :: fn as_str(self) -> & 'static str | Stable name for the runner's per-gate table. |
pub struct Recipe | An allowlisted evidence recipe |
pub struct NextStep | What the runner emits when a gate is unsatisfied. |
pub struct GateContract | One gate's requirements. |
fn parse_contract(markdown : & str) -> Result <GateContract, String> | Parse one gate contract from its markdown document |
fn parse_catalogue(docs : & (String, String),) -> Result <BTreeMap <String, GateContract>, String> | Parse a whole catalogue: gate id → contract |
`evidence`
| Item | What it is |
|---|---|
pub const ALLOWED_RECIPES: & str; 4 | Every recipe the runner knows how to execute |
pub struct EvidenceRecord | One executed piece of evidence, as recorded in an artifact. |
pub enum AuditOutcome | What an audit of one record concluded. |
AuditOutcome :: fn is_ok(& self) -> bool | Whether the record survived the audit. |
fn audit(record : & EvidenceRecord, observed : Option <& str>) -> AuditOutcome | Audit one record against a freshly observed digest |
fn select_sample(n : usize, seed : & str, k : usize) -> Vec <usize> | Deterministically choose up to k of n records to re-execute |
`identity`
| Item | What it is |
|---|---|
pub struct Scope | What a gate declared it examines |
pub struct ArtifactIdentity | The repository state an artifact was computed against. |
pub enum Freshness | Whether an artifact still describes the current state. |
Freshness :: fn is_fresh(& self) -> bool | Whether the artifact may still be relied on. |
fn freshness(recorded : & ArtifactIdentity, current : & ArtifactIdentity) -> Freshness | Compare a recorded identity against the current one |
`partial_stage`
| Item | What it is |
|---|---|
pub struct PartiallyStaged | A path that is staged and also carries unstaged modifications. |
pub const OPT_OUT: & str | Env var that permits partially-staged files for one commit. |
fn partially_staged(staged : & str, unstaged : & str) -> Vec <PartiallyStaged> | Intersect the staged and unstaged path sets |
fn refusal(found : & PartiallyStaged) -> String | The blocking message |
fn opted_out(env_value : Option <& str>) -> bool | True when the operator has opted out for this commit. |
`pin`
| Item | What it is |
|---|---|
pub struct ProtectedPrefix | A path prefix whose every file must be pinned |
pub struct PinManifest | The manifest: protected prefixes, and the expected digest of every file. |
pub struct PinReport | What a verification found. |
PinReport :: fn is_clean(& self) -> bool | Whether the enforcement surface is intact. |
PinReport :: fn lines(& self) -> Vec <String> | Human-readable lines, each naming what to do. |
fn parse_manifest(text : & str) -> Result <PinManifest, String> | Parse the manifest |
fn verify(manifest : & PinManifest, observed : & BTreeMap <String, String>) -> PinReport | Compare the manifest against the observed state of the tree |
Re-exports. Exported here, defined elsewhere.
| Export | Defined in |
|---|---|
foundation_lexiconasvocabulary | foundation_lexiconasvocabulary |
Boundary
Reaches into foundation.
Shares tier tools with 84 other crates: tools-advisory-reach, tools-archive-guard, tools-artifact-scaffold, tools-ask-ai-core, tools-ask-ais, tools-ask-gemini, tools-book, tools-book-report, … (84 total).
_What this crate deliberately does NOT own is a judgment. No committed registry records one for it, so none is stated here._
Where it sits
| Tier (ontology) | tools |
| Architectural role (taxonomy) | unclassified (baselined) |
| Location | crates/tools/githooks |
| Vocabulary in force (lexicon) | current |
Tier flow. Which tiers this crate's own edges cross.
flowchart LR n_tools["tools"] --> n_foundation["foundation"]
Dependencies
Runtime, in this workspace.
| Crate | Tier | Optional | Only on |
|---|---|---|---|
| `foundation-audit-log` | foundation | no | always |
| `foundation-bounded-io` | foundation | no | always |
| `foundation-lexicon` | foundation | no | always |
Runtime, from outside the workspace.
| Crate | Requirement | Features | Optional | Only on |
|---|---|---|---|---|
anyhow | ^1 | — | no | always |
regex | ^1 | — | no | always |
sha2 | ^0.10 | — | no | always |
toml | ^0.8 | — | no | always |
Development. None.
Build. None.
Depended on by. Nothing in this workspace.
Signal flow — what reaches this crate, and what it reaches.
flowchart LR SELF["tools-githooks"] SELF -->|runtime| n_foundation_audit_log["foundation-audit-log"] SELF -->|runtime| n_foundation_bounded_io["foundation-bounded-io"] SELF -->|runtime| n_foundation_lexicon["foundation-lexicon"] classDef self fill:#1f883d,stroke:#1f883d,color:#fff; class SELF self;
Feature flags
No Cargo features are defined: every capability is unconditional, so no consumer can receive a half-wired crate.
Targets
| Kind | Name | Source |
|---|---|---|
| bin | githook-commit-msg | `src/bin/commit_msg.rs` |
| bin | githook-fix | `src/bin/fix.rs` |
| bin | githook-pre-commit | `src/bin/pre_commit.rs` |
| bin | lexicon-audit | `src/bin/lexicon_audit.rs` |
| bin | verify-pins | `src/bin/verify_pins.rs` |
| lib | tools_githooks | `src/lib.rs` |
Error model
No public error type was detected: no public item declares a type named *Error, and no public signature returns one.
Operational characteristics
| Property | Evidence |
|---|---|
| async public surface | none detected |
| async runtime | none detected |
| database access | none detected |
| network I/O | none detected |
| unsafe code | none detected |
| environment variables | yes |
No unsafe block, unsafe fn, unsafe impl or unsafe trait was found by the parser anywhere in this crate's source.
Configuration
| Variable | Read in |
|---|---|
CARGO_MANIFEST_DIR | src/contract.rs |
GITHOOKS_WORKSPACE_ROOT | src/bin/pre_commit.rs |
Related capabilities
No workspace crate depends on this one.
Verification
| Kind | Count |
|---|---|
| Unit tests | 122 |
| Integration tests | 0 |
| Examples | 0 |
| Doctests | 0 |
Evidence by module. How often each public module is named by something executable.
| Module | Tests | Examples | Consumers |
|---|---|---|---|
crate root | 17 | 0 | 0 |
artifact_path | 2 | 0 | 0 |
authz | 11 | 0 | 0 |
contract | 6 | 0 | 0 |
evidence | 5 | 0 | 0 |
identity | 4 | 0 | 0 |
partial_stage | 5 | 0 | 0 |
pin | 5 | 0 | 0 |
What the tests establish, by name:
a_slash_in_a_branch_name_does_not_become_a_directory—src/artifact_path.rsa_traversal_in_a_branch_name_cannot_escape_the_gates_directory—src/artifact_path.rsa_traversal_in_a_gate_id_cannot_escape_either—src/artifact_path.rsa_unicode_or_empty_name_still_yields_a_usable_path—src/artifact_path.rsa_very_long_branch_name_is_bounded—src/artifact_path.rsbranches_colliding_on_the_stem_still_get_distinct_paths—src/artifact_path.rsthe_same_branch_always_maps_to_the_same_path—src/artifact_path.rsthe_slug_stays_readable_rather_than_being_only_a_hash—src/artifact_path.rsa_current_dir_component_is_not_a_wrong_refusal—src/authz.rsa_fully_discharged_authorization_permits_the_write—src/authz.rsa_negative_claim_needs_an_allowlisted_recipe_with_arguments—src/authz.rsa_reason_naming_the_candidate_discharges_nothing_without_inspection—src/authz.rsa_sibling_path_sharing_a_prefix_is_outside_scope—src/authz.rsa_stale_obligation_no_longer_surfaced_does_not_block—src/authz.rsa_trailing_slash_on_the_scope_does_not_change_the_decision—src/authz.rsa_traversal_segment_is_refused_rather_than_normalized_away—src/authz.rsa_verified_inspection_discharges_a_rejection—src/authz.rsa_write_with_no_authorization_is_refused—src/authz.rsaccepting_a_candidate_also_requires_inspection—src/authz.rsan_empty_substrate_inventory_is_unverified_never_pass—src/authz.rsan_inspection_of_an_older_version_of_the_file_does_not_discharge—src/authz.rsinspecting_a_file_the_agent_chose_does_not_discharge—src/authz.rsomitting_an_index_surfaced_candidate_from_the_obligations_blocks—src/authz.rsone_inspection_does_not_discharge_a_second_candidate—src/authz.rsunknown_coverage_blocks_rather_than_reading_as_a_negative_finding—src/authz.rsselection_defaults_to_staged_and_never_treats_a_flag_as_a_path—src/bin/fix.rsaggregate_groups_findings_by_term—src/bin/lexicon_audit.rsaggregate_of_empty_input_is_empty—src/bin/lexicon_audit.rsaggregate_preserves_order_within_a_term—src/bin/lexicon_audit.rsa_catalogue_names_the_file_that_failed—src/contract.rs- _… 92 more_
Documentation coverage
| Measure | Documented | Total |
|---|---|---|
| Public items with rustdoc | 65 | 65 |
Public modules with a //! block | 7 | 7 |
pie showData
title Public items with rustdoc
"Documented" : 65
"No rustdoc detected" : 0
Metrics
| Metric | Value |
|---|---|
| Rust source files | 13 |
| Source lines | 4392 |
| Code lines | 3124 |
| Public API items | 65 |
| Public modules | 7 |
| Tests | 122 |
| Examples | 0 |
| Cargo features | 0 |
| Direct runtime dependencies | 7 |
| Workspace reverse dependencies | 0 |
pie showData
title Public API by kind
"constant" : 2
"enum" : 6
"function" : 26
"method" : 10
"struct" : 20
"type alias" : 1
pie showData
title Rust source composition
"Code" : 3124
"Blank or comment" : 1268
Generation
Rendered by tools-corpus corpus readme from repository evidence alone, renderer schema 2, lexicon current. No model, network service or database was consulted. Regenerate with tools-corpus corpus readme --write; verify with --check.