Structure-preserving text de-identification: swap detected multi-word name-like phrases for opaque Person-N placeholders, and swap content words (nouns/verbs/adjectives/adverbs, via foundation-text-signals' POS heuristic) for a deterministic synonym from a small hand-curated thesaurus, leaving function words and punctuation untouched. Preserves sentence length, punctuation, and paragraph structure ("the shape") for a downstream reader; does NOT provide the same disclosure guarantee as the opaque structural shards (table-shard/transcript-shard/prose-shard) -- rare/technical terms may have no listed synonym and stay literal, and structure plus partial vocabulary can sometimes allow real content to be inferred. Real obfuscation, not a proof of non-recoverability -- documented as such, not oversold.
| Tier | foundation |
| Role | unclassified (baselined) |
| Path | crates/foundation/text-deidentify |
| Edition | 2021 |
| Targets | deidentify, foundation_text_deidentify |
| Public items | 3 across 0 modules |
| Tests | 16 |
What it is for
Structure-preserving text de-identification: swap detected name-like phrases for opaque Person N placeholders, and swap content words for a deterministic synonym from a small hand-curated thesaurus, leaving function words and punctuation untouched. The goal is a stand-in document with the same shape (sentence length, paragraph breaks, repeated-word/name patterns) as the real one, safe enough to use as a structural example for an outside model -- e.g. calibrating how it should handle a document of this shape, without ever showing it the real one.
This is NOT the same disclosure guarantee as the opaque structural shards (table-shard/transcript-shard/prose-shard), and this crate makes no claim otherwise. Those emit ONLY counts and types -- nothing recognizable as prose ever leaves them. This crate emits real prose, with real grammar and real sentence structure, and only the individual content words swapped:
- A word with no thesaurus entry (most technical/rare/domain-specific
vocabulary -- the exact words most likely to matter) is left literal, not masked, and this crate does not detect or warn about that case.
- Structure plus a partial, real vocabulary can sometimes let a reader
infer real content even where individual words were swapped.
- Single-word names (no capitalized surname/given-name run) are not
detected at all -- see find_name_phrases.
Treat the output as "harder to skim, not provably safe" -- useful for a structural training/calibration example, not a substitute for the disclosure-minimized shard family when the real content must not leak.
Capabilities
crate root
Structure-preserving text de-identification: swap detected name-like
| Item |
|---|
fn find_name_phrases(text : & str) -> Vec <String> |
fn synonym_for(word : & str) -> Option <& 'static str> |
fn deidentify(text : & str) -> String |
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
No public modules: the crate root is its whole surface.
Public surface
`crate root`
| Item | What it is |
|---|---|
fn find_name_phrases(text : & str) -> Vec <String> | Find every distinct run of 2+ consecutive Title-Case words, in first-appearance order -- unlike domain_transcript_shard::detect_repeated_title_case_phrases, this does NOT require a repeat: a name mentioned exactly once still needs to be swapped |
fn synonym_for(word : & str) -> Option <& 'static str> | Deterministically pick a synonym for word (case-insensitive lookup): the same input word always yields the same synonym, so a document de-identified twice produces byte-identical output, and a word repeated in the source stays recognizably repeated in the output -- preserving that structural signal rather than scattering it randomly |
fn deidentify(text : & str) -> String | De-identify text: every detected name phrase becomes Person N (first-appearance order); every content word (Noun/Verb/Adjective/ Adverb per classify_word_pos) with a thesaurus entry becomes its deterministic synonym; everything else (function words, punctuation attached to a word, words with no thesaurus entry) is left as-is |
No pub use re-exports: every item above is declared in this crate.
Boundary
Depends on no other workspace tier.
Shares tier foundation with 27 other crates: foundation-audit-log, foundation-basemodels, foundation-bounded-io, foundation-conversation-closure, foundation-crypto-sign, foundation-decisioning, foundation-encounter-vocabulary, foundation-fs-metadata, … (27 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) | foundation |
| Architectural role (taxonomy) | unclassified (baselined) |
| Location | crates/foundation/text-deidentify |
| Vocabulary in force (lexicon) | current |
Dependencies
Runtime, in this workspace.
| Crate | Tier | Optional | Only on |
|---|---|---|---|
| `foundation-text-signals` | foundation | no | always |
Runtime, from outside the workspace.
| Crate | Requirement | Features | Optional | Only on |
|---|---|---|---|---|
clap | ^4 | derive | no | always |
Development, in this workspace.
| Crate | Tier | Optional | Only on |
|---|---|---|---|
| `tools-cli-conformance` | tools | no | always |
Build. None.
Depended on by. Nothing in this workspace.
Signal flow — what reaches this crate, and what it reaches.
flowchart LR SELF["foundation-text-deidentify"] SELF -->|development| n_tools_cli_conformance["tools-cli-conformance"] SELF -->|runtime| n_foundation_text_signals["foundation-text-signals"] 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 | deidentify | `src/main.rs` |
| lib | foundation_text_deidentify | `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 | none detected |
No unsafe block, unsafe fn, unsafe impl or unsafe trait was found by the parser anywhere in this crate's source.
Configuration
No environment variable is read with a literal name anywhere in this crate. A variable whose key is computed at run time cannot be listed here, and is not claimed to be absent.
Related capabilities
No workspace crate depends on this one.
Verification
| Kind | Count |
|---|---|
| Unit tests | 16 |
| 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 | 3 | 0 | 0 |
What the tests establish, by name:
a_sentence_initial_capital_does_not_extend_a_name_run_across_the_period—src/lib.rsdeidentify_is_deterministic_across_calls—src/lib.rsdeidentify_leaves_a_markdown_heading_untouched—src/lib.rsdeidentify_leaves_function_words_and_unlisted_content_words_untouched—src/lib.rsdeidentify_merges_a_leading_article_variant_with_the_bare_phrase—src/lib.rsdeidentify_preserves_line_breaks—src/lib.rsdeidentify_replaces_a_repeated_name_consistently—src/lib.rsdeidentify_swaps_a_known_content_word_for_its_synonym—src/lib.rsdeidentify_two_distinct_names_get_distinct_placeholders—src/lib.rsfind_name_phrases_catches_a_name_mentioned_only_once—src/lib.rsfind_name_phrases_deduplicates_and_preserves_first_appearance_order—src/lib.rsfind_name_phrases_does_not_catch_a_single_capitalized_word—src/lib.rsfind_name_phrases_does_not_scan_markdown_heading_lines—src/lib.rssynonym_for_is_deterministic—src/lib.rssynonym_for_returns_none_for_an_unlisted_word—src/lib.rscli_definition_conforms—src/main.rs
Documentation coverage
| Measure | Documented | Total |
|---|---|---|
| Public items with rustdoc | 3 | 3 |
Public modules with a //! block | 0 | 0 |
pie showData
title Public items with rustdoc
"Documented" : 3
"No rustdoc detected" : 0
Metrics
| Metric | Value |
|---|---|
| Rust source files | 2 |
| Source lines | 480 |
| Code lines | 327 |
| Public API items | 3 |
| Public modules | 0 |
| Tests | 16 |
| Examples | 0 |
| Cargo features | 0 |
| Direct runtime dependencies | 2 |
| Workspace reverse dependencies | 0 |
pie showData
title Public API by kind
"function" : 3
pie showData
title Rust source composition
"Code" : 327
"Blank or comment" : 153
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.