A portable contract
One manifest describes ownership, a 12-column schema, sovereignty metadata, and executable quality expectations. Two small CSV files demonstrate success and failure without infrastructure or containers.
TokMesh Lite · Session 02 · Field workbook
We turned an abstract Rust validator into a runnable synthetic Nigeria lithium learning product, broke its inputs deliberately, and learned to read every failure as evidence.
22 August 2026 · checkpoint 2e00acdTokMesh Lite already knew how to parse a YAML manifest and validate CSV data. This session gave that engine a concrete, runnable learning product. All records are explicitly synthetic: they are test data, not claims about real mines, operators, reserves, grades, or production.
One manifest describes ownership, a 12-column schema, sovereignty metadata, and executable quality expectations. Two small CSV files demonstrate success and failure without infrastructure or containers.
Missing files, malformed YAML, broken CSV records, and semantically invalid values showed us different failure boundaries and exit codes. We repaired one layer at a time.
examples/nigeria-lithium/ ├── product.yaml # contract: schema + quality + ownership ├── valid.csv # eight rows expected to pass └── invalid.csv # eight rows with controlled defects
| Contract area | Declared behavior | Why it matters |
|---|---|---|
| Identity | id, name, semantic version, description | Lets people and automation identify the product and its release. |
| Ownership | Nigeria · TokMesh Learning Laboratory | A data product has an accountable owner, not merely a file location. |
| Schema | 12 required columns | Defines the expected shape and primitive value types. |
| Quality | Unique IDs, accepted categories, numeric ranges | Turns expectations into executable checks. |
| Sovereignty | national, allowed country Nigeria | Introduces policy metadata without pretending the CLI enforces a full governance platform. |
| License | CC0-1.0 | Makes the synthetic example easy to reuse. |
site_id, site_name, jurisdiction, district_code, zone, deposit_type, primary_mineral, li2o_grade_percent, activity, latitude, longitude, and sample_date.
The CLI process ran, but it could not read product.yaml. That is an execution/input problem, so the command returned exit code 2, not validation exit code 1.
error: could not read .../examples/nigeria-lithium/product.yaml:
No such file or directory (os error 2)After the file existed, parsing reached line 2 and stopped. YAML uses indentation as structure, so a misplaced mapping key changes—or destroys—the document.
mapping values are not allowed in this context at line 2 column 7The header had 9 fields in the early draft while subsequent physical lines had 4, 6, or 7. A CSV reader expects a consistent record width unless multiline fields are correctly quoted.
awk -F',' '{print "line", NR, "has", NF, "fields"}' \
examples/nigeria-lithium/valid.csv
line 1 has 9 fields
line 2 has 4 fields
line 3 has 6 fieldsA backup was made first. Wrapped fragments and leading indentation were repaired, then awk confirmed that all nine physical lines—one header plus eight records—contained exactly 12 fields.
line 1 has 12 fields
...
line 9 has 12 fieldsCopying valid.csv created a structurally useful starting point, but it contained no broken promise. The validator correctly returned 0. We then introduced controlled defects one at a time.
We used two already agreed public seams: the CLI process boundary for exit behavior and validate_data for detailed issue reports. We did not test private helper functions.
| CSV location | Deliberate value | Expected issue | Status |
|---|---|---|---|
row[3].site_id | NG-LI-000001 repeated | quality.unique.duplicate | GREEN |
row[4].jurisdiction | Unknown | quality.accepted_values.rejected | GREEN |
row[5].li2o_grade_percent | 12.50 | quality.range.outside | GREEN |
row[6].site_name | empty field | data.value.required | GREEN |
row[7].sample_date | still valid: 2026-01-20 | data.value.type | NEXT RED |
#[test]
fn nigeria_lithium_invalid_example_rejects_missing_site_name() {
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
let manifest_path = root.join("examples/nigeria-lithium/product.yaml");
let data_path = root.join("examples/nigeria-lithium/invalid.csv");
let manifest = load_manifest(&manifest_path).expect("manifest should parse");
let report = validate_data(&manifest, &data_path)
.expect("CSV should be readable");
assert!(report.issues.iter().any(|issue| {
issue.code == "data.value.required"
&& issue.location == "row[6].site_name"
}));
}
let status = Command::new(env!("CARGO_BIN_EXE_tokmesh-lite"))
.args([
"data",
"validate",
&format!("{root}/examples/nigeria-lithium/product.yaml"),
&format!("{root}/examples/nigeria-lithium/invalid.csv"),
])
.status()
.expect("CLI should run");
assert_eq!(status.code(), Some(1));
env!(...)The macro reads a Cargo-provided value while compiling. CARGO_MANIFEST_DIR gives tests a stable repository root instead of relying on the terminal’s current directory.
Path::new and .joinThey build filesystem paths using platform-aware path handling. Joining path components is clearer and safer than hand-building separators.
.expect(...)Unwraps a successful Result. If setup fails, the test stops with the supplied explanation, making fixture problems readable.
.iter().any(...)Borrows each issue and returns true as soon as one issue satisfies the closure. It does not consume the report.
|issue| { ... }An inline function receiving one issue. The body defines exactly what matching evidence means.
assert! and assert_eq!assert! expects a Boolean condition. assert_eq! compares two values and prints both sides when they differ.
&&Both the stable issue code and exact location must match. This prevents a test from passing because an unrelated problem happened elsewhere.
Command::newStarts the compiled CLI as a user would, providing confidence in arguments, printed behavior, and operating-system exit status.
| Command or token | Meaning |
|---|---|
cargo run -- product validate FILE | Builds if necessary, runs the binary, and passes everything after -- to TokMesh Lite. |
cargo fmt | Rewrites Rust source into standard Rust formatting. |
cargo fmt --check | Checks formatting without changing files; useful before a commit or in CI. |
cargo test | Runs unit tests, integration tests, and documentation tests. |
cargo test --test validation | Selects the integration-test binary built from tests/validation.rs. |
NAME -- --exact | Selects a test name; the separator passes --exact to Rust’s test harness so partial names do not match. |
cargo clippy | Runs Rust’s linter for likely mistakes and non-idiomatic code. |
--all-targets | Checks library, binary, tests, examples, and other Cargo targets. |
--all-features | Enables every declared Cargo feature while checking. |
-- -D warnings | Passes a compiler flag after the separator; -D means deny, so warnings fail the command. |
\ at line end | The shell continues the same command on the next physical line. It improves readability without creating another command. |
| | A pipe sends standard output from the command on its left to standard input of the command on its right. |
sed -i '...' | Runs the external Unix stream editor and modifies a file in place. Zsh or Bash parses the command; sed performs the edit. |
sed -n '6p' | Suppresses automatic printing and prints only physical line 6. |
cut -d',' -f1-3 | Uses comma as delimiter and prints fields 1 through 3. |
awk -F',' 'NR == 6 { ... }' | Uses comma as field separator, selects line 6, and can print NF, the number of fields. |
cp SOURCE DESTINATION | Copies a file. We used it to preserve the pre-repair CSV in /tmp. |
cargo test --test validation TEST_NAME -- --exact
│ │ │ │ └─ argument for Rust test harness
│ │ │ └────────── separator: Cargo stops parsing here
│ │ └──────────────────────────── integration-test target
│ └───────────────────────────────── run tests
└─────────────────────────────────────── Rust package manager/build tool
git status git add tests/cli.rs tests/validation.rs \ examples/nigeria-lithium/product.yaml \ examples/nigeria-lithium/valid.csv \ examples/nigeria-lithium/invalid.csv git diff --cached --check git commit -m "feat: add Nigeria lithium validation example" git push origin main
| Command | Repository effect |
|---|---|
git status | Read-only inventory of tracked, modified, staged, and untracked files. |
git add FILE... | Copies the selected current file contents into the staging area for the next commit. |
git diff --cached --stat | Summarizes what is staged. --cached means the index/staging area. |
git diff --cached --check | Checks staged changes for whitespace errors. Silence means none were found. |
git commit -m "..." | Creates a local, named snapshot from the staging area. |
git push origin main | Explicitly sends local main commits to origin/main. |
git push -u origin main | Pushes and sets the upstream tracking relationship. -u is short for --set-upstream. |
git push | After upstream exists, pushes the current branch to its configured upstream. |
d3883f9 contains the Nigeria example and tests. 2e00acd adds prominent rendered-library links to the README. Both reached origin/main, and the working tree was clean.docs/index.html existsGitHub’s repository viewer is a source-code browser. Clicking an HTML file under github.com/.../blob/... shows its source. GitHub Pages is the static web host that renders the same committed HTML.
main branch └── docs/ # configured Pages source ├── .nojekyll # publish static files directly ├── index.html # default learning-library home └── sessions/ ├── 2026-08-20-session-01.html └── 2026-08-22-session-02.html
Open the rendered TokMesh Lite learning library
target="_blank". Use Ctrl+click on Linux/Windows, Command+click on macOS, middle-click, or the browser’s “Open link in new tab” menu.Commit at coherent GREEN checkpoints—not while a test is intentionally RED. Several closely related fixture behaviors can form one focused commit after the complete quality gates pass.
Test-driven development is a RED→GREEN learning loop: state one public behavior in a failing test, implement only enough to make it pass, then verify. Refactoring comes during review.
sed -i ... a Bash command?sed is an external Unix program. Bash or Zsh is the shell that parses quoting, line continuations, and pipes before launching it.
cargo fmt do?It applies the standard Rust formatter. cargo fmt --check only checks and is ideal as a quality gate.
It lints every target with every Cargo feature and turns warnings into failures, keeping the project warning-free.
Code 1 means the validator successfully evaluated data and found contract violations. Code 2 means it could not perform validation because input was missing, unreadable, or malformed.
It was copied from the valid fixture and had not yet been mutated. A filename does not make data invalid; an observable broken contract promise does.
awk?It exposed physical record breaks before the Rust validator could reach semantic checks. Every row now has the contract’s 12 fields.
-u change when pushing?It records an upstream relationship while pushing. Once local main tracks origin/main, plain git push and git pull know the default destination/source.
index.html for?It is the default entrance to the GitHub Pages learning library, linking session workbooks and durable guides in a readable website.
The normal repository view shows source. The Pages URL renders the page. The README now links directly to that rendered address.
No reliable repository-controlled method exists because GitHub sanitizes such HTML behavior. Keyboard modifiers, middle-click, or the browser menu remain the dependable choices.
It is the caller-visible boundary where behavior is observed. For this feature, that means the public validate_data report and the CLI process exit code—not private parsing helpers.
cargo fmt --check # passed with no output cargo clippy --all-targets --all-features -- -D warnings Finished dev profile ... cargo test tests/cli.rs: 4 passed; 0 failed tests/validation.rs: 7 passed; 0 failed doc tests: 0 failed INVALID — 4 issue(s) - [quality.unique.duplicate] row[3].site_id - [quality.accepted_values.rejected] row[4].jurisdiction - [quality.range.outside] row[5].li2o_grade_percent - [data.value.required] row[6].site_name
The suite contains 11 executable integration tests in total. The invalid CLI output demonstrates four simultaneous findings while the focused report tests prove three of them by exact code and location. Duplicate-ID behavior is also confirmed by the CLI test and output.
The next test observes validate_data, because consumers care that an invalid date becomes a stable report issue at an exact row and column. They do not care which private helper discovers it.
Then add one test expecting data.value.type at row[7].sample_date. Confirm RED while row 7 still contains valid 2026-01-20. Change only physical CSV line 7 to an unambiguously invalid month such as 2026-13-20, then confirm GREEN.
Do not conflate this fixture slice with the future date-engine feature. The current checker handles shape and broad month/day ranges. Calendar-valid parsing remains a separate roadmap item.