[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6F9QERKNTQT2XVG4T5SEFE4":3,"skill-same:01M6F9QERKNTQT2XVG4T5SEFE4":81},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":22,"publishedAt":47,"linkOnly":48,"markdownContent":49,"markdownBody":50,"frontMatter":51,"refName":60,"commitSha":61,"skillDirectory":62,"forksCount":63,"repositoryHtmlUrl":64,"repositoryHomepageUrl":6,"ownerHtmlUrl":65,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":66,"install":67,"resources":80},"01M6F9QERKNTQT2XVG4T5SEFE4","oneroster-csv-validator",null,"Validates 1EdTech \u002F IMS Global OneRoster v1.1 and v1.2 CSV roster sets: manifest integrity, bulk vs delta strictness, foreign key references, and encoding sanitization. Trigger phrases: oneroster csv validator, validate oneroster zip, clever roster error, oneroster sourcedid.","boisenoise\u002Fskills-collections","boisenoise","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F193180?v=4","01M6CDBDQK6BDN55ZMA6B6PKBM","01M6CDBDVSJ3JPTBYN26FZ7TZK",13,"MIT","https:\u002F\u002Fgithub.com\u002Fboisenoise\u002Fskills-collections\u002Fblob\u002Fmain\u002Fskills\u002Fantigravity-oneroster-csv-validator\u002FSKILL.md",[17],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000004","电子表格","SCENARIO","使用场景",[23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],"validator","csv","oneroster csv validator","roster","2026","sanitization","zip","1edtech","ims-global","csv-validation","oneroster","edtech","bulk","testing","error","trigger","windsurf","delta","community","references","integrity","data","gemini","safe","2026-11-10 10:12:30",false,"---\nname: oneroster-csv-validator\ndescription: \"Validates 1EdTech \u002F IMS Global OneRoster v1.1 and v1.2 CSV roster sets: manifest integrity, bulk vs delta strictness, foreign key references, and encoding sanitization. Trigger phrases: oneroster csv validator, validate oneroster zip, clever roster error, oneroster sourcedid.\"\ncategory: testing\nrisk: safe\nsource: community\nsource_repo: wwewtech\u002Foneroster-csv-validator\nsource_type: community\ndate_added: \"2026-09-22\"\nauthor: wwewtech\ntags: [edtech, oneroster, csv-validation, ims-global, 1edtech, data-engineering]\ntools: [claude, cursor, gemini, windsurf]\nlicense: \"MIT\"\n---\n\n# OneRoster CSV Validator: Strict 1EdTech Data Integrity & Pre-Flight Linter\n\nPerform rigorous pre-flight validation on OneRoster v1.1 and v1.2 CSV roster bundles prior to SIS\u002FClever\u002FClassLink ingestion: audit foreign key referential integrity, eliminate orphan enrollments, enforce bulk vs delta partitioning, and sanitize encoding traps.\n\n## When to Use This Skill\n\nActivate this skill when:\n- Validating or debugging student information system (SIS) roster export packages (`.zip` archives or CSV directories) for OneRoster v1.1 or v1.2 compliance.\n- The user asks: \"Why is Clever rejecting our OneRoster zip file?\", \"Check for orphaned userSourcedIds in enrollments.csv\", \"Validate bulk vs delta mode in OneRoster CSVs\", or \"Sanitize UTF-8 BOM characters from school rosters\".\n- Auditing relational integrity across `manifest.csv`, `orgs.csv`, `users.csv`, `courses.csv`, `classes.csv`, `enrollments.csv`, and `academicSessions.csv`.\n- Preparing automated pre-flight CI\u002FCD pipelines to catch corrupted student or teacher records before nightly SIS sync runs.\n\nDo NOT use this skill when:\n- Integrating OneRoster REST \u002F OAuth 2.0 API endpoints (this skill focuses strictly on CSV table bindings).\n- Generating synthetic or fabricated student PII without authorization.\n- Working on generic non-standard CSV spreadsheets that do not follow 1EdTech specifications.\n\n## Core Mental Models & Non-Negotiable Rules\n\n1. **The Manifest Root Authority Law**:\n   - `manifest.csv` MUST reside strictly at the root level of the ZIP archive (never nested inside a subfolder).\n   - It MUST declare the exact specification version: `oneroster.version,1.1` or `oneroster.version,1.2`.\n   - It MUST list every single file present in the exchange. If a CSV file is present in the archive but omitted from `manifest.csv`, or listed in `manifest.csv` but missing from the zip, the package is rejected.\n\n2. **The Strict Bulk vs Delta Partition**:\n   - One exchange bundle = strictly ONE operational mode.\n   - **Bulk Exchange Mode**: Represents a complete master snapshot. In OneRoster 1.1, retain required CSV headers while leaving `dateLastModified` and `status` values empty. OneRoster1.2 also prohibits using those fields in bulk; do not populate active by default.\n   - **Delta Exchange Mode**: Represents incremental changes. Every row MUST contain a valid ISO 8601 UTC timestamp (`YYYY-MM-DDTHH:MM:SS.sssZ`) in `dateLastModified` and a valid status (`active` or `tobedeleted`).\n   - Mixing bulk and delta formats within the same archive is a fatal validation failure.\n\n3. **The Foreign Key Referential Integrity Hierarchy**:\n   - Relational dependency chain:\n     $$\\mathbf{orgs.csv} \\longleftarrow \\mathbf{courses.csv} \\longleftarrow \\mathbf{classes.csv} \\longleftarrow \\mathbf{enrollments.csv} \\longrightarrow \\mathbf{users.csv}$$\n   - Validate the version-specific org reference fields (including multi-valued orgSourcedIds in1.1 users and separate1.2 roles); each applicable reference must resolve to an existing `sourcedId` in `orgs.csv`.\n   - Every `courseSourcedId` in `classes.csv` MUST resolve to `courses.csv`.\n   - In a complete bulk snapshot, enrollment references must resolve. A delta may reference unchanged records already in the recipient baseline; validate against that state and deletion semantics, not the archive alone.\n   - **Zero Orphan Tolerance**: A single orphan link (e.g. enrolling a deleted student ID) invalidates the batch.\n\n4. **Strict Role Enum Constraint Axioms**:\n   - In `users.csv`: `role` MUST strictly match the OneRoster enum:\n     - Load the exact version-specific enumeration and file layout from the binding. OneRoster1.2 moves role relationships to roles.csv; do not apply a1.1 users.role check to it.\n   - In `enrollments.csv`: `role` is strictly constrained to `administrator`, `proctor`, `student`, `teacher`. Custom vendor roles (`substitute`, `dean`) must be mapped to valid specification enums.\n   - Primary `sourcedId` values MUST be case-sensitively unique strings (RFC 4122 UUID format strongly recommended).\n\n5. **RFC 4180 CSV & UTF-8 BOM Sanitization**:\n   - Files MUST be encoded in **UTF-8 without Byte Order Mark (BOM)**.\n   - Microsoft Excel frequently prepends `\\xEF\\xBB\\xBF` to CSV exports, corrupting the first column name (`﻿sourcedId` $\\neq$ `sourcedId`).\n   - Fields containing commas, double-quotes, or newlines MUST be enclosed in double quotes (`\"`). Literal quotes inside fields must be escaped as `\"\"`.\n\n## Named Sins & Anti-Patterns (Что категорически ЗАПРЕЩЕНО)\n\n| Anti-Pattern | Manifestation in CSV Packages | Mandatory Production Counter-Rule |\n| :--- | :--- | :--- |\n| **Nested Archive Packaging** | Zipping a parent folder (`district_roster\u002Fmanifest.csv`). | Manifest and CSVs MUST reside at the archive root (`\u002Fmanifest.csv`). |\n| **The UTF-8 BOM Trap** | Excel export injecting `\\xEF\\xBB\\xBF` into `sourcedId`. | Strip BOM headers during pre-flight sanitization pass. |\n| **Orphan Enrollment Links** | `enrollments.csv` pointing to non-existent user IDs. | Enforce foreign key validation across all entity files before import. |\n| **Bulk \u002F Delta Column Bleed** | Including `dateLastModified` or `tobedeleted` in bulk. | Enforce schema separation: bulk retains schema headers with status\u002FdateLastModified values empty. |\n| **Circular Org Hierarchies** | School A listed as parent of School B, and vice-versa. | Perform directed acyclic graph (DAG) cycle detection on `orgs.csv`. |\n| **Illegal Role Strings** | Using non-standard strings like `counselor` or `sub`. | Map all roles to standard enums (`administrator`, `teacher`, `student`). |\n| **Malformed ISO 8601 Timestamps** | Writing `\"09\u002F22\u002F2026 14:00\"` instead of ISO 8601. | Enforce strict RFC 3339 UTC format: `YYYY-MM-DDTHH:MM:SS.sssZ`. |\n| **Unescaped CSV Commas** | Unquoted commas in user names (`Doe, John`) shifting columns. | Wrap text fields containing commas in standard RFC 4180 quotes. |\n| **Missing Mandatory Core Files** | Exporting a bulk zip without `academicSessions.csv`. | Determine required files from the exact exchange profile; implementation support requirements do not mean every exchange must contain the full core. |\n| **Hallucinated SourcedIDs** | Inventing arbitrary IDs during reconciliation. | Preserve authoritative SIS primary keys without synthetic generation. |\n\n## Concrete Archetypes \u002F Presets\n\n### Archetype 1: Pre-Flight Python Referential Integrity Linter\n```python\nimport csv\nimport io\nimport re\nimport zipfile\nfrom typing import Dict, List, Set\n\nCORE_FILES = [\n    \"manifest.csv\", \"orgs.csv\", \"users.csv\", \"courses.csv\",\n    \"classes.csv\", \"enrollments.csv\", \"academicSessions.csv\"\n]\nVALID_ROLES_1_1 = {\"administrator\", \"proctor\", \"student\", \"teacher\"}\n\ndef validate_oneroster_archive(zip_path: str) -> dict:\n    errors = []\n    warnings = []\n\n    with zipfile.ZipFile(zip_path, 'r') as z:\n        infos = z.infolist()\n        if len(infos) > 100 or len({i.filename for i in infos}) != len(infos):\n            raise ValueError(\"Too many or duplicate ZIP members\")\n        maximum = 4 * 1024 * 1024\n        if sum(i.file_size for i in infos) > 16 * 1024 * 1024:\n            raise ValueError(\"Archive expansion limit exceeded\")\n        for info in infos:\n            if info.file_size > maximum or info.file_size \u002F max(1, info.compress_size) > 100:\n                raise ValueError(\"ZIP expansion or compression-ratio limit exceeded\")\n        names = z.namelist()\n\n        # 1. Manifest Root Check\n        if \"manifest.csv\" not in names:\n            return {\"status\": \"FATAL\", \"errors\": [\"manifest.csv missing from zip root.\"]}\n\n        # 2. Extract and Parse Core Sets\n        data: Dict[str, List[dict]] = {}\n        keys: Dict[str, Set[str]] = {}\n\n        for filename in CORE_FILES:\n            if filename in names:\n                with z.open(filename) as member:\n                    raw_bytes = member.read(maximum + 1)\n                if len(raw_bytes) > maximum:\n                    raise ValueError(\"Expanded member exceeds limit\")\n                # Strip UTF-8 BOM if present\n                if raw_bytes.startswith(b'\\xef\\xbb\\xbf'):\n                    warnings.append(f\"{filename} contains UTF-8 BOM; stripped automatically.\")\n                    raw_bytes = raw_bytes[3:]\n\n                reader = csv.DictReader(io.StringIO(raw_bytes.decode('utf-8', errors='strict')))\n                rows = []\n                for row in reader:\n                    if len(rows) >= 10000:\n                        raise ValueError(\"CSV row limit exceeded\")\n                    rows.append(row)\n                data[filename] = rows\n                if rows and \"sourcedId\" in rows[0]:\n                    keys[filename] = {r[\"sourcedId\"] for r in rows if \"sourcedId\" in r}\n            else:\n                errors.append(f\"Mandatory core file missing: {filename}\")\n\n        # 3. Foreign Key Checks\n        if \"orgs.csv\" in keys and \"users.csv\" in data:\n            for r in data[\"users.csv\"]:\n                org_ref = r.get(\"orgSourcedIds\") or r.get(\"schoolSourcedId\")\n                for org_id in (org_ref or \"\").split(\",\"):\n                    if org_id and org_id not in keys[\"orgs.csv\"]:\n                        errors.append(\"users.csv: unresolved org reference (identifier redacted)\")\n\n        if \"users.csv\" in keys and \"classes.csv\" in keys and \"enrollments.csv\" in data:\n            for r in data[\"enrollments.csv\"]:\n                uid = r.get(\"userSourcedId\")\n                cid = r.get(\"classSourcedId\")\n                if uid not in keys[\"users.csv\"]:\n                    errors.append(f\"enrollments.csv: orphan enrollment references missing user {uid}\")\n                if cid not in keys[\"classes.csv\"]:\n                    errors.append(f\"enrollments.csv: orphan enrollment references missing class {cid}\")\n\n                role = r.get(\"role\")\n                if role not in VALID_ROLES_1_1:\n                    errors.append(f\"enrollments.csv: illegal role '{role}' on user {uid}\")\n\n    return {\n        \"status\": \"FAILED\" if errors else \"PARTIAL_CHECKS_ONLY\",\n        \"errors\": errors,\n        \"warnings\": warnings,\n        \"total_records_checked\": sum(len(v) for v in data.values())\n    }\n```\n\n### Archetype 2: Partial OneRoster v1.1 Manifest Sketch (add required absent declarations)\n```csv\npropertyName,value\nmanifest.version,1.0\noneroster.version,1.1\nfile.orgs,bulk\nfile.users,bulk\nfile.courses,bulk\nfile.classes,bulk\nfile.enrollments,bulk\nfile.academicSessions,bulk\n```\n\n### Archetype 3: Delta Synchronization Row Example\n```csv\nsourcedId,status,dateLastModified,userSourcedId,classSourcedId,role,primary\nenr_99201,active,2026-09-22T08:30:00.000Z,usr_0421,cls_bio101,student,true\nenr_99202,tobedeleted,2026-09-22T08:30:00.000Z,usr_0884,cls_bio101,student,false\n```\n\n## The Pre-Emit Quality Gate Checklist\n\nBefore emitting validation results, CSV transforms, or roster scripts:\n- [ ] **Root Location**: Confirmed `manifest.csv` is located strictly at `\u002F` without folder nesting.\n- [ ] **BOM Scrubbing**: Verified all CSV headers are clean UTF-8 without `\\xEF\\xBB\\xBF`.\n- [ ] **Mode Consistency**: Verified bulk headers are present and status\u002FdateLastModified values are empty, and timestamps exist in deltas.\n- [ ] **Referential Integrity**: 100% check of `userSourcedId`, `classSourcedId`, `orgSourcedIds`, and `courseSourcedId`.\n- [ ] **Role Enum Validation**: Confirmed all role values match 1EdTech specifications.\n- [ ] **Cycle-Free Orgs**: DAG validation run on `orgs.csv` to ensure no circular parent references.\n- [ ] **RFC 4180 Escaping**: Commas, quotes, and multi-line values enclosed in double quotes.\n\n## Self-Correction & Verification Loop\n\n### Bad (Vibe-Coded AI Slop Output):\n> \"Zip your CSV folder up and upload it. If Clever says there is a missing manifest, just add a blank manifest.csv inside the folder. If some student IDs are missing in enrollments, just delete those rows or invent temporary user IDs.\"\n*Problems: Nested zip breaks root discovery, blank manifest violates schema, inventing fake IDs corrupts student records, ignores UTF-8 BOM.*\n\n### Good (Production OneRoster CSV Validator Output):\n> 1. Extract zip to verify `\u002Fmanifest.csv` exists at the root path; confirm `oneroster.version` is declared as `1.1`.\n> 2. Scan all CSV headers: detect and strip UTF-8 BOM (`\\xEF\\xBB\\xBF`) from `users.csv` to prevent header corruption.\n> 3. Perform foreign key referential integrity pass:\n>    - Flag 4 orphan rows in `enrollments.csv` referencing deleted student ID `usr_9914`.\n>    - Flag invalid role `\"substitute_teacher\"` on row 14 of `enrollments.csv` (must map to `\"teacher\"`).\n> 4. Verify bulk purity: confirm status\u002FdateLastModified values are empty in bulk mode.\n> 5. Emit clean, line-numbered pre-flight audit report before committing to SIS ingestion.\n\n## Limitations\n\n- OneRoster version details and required files can differ by exchange profile and vendor implementation. Validate against the exact 1EdTech specification and recipient's current import contract before rejecting or transforming a roster.\n- Student and staff roster files contain sensitive education records. Process only with authorization, minimize retained data, redact identifiers in diagnostics, and do not upload source CSVs to external services.\n- The included snippets are illustrative, not a complete certified validator; for example, large files, namespaces, delta semantics, and vendor extensions require additional handling and tests.\n\nThis linter intentionally checks only a narrow1.1-style bulk subset; it does not implement manifest validation, full version\u002Fheader\u002Fenumeration rules, duplicate IDs, org cycles or1.2 roles. Never report full compliance from PARTIAL_CHECKS_ONLY. Strictly decode input and preserve originals; BOM tolerance is a normalization choice, not permission to silently rewrite source rosters. Version-specific files and recipient baseline are required for deltas. References: [1.1 bulk semantics](https:\u002F\u002Fdevelopers.imsglobal.org\u002Foneroster-v11-final-best-practice-and-implementation-guide), [1.2 CSV binding](https:\u002F\u002Fwww.imsglobal.org\u002Fspec\u002Foneroster\u002Fv1p2\u002Fbind\u002Fcsv\u002F), [1.2 conformance scope](https:\u002F\u002Fwww.imsglobal.org\u002Fspec\u002Foneroster\u002Fv1p2\u002Fcert\u002F).\n","# OneRoster CSV Validator: Strict 1EdTech Data Integrity & Pre-Flight Linter\n\nPerform rigorous pre-flight validation on OneRoster v1.1 and v1.2 CSV roster bundles prior to SIS\u002FClever\u002FClassLink ingestion: audit foreign key referential integrity, eliminate orphan enrollments, enforce bulk vs delta partitioning, and sanitize encoding traps.\n\n## When to Use This Skill\n\nActivate this skill when:\n- Validating or debugging student information system (SIS) roster export packages (`.zip` archives or CSV directories) for OneRoster v1.1 or v1.2 compliance.\n- The user asks: \"Why is Clever rejecting our OneRoster zip file?\", \"Check for orphaned userSourcedIds in enrollments.csv\", \"Validate bulk vs delta mode in OneRoster CSVs\", or \"Sanitize UTF-8 BOM characters from school rosters\".\n- Auditing relational integrity across `manifest.csv`, `orgs.csv`, `users.csv`, `courses.csv`, `classes.csv`, `enrollments.csv`, and `academicSessions.csv`.\n- Preparing automated pre-flight CI\u002FCD pipelines to catch corrupted student or teacher records before nightly SIS sync runs.\n\nDo NOT use this skill when:\n- Integrating OneRoster REST \u002F OAuth 2.0 API endpoints (this skill focuses strictly on CSV table bindings).\n- Generating synthetic or fabricated student PII without authorization.\n- Working on generic non-standard CSV spreadsheets that do not follow 1EdTech specifications.\n\n## Core Mental Models & Non-Negotiable Rules\n\n1. **The Manifest Root Authority Law**:\n   - `manifest.csv` MUST reside strictly at the root level of the ZIP archive (never nested inside a subfolder).\n   - It MUST declare the exact specification version: `oneroster.version,1.1` or `oneroster.version,1.2`.\n   - It MUST list every single file present in the exchange. If a CSV file is present in the archive but omitted from `manifest.csv`, or listed in `manifest.csv` but missing from the zip, the package is rejected.\n\n2. **The Strict Bulk vs Delta Partition**:\n   - One exchange bundle = strictly ONE operational mode.\n   - **Bulk Exchange Mode**: Represents a complete master snapshot. In OneRoster 1.1, retain required CSV headers while leaving `dateLastModified` and `status` values empty. OneRoster1.2 also prohibits using those fields in bulk; do not populate active by default.\n   - **Delta Exchange Mode**: Represents incremental changes. Every row MUST contain a valid ISO 8601 UTC timestamp (`YYYY-MM-DDTHH:MM:SS.sssZ`) in `dateLastModified` and a valid status (`active` or `tobedeleted`).\n   - Mixing bulk and delta formats within the same archive is a fatal validation failure.\n\n3. **The Foreign Key Referential Integrity Hierarchy**:\n   - Relational dependency chain:\n     $$\\mathbf{orgs.csv} \\longleftarrow \\mathbf{courses.csv} \\longleftarrow \\mathbf{classes.csv} \\longleftarrow \\mathbf{enrollments.csv} \\longrightarrow \\mathbf{users.csv}$$\n   - Validate the version-specific org reference fields (including multi-valued orgSourcedIds in1.1 users and separate1.2 roles); each applicable reference must resolve to an existing `sourcedId` in `orgs.csv`.\n   - Every `courseSourcedId` in `classes.csv` MUST resolve to `courses.csv`.\n   - In a complete bulk snapshot, enrollment references must resolve. A delta may reference unchanged records already in the recipient baseline; validate against that state and deletion semantics, not the archive alone.\n   - **Zero Orphan Tolerance**: A single orphan link (e.g. enrolling a deleted student ID) invalidates the batch.\n\n4. **Strict Role Enum Constraint Axioms**:\n   - In `users.csv`: `role` MUST strictly match the OneRoster enum:\n     - Load the exact version-specific enumeration and file layout from the binding. OneRoster1.2 moves role relationships to roles.csv; do not apply a1.1 users.role check to it.\n   - In `enrollments.csv`: `role` is strictly constrained to `administrator`, `proctor`, `student`, `teacher`. Custom vendor roles (`substitute`, `dean`) must be mapped to valid specification enums.\n   - Primary `sourcedId` values MUST be case-sensitively unique strings (RFC 4122 UUID format strongly recommended).\n\n5. **RFC 4180 CSV & UTF-8 BOM Sanitization**:\n   - Files MUST be encoded in **UTF-8 without Byte Order Mark (BOM)**.\n   - Microsoft Excel frequently prepends `\\xEF\\xBB\\xBF` to CSV exports, corrupting the first column name (`﻿sourcedId` $\\neq$ `sourcedId`).\n   - Fields containing commas, double-quotes, or newlines MUST be enclosed in double quotes (`\"`). Literal quotes inside fields must be escaped as `\"\"`.\n\n## Named Sins & Anti-Patterns (Что категорически ЗАПРЕЩЕНО)\n\n| Anti-Pattern | Manifestation in CSV Packages | Mandatory Production Counter-Rule |\n| :--- | :--- | :--- |\n| **Nested Archive Packaging** | Zipping a parent folder (`district_roster\u002Fmanifest.csv`). | Manifest and CSVs MUST reside at the archive root (`\u002Fmanifest.csv`). |\n| **The UTF-8 BOM Trap** | Excel export injecting `\\xEF\\xBB\\xBF` into `sourcedId`. | Strip BOM headers during pre-flight sanitization pass. |\n| **Orphan Enrollment Links** | `enrollments.csv` pointing to non-existent user IDs. | Enforce foreign key validation across all entity files before import. |\n| **Bulk \u002F Delta Column Bleed** | Including `dateLastModified` or `tobedeleted` in bulk. | Enforce schema separation: bulk retains schema headers with status\u002FdateLastModified values empty. |\n| **Circular Org Hierarchies** | School A listed as parent of School B, and vice-versa. | Perform directed acyclic graph (DAG) cycle detection on `orgs.csv`. |\n| **Illegal Role Strings** | Using non-standard strings like `counselor` or `sub`. | Map all roles to standard enums (`administrator`, `teacher`, `student`). |\n| **Malformed ISO 8601 Timestamps** | Writing `\"09\u002F22\u002F2026 14:00\"` instead of ISO 8601. | Enforce strict RFC 3339 UTC format: `YYYY-MM-DDTHH:MM:SS.sssZ`. |\n| **Unescaped CSV Commas** | Unquoted commas in user names (`Doe, John`) shifting columns. | Wrap text fields containing commas in standard RFC 4180 quotes. |\n| **Missing Mandatory Core Files** | Exporting a bulk zip without `academicSessions.csv`. | Determine required files from the exact exchange profile; implementation support requirements do not mean every exchange must contain the full core. |\n| **Hallucinated SourcedIDs** | Inventing arbitrary IDs during reconciliation. | Preserve authoritative SIS primary keys without synthetic generation. |\n\n## Concrete Archetypes \u002F Presets\n\n### Archetype 1: Pre-Flight Python Referential Integrity Linter\n```python\nimport csv\nimport io\nimport re\nimport zipfile\nfrom typing import Dict, List, Set\n\nCORE_FILES = [\n    \"manifest.csv\", \"orgs.csv\", \"users.csv\", \"courses.csv\",\n    \"classes.csv\", \"enrollments.csv\", \"academicSessions.csv\"\n]\nVALID_ROLES_1_1 = {\"administrator\", \"proctor\", \"student\", \"teacher\"}\n\ndef validate_oneroster_archive(zip_path: str) -> dict:\n    errors = []\n    warnings = []\n\n    with zipfile.ZipFile(zip_path, 'r') as z:\n        infos = z.infolist()\n        if len(infos) > 100 or len({i.filename for i in infos}) != len(infos):\n            raise ValueError(\"Too many or duplicate ZIP members\")\n        maximum = 4 * 1024 * 1024\n        if sum(i.file_size for i in infos) > 16 * 1024 * 1024:\n            raise ValueError(\"Archive expansion limit exceeded\")\n        for info in infos:\n            if info.file_size > maximum or info.file_size \u002F max(1, info.compress_size) > 100:\n                raise ValueError(\"ZIP expansion or compression-ratio limit exceeded\")\n        names = z.namelist()\n\n        # 1. Manifest Root Check\n        if \"manifest.csv\" not in names:\n            return {\"status\": \"FATAL\", \"errors\": [\"manifest.csv missing from zip root.\"]}\n\n        # 2. Extract and Parse Core Sets\n        data: Dict[str, List[dict]] = {}\n        keys: Dict[str, Set[str]] = {}\n\n        for filename in CORE_FILES:\n            if filename in names:\n                with z.open(filename) as member:\n                    raw_bytes = member.read(maximum + 1)\n                if len(raw_bytes) > maximum:\n                    raise ValueError(\"Expanded member exceeds limit\")\n                # Strip UTF-8 BOM if present\n                if raw_bytes.startswith(b'\\xef\\xbb\\xbf'):\n                    warnings.append(f\"{filename} contains UTF-8 BOM; stripped automatically.\")\n                    raw_bytes = raw_bytes[3:]\n\n                reader = csv.DictReader(io.StringIO(raw_bytes.decode('utf-8', errors='strict')))\n                rows = []\n                for row in reader:\n                    if len(rows) >= 10000:\n                        raise ValueError(\"CSV row limit exceeded\")\n                    rows.append(row)\n                data[filename] = rows\n                if rows and \"sourcedId\" in rows[0]:\n                    keys[filename] = {r[\"sourcedId\"] for r in rows if \"sourcedId\" in r}\n            else:\n                errors.append(f\"Mandatory core file missing: {filename}\")\n\n        # 3. Foreign Key Checks\n        if \"orgs.csv\" in keys and \"users.csv\" in data:\n            for r in data[\"users.csv\"]:\n                org_ref = r.get(\"orgSourcedIds\") or r.get(\"schoolSourcedId\")\n                for org_id in (org_ref or \"\").split(\",\"):\n                    if org_id and org_id not in keys[\"orgs.csv\"]:\n                        errors.append(\"users.csv: unresolved org reference (identifier redacted)\")\n\n        if \"users.csv\" in keys and \"classes.csv\" in keys and \"enrollments.csv\" in data:\n            for r in data[\"enrollments.csv\"]:\n                uid = r.get(\"userSourcedId\")\n                cid = r.get(\"classSourcedId\")\n                if uid not in keys[\"users.csv\"]:\n                    errors.append(f\"enrollments.csv: orphan enrollment references missing user {uid}\")\n                if cid not in keys[\"classes.csv\"]:\n                    errors.append(f\"enrollments.csv: orphan enrollment references missing class {cid}\")\n\n                role = r.get(\"role\")\n                if role not in VALID_ROLES_1_1:\n                    errors.append(f\"enrollments.csv: illegal role '{role}' on user {uid}\")\n\n    return {\n        \"status\": \"FAILED\" if errors else \"PARTIAL_CHECKS_ONLY\",\n        \"errors\": errors,\n        \"warnings\": warnings,\n        \"total_records_checked\": sum(len(v) for v in data.values())\n    }\n```\n\n### Archetype 2: Partial OneRoster v1.1 Manifest Sketch (add required absent declarations)\n```csv\npropertyName,value\nmanifest.version,1.0\noneroster.version,1.1\nfile.orgs,bulk\nfile.users,bulk\nfile.courses,bulk\nfile.classes,bulk\nfile.enrollments,bulk\nfile.academicSessions,bulk\n```\n\n### Archetype 3: Delta Synchronization Row Example\n```csv\nsourcedId,status,dateLastModified,userSourcedId,classSourcedId,role,primary\nenr_99201,active,2026-09-22T08:30:00.000Z,usr_0421,cls_bio101,student,true\nenr_99202,tobedeleted,2026-09-22T08:30:00.000Z,usr_0884,cls_bio101,student,false\n```\n\n## The Pre-Emit Quality Gate Checklist\n\nBefore emitting validation results, CSV transforms, or roster scripts:\n- [ ] **Root Location**: Confirmed `manifest.csv` is located strictly at `\u002F` without folder nesting.\n- [ ] **BOM Scrubbing**: Verified all CSV headers are clean UTF-8 without `\\xEF\\xBB\\xBF`.\n- [ ] **Mode Consistency**: Verified bulk headers are present and status\u002FdateLastModified values are empty, and timestamps exist in deltas.\n- [ ] **Referential Integrity**: 100% check of `userSourcedId`, `classSourcedId`, `orgSourcedIds`, and `courseSourcedId`.\n- [ ] **Role Enum Validation**: Confirmed all role values match 1EdTech specifications.\n- [ ] **Cycle-Free Orgs**: DAG validation run on `orgs.csv` to ensure no circular parent references.\n- [ ] **RFC 4180 Escaping**: Commas, quotes, and multi-line values enclosed in double quotes.\n\n## Self-Correction & Verification Loop\n\n### Bad (Vibe-Coded AI Slop Output):\n> \"Zip your CSV folder up and upload it. If Clever says there is a missing manifest, just add a blank manifest.csv inside the folder. If some student IDs are missing in enrollments, just delete those rows or invent temporary user IDs.\"\n*Problems: Nested zip breaks root discovery, blank manifest violates schema, inventing fake IDs corrupts student records, ignores UTF-8 BOM.*\n\n### Good (Production OneRoster CSV Validator Output):\n> 1. Extract zip to verify `\u002Fmanifest.csv` exists at the root path; confirm `oneroster.version` is declared as `1.1`.\n> 2. Scan all CSV headers: detect and strip UTF-8 BOM (`\\xEF\\xBB\\xBF`) from `users.csv` to prevent header corruption.\n> 3. Perform foreign key referential integrity pass:\n>    - Flag 4 orphan rows in `enrollments.csv` referencing deleted student ID `usr_9914`.\n>    - Flag invalid role `\"substitute_teacher\"` on row 14 of `enrollments.csv` (must map to `\"teacher\"`).\n> 4. Verify bulk purity: confirm status\u002FdateLastModified values are empty in bulk mode.\n> 5. Emit clean, line-numbered pre-flight audit report before committing to SIS ingestion.\n\n## Limitations\n\n- OneRoster version details and required files can differ by exchange profile and vendor implementation. Validate against the exact 1EdTech specification and recipient's current import contract before rejecting or transforming a roster.\n- Student and staff roster files contain sensitive education records. Process only with authorization, minimize retained data, redact identifiers in diagnostics, and do not upload source CSVs to external services.\n- The included snippets are illustrative, not a complete certified validator; for example, large files, namespaces, delta semantics, and vendor extensions require additional handling and tests.\n\nThis linter intentionally checks only a narrow1.1-style bulk subset; it does not implement manifest validation, full version\u002Fheader\u002Fenumeration rules, duplicate IDs, org cycles or1.2 roles. Never report full compliance from PARTIAL_CHECKS_ONLY. Strictly decode input and preserve originals; BOM tolerance is a normalization choice, not permission to silently rewrite source rosters. Version-specific files and recipient baseline are required for deltas. References: [1.1 bulk semantics](https:\u002F\u002Fdevelopers.imsglobal.org\u002Foneroster-v11-final-best-practice-and-implementation-guide), [1.2 CSV binding](https:\u002F\u002Fwww.imsglobal.org\u002Fspec\u002Foneroster\u002Fv1p2\u002Fbind\u002Fcsv\u002F), [1.2 conformance scope](https:\u002F\u002Fwww.imsglobal.org\u002Fspec\u002Foneroster\u002Fv1p2\u002Fcert\u002F).\n",{"name":5,"risk":46,"tags":52,"tools":54,"author":57,"source":41,"license":14,"category":36,"date_added":58,"description":7,"source_repo":59,"source_type":41},[34,33,32,31,30,53],"data-engineering",[55,56,45,39],"claude","cursor","wwewtech","2026-09-22","wwewtech\u002Foneroster-csv-validator","main","7bf2279c8a65758319526a721d5c4b75683aec9c","skills\u002Fantigravity-oneroster-csv-validator",3,"https:\u002F\u002Fgithub.com\u002Fboisenoise\u002Fskills-collections","https:\u002F\u002Fgithub.com\u002Fboisenoise","2026-11-14 14:18:11",{"source":8,"skillName":5,"commands":68,"prompt":78,"zipAvailable":79},[69,72,75],{"tool":70,"command":71},"npx","npx skills add boisenoise\u002Fskills-collections --skill oneroster-csv-validator",{"tool":73,"command":74},"bunx","bunx skills add boisenoise\u002Fskills-collections --skill oneroster-csv-validator",{"tool":76,"command":77},"pnpm","pnpm dlx skills add boisenoise\u002Fskills-collections --skill oneroster-csv-validator","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fboisenoise\u002Fskills-collections\u002Fblob\u002Fmain\u002Fskills\u002Fantigravity-oneroster-csv-validator\u002FSKILL.md",true,[],[82,110,136,162,181,204],{"skillNo":83,"name":84,"nameZh":6,"description":85,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":86,"licenseFileUrl":87,"sourceHtmlUrl":88,"categories":89,"keywords":90,"publishedAt":47},"01M6WNH92BHVFXHGPDZCPJ0AG0","web-access","Use Treg for public web or platform data only when existing search, browser and connected tools cannot cover the task, with explicit cost and data boundaries.","Apache-2.0","https:\u002F\u002Fgithub.com\u002Fboisenoise\u002Fskills-collections\u002Fblob\u002Fmain\u002Fskills\u002Fantigravity-web-access\u002FLICENSE","https:\u002F\u002Fgithub.com\u002Fboisenoise\u002Fskills-collections\u002Fblob\u002Fmain\u002Fskills\u002Fantigravity-web-access\u002FSKILL.md",[],[91,92,93,94,95,96,97,44,98,99,100,101,102,103,104,105,106,107,108,109,27],"web","access","web access","official","development","browser","search","Tools","read","fetch","only","youtube","free tools","platform","critical","free","cover","normal","when",{"skillNo":111,"name":112,"nameZh":6,"description":113,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":86,"licenseFileUrl":114,"sourceHtmlUrl":115,"categories":116,"keywords":120,"publishedAt":47},"01M6WNF9XAYAB2CZDDJBAKNM93","treg","Use the Treg CLI or MCP catalog to discover, price and call authorized external tools with team-scoped credentials and a user-approved budget.","https:\u002F\u002Fgithub.com\u002Fboisenoise\u002Fskills-collections\u002Fblob\u002Fmain\u002Fskills\u002Fantigravity-treg\u002FLICENSE","https:\u002F\u002Fgithub.com\u002Fboisenoise\u002Fskills-collections\u002Fblob\u002Fmain\u002Fskills\u002Fantigravity-treg\u002FSKILL.md",[117],{"categoryNo":118,"categoryName":119,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000023","Skill与智能体开发",[112,121,122,123,94,95,124,98,125,126,127,128,129,130,131,105,132,133,134,135,27],"MCP","cli","github","license","Budget","credentials","team","catalog","use","call","discover","external tools","with","scoped","blob",{"skillNo":137,"name":138,"nameZh":6,"description":139,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":86,"licenseFileUrl":140,"sourceHtmlUrl":141,"categories":142,"keywords":148,"publishedAt":47},"01M6WN93MEAZ4E41DYAHJRPHQT","recruit","Turn a job description into an evidence-linked candidate shortlist through Treg, with job-related ranking and optional authorized business contact enrichment.","https:\u002F\u002Fgithub.com\u002Fboisenoise\u002Fskills-collections\u002Fblob\u002Fmain\u002Fskills\u002Fantigravity-recruit\u002FLICENSE","https:\u002F\u002Fgithub.com\u002Fboisenoise\u002Fskills-collections\u002Fblob\u002Fmain\u002Fskills\u002Fantigravity-recruit\u002FSKILL.md",[143],{"categoryNo":144,"categoryName":145,"taxonomyType":146,"taxonomyTypeDesc":147},"01JBSEEDCAT000000000000038","人力资源","PROFESSION","适用职业",[138,123,94,124,149,150,151,152,153,154,105,155,156,157,133,158,159,160,161,135,112,27],"https","email","enrichment","business","evidence","phone","ranking","post","job description","for","person","candidate","reason",{"skillNo":163,"name":164,"nameZh":6,"description":165,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":86,"licenseFileUrl":166,"sourceHtmlUrl":167,"categories":168,"keywords":169,"publishedAt":47},"01M6WN8R5RXSMHM9B4D9FAFX42","rea-growth","Analyze a company’s public growth and content through Treg, distinguish observations from hypotheses, and produce an evidence-linked playbook within an approved budget.","https:\u002F\u002Fgithub.com\u002Fboisenoise\u002Fskills-collections\u002Fblob\u002Fmain\u002Fskills\u002Fantigravity-rea-growth\u002FLICENSE","https:\u002F\u002Fgithub.com\u002Fboisenoise\u002Fskills-collections\u002Fblob\u002Fmain\u002Fskills\u002Fantigravity-rea-growth\u002FSKILL.md",[],[170,171,172,123,94,124,173,174,149,175,125,176,177,178,153,105,179,180,135,112,27],"growth","rea","rea growth","produce","content","playbook","marketing","analyze","public","observations","company",{"skillNo":182,"name":183,"nameZh":6,"description":184,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":86,"licenseFileUrl":185,"sourceHtmlUrl":186,"categories":187,"keywords":188,"publishedAt":47},"01M6WN2EVE3RQRGWAKBT4S7YT2","make-ugc","Create original AI UGC-style product videos through Treg: research hooks, approve a licensed presenter and voice, generate clips, add captions and report costs.","https:\u002F\u002Fgithub.com\u002Fboisenoise\u002Fskills-collections\u002Fblob\u002Fmain\u002Fskills\u002Fantigravity-make-ugc\u002FLICENSE","https:\u002F\u002Fgithub.com\u002Fboisenoise\u002Fskills-collections\u002Fblob\u002Fmain\u002Fskills\u002Fantigravity-make-ugc\u002FSKILL.md",[],[189,190,123,191,94,192,124,193,194,195,196,197,105,198,199,200,201,202,203,135],"make","make ugc","research","create","Video","Captions","ads","short","hooks","head","style","niche","trending","talking-head","approve",{"skillNo":205,"name":206,"nameZh":6,"description":207,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":86,"licenseFileUrl":208,"sourceHtmlUrl":209,"categories":210,"keywords":211,"publishedAt":47},"01M6WN10ARAA63Y8K1YNGXJ2RM","lead-signals","Find and qualify public buying signals through Treg, rank fitting business accounts and optionally enrich authorized business contact information.","https:\u002F\u002Fgithub.com\u002Fboisenoise\u002Fskills-collections\u002Fblob\u002Fmain\u002Fskills\u002Fantigravity-lead-signals\u002FLICENSE","https:\u002F\u002Fgithub.com\u002Fboisenoise\u002Fskills-collections\u002Fblob\u002Fmain\u002Fskills\u002Fantigravity-lead-signals\u002FSKILL.md",[],[212,213,214,123,94,124,215,216,152,217,218,105,219,220,221,222,223,224,225,135],"signals","lead","lead signals","how","find","ready","as","how to","qualify","people","information","enrich","changes","funding"]