Skip to content

Commit b570ebb

Browse files
authored
ci: add the typos spell check (#632)
1 parent 9570c7a commit b570ebb

13 files changed

Lines changed: 93 additions & 25 deletions

File tree

.github/workflows/ci.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ jobs:
9393
check_agents_md:
9494
name: Check AGENTS.md Links
9595
runs-on: ubuntu-latest
96+
timeout-minutes: 10
9697
permissions:
9798
contents: read
9899
steps:
@@ -133,6 +134,18 @@ jobs:
133134
- name: Verify documentation structure and RFC registry
134135
run: python3 scripts/check-docs.py
135136

137+
# Spelling gate: docs/dev/ci.md, "Pull-request gates". typos also reads
138+
# a sibling typos.toml / _typos.toml, so refuse a second config first.
139+
- name: Refuse a second typos config
140+
run: |
141+
if [ -e typos.toml ] || [ -e _typos.toml ]; then
142+
echo "::error::every typos exemption lives in .typos.toml; remove typos.toml / _typos.toml"
143+
exit 1
144+
fi
145+
146+
- name: Check spelling
147+
uses: crate-ci/typos@d43b6c087ac471e2ea7b8af622ff15f05c0c365b # v1.50.1
148+
136149
workflow_action_pins:
137150
name: Check Workflow Action Pins
138151
runs-on: ubuntu-latest

.typos.toml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Known-misspelling check (crate-ci/typos). CI runs it from the repository
2+
# root, and so must a local run. Every exemption lives in this file, one
3+
# comment per entry saying why the token is intended where it occurs.
4+
5+
[files]
6+
ignore-hidden = false # workflows, issue templates and .cargo/ hold prose too
7+
extend-exclude = [
8+
".git/",
9+
".typos.toml", # names the tokens it exempts
10+
]
11+
12+
[default]
13+
# The "mis-" prefix (mis-renamed, mis-estimate); a bare "mis" still fires.
14+
extend-ignore-re = ['\bmis-\w+']
15+
16+
# Tokens that occur in prose and code alike.
17+
[default.extend-words]
18+
AKS = "AKS" # Azure Kubernetes Service (RFC 0029 assets)
19+
BA = "BA" # AB/BA measurement-order labels (RFCs 0023, 0031); also the hex fragment of the short commit SHAs 2aab48ba (RFC 0019) and da466ba (vocabulary guard test). Cost: a bare "ba" typed for "by" passes
20+
fof = "fof" # friend-of-friend: pattern variable in test queries and the query reference
21+
unparseable = "unparseable" # accepted variant; appears in operator-facing error strings and docs
22+
23+
# Tokens that exist only inside Rust sources; the same misspelling in
24+
# Markdown still fires.
25+
[type.rust.extend-identifiers]
26+
write_applyable_state = "write_applyable_state" # cluster test helper: state that can be applied
27+
seed_applyable_state = "seed_applyable_state" # same family
28+
write_cluster_applyable_state = "write_cluster_applyable_state" # CLI test helper, same meaning
29+
30+
[type.rust.extend-words]
31+
alph = "alph" # prefix-filter probe value in literal_filters.rs
32+
celle = "celle" # matrix cell "e" in server/tests/multi_graph.rs (cella … cellj)
33+
froms = "froms" # plural of `from`, paired with `tos`, in the DST edge fixtures
34+
nmae = "nmae" # deliberately wrong parameter name (stored_queries.rs, vocabulary guard lib.rs tests)
35+
pn = "pn" # external-id prefix ("pn:12345") in a consistency fixture
36+
propert = "propert" # deliberate substring assertion on a typecheck error message
37+
referenc = "referenc" # deliberate substring match on Lance's error text (table_ops.rs, branch_control.rs)

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ cargo clippy --workspace --all-targets --locked -- \
136136
bash scripts/check-agents-md.sh
137137
python3 scripts/check-docs.py
138138
python3 scripts/check-workflow-action-pins.py
139+
typos # from the repository root; version pinned in ci.yml; exemptions in .typos.toml
139140
```
140141

141142
S3 suites require `OMNIGRAPH_S3_TEST_BUCKET` and the documented `AWS_*`

crates/omnigraph-dst/tests/scenarios.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3741,7 +3741,7 @@ fn dst_merge_duplicates_born_on_both_edge() {
37413741
omnigraph::dst_clock::install_logical_clock();
37423742
let storage: Arc<dyn StorageAdapter> = Arc::new(ObjectStorageAdapter::in_memory());
37433743
let db = Omnigraph::init_with_storage(
3744-
"shared-memory://dst-classe-probe",
3744+
"shared-memory://dst-class-probe",
37453745
TEST_SCHEMA,
37463746
storage.clone(),
37473747
InitOptions::default(),

crates/omnigraph-server/tests/data_routes.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2631,7 +2631,7 @@ async fn change_concurrent_updates_same_key_return_typed_pre_effect_conflicts()
26312631
.unwrap();
26322632
let app = build_app(state);
26332633

2634-
// Spawn N=8 concurrent UPDATEs on Alice (from test.jsonl, age=30 at V0)
2634+
// Spawn N=8 concurrent UPDATE mutations on Alice (from test.jsonl, age=30 at V0)
26352635
// writing distinct ages.
26362636
const N: usize = 8;
26372637
let mut handles = Vec::with_capacity(N);

crates/omnigraph/src/db/manifest/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1401,7 +1401,7 @@ async fn test_batch_create_table_versions_allows_owner_branch_handoff_at_same_ve
14011401
}
14021402

14031403
/// Regression (PR #307 review — Cursor Bugbot High + Codex P2): the post-publish
1404-
/// fold (`#1b`) must reflect an owner-branch handoff. A handoff UPDATEs a
1404+
/// fold (`#1b`) must reflect an owner-branch handoff. A handoff updates a
14051405
/// `table_version` row IN PLACE at the SAME Lance version with a new
14061406
/// `table_branch` — merge-insert `UpdateAll` on the deterministic
14071407
/// `version_object_id(table_key, version)`, so `__manifest` ends with one row

crates/omnigraph/src/exec/query.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2871,7 +2871,7 @@ async fn execute_expand_bound(
28712871
.downcast_ref::<StringArray>()
28722872
.ok_or_else(|| OmniError::manifest(format!("edge '{}' is not Utf8", key_col)))?
28732873
.clone();
2874-
let opps = batch
2874+
let opposites = batch
28752875
.column_by_name(opp_col)
28762876
.ok_or_else(|| OmniError::manifest(format!("edge batch missing '{}'", opp_col)))?
28772877
.as_any()
@@ -2888,7 +2888,7 @@ async fn execute_expand_bound(
28882888
for r in 0..batch.num_rows() {
28892889
// Undirected probes both orientations; a self-loop row would
28902890
// match the same wide row through both, so emit it only once.
2891-
if probe_idx == 1 && keys.value(r) == opps.value(r) {
2891+
if probe_idx == 1 && keys.value(r) == opposites.value(r) {
28922892
continue;
28932893
}
28942894
let Some(wide_rows) = rows_by_src.get(keys.value(r)) else {
@@ -2897,7 +2897,7 @@ async fn execute_expand_bound(
28972897
for &wide_row in wide_rows {
28982898
matches.push((
28992899
wide_row,
2900-
opps.value(r).to_string(),
2900+
opposites.value(r).to_string(),
29012901
batch_idx,
29022902
r,
29032903
edge_ids.value(r).to_string(),
@@ -3296,7 +3296,7 @@ async fn execute_expand_bfs(
32963296
.ok_or_else(|| {
32973297
OmniError::manifest(format!("edge '{}' is not Utf8", key_col))
32983298
})?;
3299-
let opps = batch
3299+
let opposites = batch
33003300
.column_by_name(opp_col)
33013301
.ok_or_else(|| {
33023302
OmniError::manifest(format!("edge batch missing '{}'", opp_col))
@@ -3308,7 +3308,7 @@ async fn execute_expand_bfs(
33083308
})?;
33093309
for r in 0..batch.num_rows() {
33103310
let k = src.interner.get_or_insert(keys.value(r));
3311-
let o = src.interner.get_or_insert(opps.value(r));
3311+
let o = src.interner.get_or_insert(opposites.value(r));
33123312
src.neighbor_map.entry(k).or_default().push(o);
33133313
}
33143314
}

crates/omnigraph/tests/failpoints.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5394,7 +5394,7 @@ async fn s3_load_recovers_after_publisher_failure_without_reopen() {
53945394
}
53955395

53965396
// Same-handle follow-up load: the entry heal LISTs __recovery/ on
5397-
// S3, rolls the sidecar forward, DELETEs it, and the write lands.
5397+
// S3, rolls the sidecar forward, issues a DELETE for it, and the write lands.
53985398
load_jsonl(
53995399
&db,
54005400
r#"{"type":"Person","data":{"name":"Bob","age":25}}

crates/omnigraph/tests/writes.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -923,7 +923,7 @@ async fn overlapping_delete_predicates_do_not_double_count_affected() {
923923
}
924924

925925
/// The overlap-exclusion filter must use SQL `IS NOT TRUE`, not `NOT`: a prior
926-
/// delete predicate referencing a NULLable column must NOT drop a later
926+
/// delete predicate referencing a nullable column must NOT drop a later
927927
/// statement's matching row just because that column is NULL (SQL UNKNOWN).
928928
/// With `NOT (age > 30)`, a row with NULL `age` makes the clause UNKNOWN and the
929929
/// row is filtered out of `deleted_ids` — skipping its cascade (orphaned edges),

docs/dev/ci.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,22 @@ the same context also rejects any pull request whose own diff adds a
5858
conflict-marker line in any file type, annotating each offending file and
5959
line; markers already on the base branch never fail an unrelated pull
6060
request. There is no exemption; a document that must quote a conflict block
61-
indents the markers one space.
61+
indents the markers one space. After the documentation checks, the same
62+
context runs `typos` (`crate-ci/typos`, pinned by commit) over every tracked
63+
text file, hidden paths such as `.github/` included; the tool itself skips
64+
`Cargo.toml` manifests, lock files and binaries. It matches each word against
65+
a list of known misspellings, not a dictionary: an unknown word never fires,
66+
and an identifier fires only when one of the words it splits into is on the
67+
list (a CamelCase fragment or a short abbreviation can be one). A
68+
flagged token that is correct where it occurs gets one commented line in
69+
`.typos.toml`: under `[default.extend-words]` when it appears in prose and
70+
code alike, under `[type.rust.extend-words]` (or `extend-identifiers` for one
71+
exact identifier) when it exists only in Rust sources, so the same
72+
misspelling in Markdown still fires; a hyphenated prefix goes in
73+
`extend-ignore-re`; a generated text file gets an `extend-exclude` glob.
74+
Every exemption lives in that one file: the job refuses a sibling
75+
`typos.toml` or `_typos.toml`, and CI ignores a config file in a
76+
subdirectory (a local run inside that subdirectory would not).
6277

6378
`Graph Vocabulary Guard` remains a required reporting context, but its
6479
substrate-sized audit steps are currently disabled everywhere (decision of
@@ -191,11 +206,12 @@ python3 scripts/check-workflow-action-pins.py
191206
python3 scripts/check-release-vocabulary-gates.py
192207
python3 scripts/check-container-binary-contract.py
193208
python3 scripts/check-azure-admission-boundary.py
209+
typos # from the repository root; a subdirectory run scans only that subtree
194210
actionlint .github/workflows/*.yml
195211
shellcheck scripts/*.sh
196212
```
197213

198-
`actionlint` and `shellcheck` are developer tools, not installed by Cargo. Run the applicable subset when a change does not touch their surface.
214+
`typos` (`cargo install typos-cli --locked --version 1.50.1`, the version `ci.yml` pins; the misspelling list grows per release, so a newer local binary can flag words CI accepts), `actionlint` and `shellcheck` are developer tools, not workspace dependencies. Run the applicable subset when a change does not touch their surface.
199215

200216
## Release workflows
201217

0 commit comments

Comments
 (0)