opencode-skills-collection 4.0.71 → 4.0.73
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundled-skills/.antigravity-install-manifest.json +13 -1
- package/bundled-skills/agent-memory-discipline/SKILL.md +204 -0
- package/bundled-skills/agent-memory-discipline/references/entry-format.md +37 -0
- package/bundled-skills/antigravity-maintainer-batch-release/SKILL.md +2 -2
- package/bundled-skills/chatexport-need-miner/SKILL.md +196 -0
- package/bundled-skills/dali-short-address-commissioner/SKILL.md +185 -0
- package/bundled-skills/eol-resistor-calculator/SKILL.md +211 -0
- package/bundled-skills/esl-price-sync/SKILL.md +210 -0
- package/bundled-skills/jev-social/SKILL.md +5 -5
- package/bundled-skills/marlin-bed-leveling/SKILL.md +181 -0
- package/bundled-skills/noaa-radar-satellite-fetching/SKILL.md +253 -0
- package/bundled-skills/oneroster-csv-validator/SKILL.md +205 -0
- package/bundled-skills/telegram-mini-app/references/detailed-guide.md +26 -0
- package/bundled-skills/weather-data-lifecycle-management/SKILL.md +159 -0
- package/bundled-skills/weather-data-reproducibility/SKILL.md +292 -0
- package/bundled-skills/weather-model-run-discovery/SKILL.md +241 -0
- package/bundled-skills/weather-pipeline-performance-diagnosis/SKILL.md +184 -0
- package/package.json +1 -1
- package/skills_index.json +385 -1
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: marlin-bed-leveling
|
|
3
|
+
description: "Calibrates Marlin 2.x 3D printer firmware bed leveling: Unified Bed Leveling (UBL), Bilinear ABL, M420 S1 post-homing, Z-probe offsets, G26 mesh prints, and EEPROM slots. Trigger phrases: marlin bed leveling, ubl calibration, m420 s1, z probe offset, g29 bed level, first layer adhesion."
|
|
4
|
+
category: development
|
|
5
|
+
risk: safe
|
|
6
|
+
source: community
|
|
7
|
+
source_repo: wwewtech/marlin-bed-leveling
|
|
8
|
+
source_type: community
|
|
9
|
+
date_added: "2026-09-22"
|
|
10
|
+
author: wwewtech
|
|
11
|
+
tags: [3d-printing, marlin-firmware, gcode, hardware-calibration, fdm, additive-manufacturing]
|
|
12
|
+
tools: [claude, cursor, gemini, windsurf]
|
|
13
|
+
license: "MIT"
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# Marlin Bed Leveling: Deterministic Calibration & First-Layer Mesh Engineering
|
|
17
|
+
|
|
18
|
+
Master Marlin 2.x bed leveling architectures (Unified Bed Leveling UBL, Bilinear ABL, Manual Mesh MBL) to guarantee flawless first-layer adhesion through rigorous G-code phase workflows, probe offset math, and persistent EEPROM compensation.
|
|
19
|
+
|
|
20
|
+
## When to Use This Skill
|
|
21
|
+
|
|
22
|
+
Activate this skill when:
|
|
23
|
+
- Calibrating, tuning, or troubleshooting automatic bed leveling on Marlin 2.x 3D printers (Ender 3, CR-10, Prusa clones, custom CoreXY / Cartesian machines).
|
|
24
|
+
- The user asks: "How do I set up UBL in Marlin?", "My printer homes with G28 and ignores the mesh", "Calculate BLTouch Z-probe offset", "Generate G26 validation print G-code", or "Edit warped mesh points with M421".
|
|
25
|
+
- Diagnosing first-layer defects: nozzle dragging across PEI sheets, corner detachment, uneven squish, or compensation disabling after homing.
|
|
26
|
+
- Sizing slicer start G-code sequences (Cura, PrusaSlicer, OrcaSlicer, Bambu Studio) with thermal soaking and mesh restoration.
|
|
27
|
+
|
|
28
|
+
Do NOT use this skill when:
|
|
29
|
+
- Calibrating Klipper or RepRapFirmware printers (different firmware syntax and mesh compensation systems).
|
|
30
|
+
- Fixing gross mechanical skew: loose V-slot rollers, bent Z leadscrews, or wobbly gantry arms (hardware faults cannot be solved by software leveling).
|
|
31
|
+
- Delta printer kinematics setup requiring radius and tower endstop adjustments before mesh probing.
|
|
32
|
+
|
|
33
|
+
## Core Mental Models & Non-Negotiable Rules
|
|
34
|
+
|
|
35
|
+
1. **The Post-Homing Enable Mandate (`G28` Disables Leveling)**:
|
|
36
|
+
- In Marlin firmware, executing `G28` (Home All Axes) automatically **DISABLES bed leveling compensation by default** (`RESTORE_LEVELING_AFTER_G28` is often disabled in vendor firmware).
|
|
37
|
+
- In every print start G-code, the leveling restoration command MUST be issued **strictly AFTER `G28`**:
|
|
38
|
+
```gcode
|
|
39
|
+
G28 ; Home all axes (silently turns leveling OFF)
|
|
40
|
+
M420 S1 ; Re-enable bed leveling compensation from EEPROM mesh
|
|
41
|
+
```
|
|
42
|
+
- Failing to include `M420 S1` (or `G29 A` for UBL) after `G28` causes the printer to execute the entire print with zero leveling compensation.
|
|
43
|
+
|
|
44
|
+
2. **The 5-Phase UBL Calibration Loop**:
|
|
45
|
+
Unified Bed Leveling (UBL) is a state machine requiring five sequential phases:
|
|
46
|
+
- **Phase 1 (Probe Reachable Grid)**: `G29 P1` probes all coordinates physically reachable by the probe offset.
|
|
47
|
+
- **Phase 2 (Manual Edge Probing)**: `G29 P2` uses a paper feeler gauge for points the BLTouch cannot reach due to gantry geometry.
|
|
48
|
+
- **Phase 3 (Mathematical Extrapolation)**: `G29 P3 T0.0` fills unprobed edge coordinates by extrapolating slopes from adjacent measured points.
|
|
49
|
+
- **Phase 4 (Validation & Micro-Tuning)**: `G26 B60 H210 F1.75` prints a single-layer validation test pattern; inspect and edit coordinates with `M421 I... J... Q...`.
|
|
50
|
+
- **Phase 5 (Storage & Activation)**: `G29 S1` saves to EEPROM mesh slot 1; `G29 A` activates UBL; `M500` commits to permanent EEPROM storage.
|
|
51
|
+
|
|
52
|
+
3. **Thermal Equilibrium Pre-Flight Invariant**:
|
|
53
|
+
- Aluminum build plates, magnetic stickers, and spring-steel PEI sheets expand and warp non-linearly under thermal expansion.
|
|
54
|
+
- Probing a bed at room temperature (20°C) and then printing at 60°C or 100°C bakes a $0.08\text{--}0.20\text{mm}$ error into every mesh point.
|
|
55
|
+
- **Mandatory Pre-Flight**: Beds MUST be heated to target operating temperature and soaked for $\ge 5\text{ minutes}$ prior to running `G29` probing.
|
|
56
|
+
|
|
57
|
+
4. **Z-Probe Offset Sign Convention (`M851 Z...`)**:
|
|
58
|
+
- For downward-deploying probes (BLTouch, CRTouch, inductive sensors) where the probe trigger point triggers *below* the nozzle tip:
|
|
59
|
+
$$\mathbf{Z\text{-}Offset\ is\ strictly\ NEGATIVE}\ (e.g.,\ \text{M851 Z-1.85})$$
|
|
60
|
+
- A *positive* Z-offset on a downward-deploying probe instructs the firmware that the nozzle is lower than the probe trigger, driving the nozzle directly into the bed on the first move.
|
|
61
|
+
- Adjusting Z-offset with paper gauge:
|
|
62
|
+
- More resistance / tighter squish $\implies$ more negative (e.g., `-1.80` $\to$ `-1.85`).
|
|
63
|
+
- Less resistance / looser gap $\implies$ less negative (e.g., `-1.80` $\to$ `-1.75`).
|
|
64
|
+
|
|
65
|
+
5. **Mesh Fade Height Geometry (`M420 Z...`)**:
|
|
66
|
+
- Leveling compensation must gradually fade out as Z-height increases so top surface layers print geometrically flat rather than conforming to the bed warp.
|
|
67
|
+
- Set fade height to `M420 Z10.0` (fades compensation smoothly over the first 10mm of print height). Setting `Z0` disables fade (perpetuating bed ripples to the top of the model); setting `Z > 25` is unnecessary ballast.
|
|
68
|
+
|
|
69
|
+
## Named Sins & Anti-Patterns (Что категорически ЗАПРЕЩЕНО)
|
|
70
|
+
|
|
71
|
+
| Anti-Pattern | Manifestation in Code/Workflow | Mandatory Production Counter-Rule |
|
|
72
|
+
| :--- | :--- | :--- |
|
|
73
|
+
| **The Forgotten `M420 S1`** | `G28` followed immediately by print moves. | Always place `M420 S1` (or `G29 A`) immediately after `G28`. |
|
|
74
|
+
| **Cold Bed Mesh Probing** | Running `G29 P1` with bed heater at 20°C. | Heat bed to printing temperature and soak for $\ge 5$ minutes first. |
|
|
75
|
+
| **Positive Z-Probe Offset** | Setting `M851 Z+1.5` on a BLTouch probe. | Downward-deploying probes MUST have a negative Z-offset (`M851 Z-...`). |
|
|
76
|
+
| **Unsaved Mesh Discard** | Probing a 7x7 mesh without running `M500`. | Always commit mesh to EEPROM via `G29 S1` followed by `M500`. |
|
|
77
|
+
| **Software-Fixing Physical Tilt** | Compensating for a 2.5mm physical gantry tilt with UBL. | Physically tram bed corners (`G35` or thumb screws) before probing. |
|
|
78
|
+
| **Blind Unchecked Extrapolation** | Running `G29 P3` without verifying boundary values. | Inspect extrapolated border points with `M420 V` or `G29 T`. |
|
|
79
|
+
| **Cold Extruder G26 Validation** | Running G26 test without nozzle heating. | Heat nozzle to printing temperature (`G26 B60 H210 F1.75`). |
|
|
80
|
+
| **Loose Probe Mount Blindness** | Probing while BLTouch bracket has physical wobble. | Check physical probe mount rigidity; probe repeatability requires $s \le 0.005\text{mm}$ (`M48`). |
|
|
81
|
+
| **Excessive Fade Height** | Setting `M420 Z0` or `M420 Z50`. | Use `M420 Z10.0` to smoothly transition from warped bed to true flat geometry. |
|
|
82
|
+
| **Blind Point Over-Editing** | Manually offsetting a single mesh point by 0.5mm via `M421`. | Clean the build plate or check for debris under PEI sheet before altering matrix. |
|
|
83
|
+
|
|
84
|
+
## Concrete Archetypes / Presets
|
|
85
|
+
|
|
86
|
+
### Archetype 1: End-to-End Unified Bed Leveling (UBL) Calibration Macro
|
|
87
|
+
```gcode
|
|
88
|
+
; ==============================================================================
|
|
89
|
+
; MARLIN 2.x UBL FULL CALIBRATION SEQUENCE
|
|
90
|
+
; ==============================================================================
|
|
91
|
+
M140 S60 ; Start heating bed to 60°C
|
|
92
|
+
M190 S60 ; Wait for bed to reach 60°C
|
|
93
|
+
G4 S300 ; Thermal soak for 300 seconds (5 minutes)
|
|
94
|
+
|
|
95
|
+
G28 ; Home all axes
|
|
96
|
+
M117 Probing Reachable Points...
|
|
97
|
+
G29 P1 ; Phase 1: Automated probe of reachable grid points
|
|
98
|
+
|
|
99
|
+
M117 Extrapolating Mesh Edges...
|
|
100
|
+
G29 P3 T0.0 ; Phase 3: Mathematically extrapolate unprobed perimeter points
|
|
101
|
+
G29 P3 T0.0 ; Second pass to ensure corner fill
|
|
102
|
+
|
|
103
|
+
G29 T ; Print ASCII topology matrix to terminal for verification
|
|
104
|
+
G29 S1 ; Save calibrated mesh to EEPROM slot 1
|
|
105
|
+
G29 A ; Activate UBL leveling system
|
|
106
|
+
M500 ; Save EEPROM permanently to board flash
|
|
107
|
+
M117 UBL Mesh 1 Saved and Activated!
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Archetype 2: Production Slicer Start G-Code (Cura / PrusaSlicer / Orca)
|
|
111
|
+
```gcode
|
|
112
|
+
; ==============================================================================
|
|
113
|
+
; PRODUCTION SLICER START G-CODE (MARLIN UBL / BILINEAR)
|
|
114
|
+
; ==============================================================================
|
|
115
|
+
M140 S[first_layer_bed_temperature] ; Start heating bed
|
|
116
|
+
M104 S150 ; Pre-heat nozzle to 150°C (prevent oozing)
|
|
117
|
+
M190 S[first_layer_bed_temperature] ; Wait for bed to stabilize
|
|
118
|
+
|
|
119
|
+
G28 ; Home all axes (turns leveling OFF)
|
|
120
|
+
M420 S1 Z10.0 ; Restore mesh from EEPROM & set 10mm fade
|
|
121
|
+
; (OR for UBL: G29 A followed by G29 L1)
|
|
122
|
+
|
|
123
|
+
G1 Z50 F3000 ; Move nozzle up to safe clearance
|
|
124
|
+
M109 S[first_layer_temperature] ; Heat nozzle to final printing temp
|
|
125
|
+
|
|
126
|
+
; Priming Purge Line on bed edge
|
|
127
|
+
G92 E0 ; Reset extruder
|
|
128
|
+
G1 X0.1 Y20 Z0.28 F5000.0 ; Move to start position
|
|
129
|
+
G1 X0.1 Y200.0 Z0.28 F1500.0 E15 ; Draw first purge line
|
|
130
|
+
G1 X0.4 Y200.0 Z0.28 F5000.0 ; Move to side
|
|
131
|
+
G1 X0.4 Y20 Z0.28 F1500.0 E30 ; Draw second purge line
|
|
132
|
+
G92 E0 ; Reset extruder
|
|
133
|
+
G1 Z2.0 F3000 ; Z-hop before moving to print
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Archetype 3: Probe Repeatability & Offset Verification (`M48` & `M851`)
|
|
137
|
+
```gcode
|
|
138
|
+
; ==============================================================================
|
|
139
|
+
; PROBE REPEATABILITY & Z-OFFSET CALIBRATION
|
|
140
|
+
; ==============================================================================
|
|
141
|
+
G28 ; Home axes
|
|
142
|
+
G1 X117.5 Y117.5 Z10 F5000 ; Move to center of 235x235 bed
|
|
143
|
+
M48 P10 X117.5 Y117.5 V2 E L2 ; Run 10-point repeatability test
|
|
144
|
+
; Standard deviation must be <= 0.005 mm. If > 0.010 mm, tighten probe mount!
|
|
145
|
+
|
|
146
|
+
; Manual Z-Offset adjustment with paper feeler gauge:
|
|
147
|
+
; 1. Move to Z=0: G1 Z0 F1000
|
|
148
|
+
; 2. Baby-step Z until paper drags with slight friction
|
|
149
|
+
; 3. If nozzle moved down 0.15mm: M851 Z-1.85 (subtract 0.15 from current -1.70)
|
|
150
|
+
; 4. Commit: M500
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## The Pre-Emit Quality Gate Checklist
|
|
154
|
+
|
|
155
|
+
Before emitting any Marlin leveling advice, macro, or slicer start code:
|
|
156
|
+
- [ ] **Post-Homing Verification**: `M420 S1` (or `G29 A`) placed strictly after `G28` in the execution flow.
|
|
157
|
+
- [ ] **Thermal Soak Pre-Flight**: Bed heating and 5-minute soak mandated prior to `G29` probing.
|
|
158
|
+
- [ ] **Negative Offset Math**: Downward-deploying probes have verified negative `M851 Z-...` values.
|
|
159
|
+
- [ ] **EEPROM Commit**: `G29 S1` followed immediately by `M500` to preserve the mesh.
|
|
160
|
+
- [ ] **Fade Height Limit**: `M420 Z10.0` configured for smooth transition to flat geometry.
|
|
161
|
+
- [ ] **Physical Tramming Precedence**: Confirmed that manual corner leveling is performed before software compensation.
|
|
162
|
+
- [ ] **Probe Repeatability**: Confirmed that `M48` probe standard deviation is $\le 0.005\text{mm}$.
|
|
163
|
+
|
|
164
|
+
## Self-Correction & Verification Loop
|
|
165
|
+
|
|
166
|
+
### Bad (Vibe-Coded AI Slop Output):
|
|
167
|
+
> "Put `G29` before `G28` in your start G-code. If your BLTouch is scraping, set `M851 Z1.5`. You don't need to heat the bed during leveling because Marlin compensates for it automatically."
|
|
168
|
+
*Problems: Fatal G28 homing wipes out the preceding G29 mesh, positive Z-offset crashes nozzle into bed, dangerous cold probing advice.*
|
|
169
|
+
|
|
170
|
+
### Good (Production Marlin Bed Leveling Output):
|
|
171
|
+
> 1. Tram the four bed corners manually using `G35` or thumb screws to get mechanical variance under $\pm 0.1\text{mm}$.
|
|
172
|
+
> 2. Heat bed to 60°C and nozzle to 150°C; let soak for 5 minutes so thermal expansion stabilizes.
|
|
173
|
+
> 3. Verify probe repeatability using `M48 P10` (standard deviation must be $\le 0.005\text{mm}$).
|
|
174
|
+
> 4. Probe grid with `G29 P1`, extrapolate unprobed corners with `G29 P3 T0.0`, save to slot 1 with `G29 S1`, and commit to flash with `M500`.
|
|
175
|
+
> 5. In your slicer start G-code, place `M420 S1 Z10.0` immediately after `G28` to restore the active mesh with a 10mm fade height.
|
|
176
|
+
|
|
177
|
+
## Limitations
|
|
178
|
+
|
|
179
|
+
- Marlin commands and mesh behavior vary with firmware version, configuration, probe, bed geometry, and slicer. Check the exact firmware build and printer documentation before applying a macro or offset.
|
|
180
|
+
- G-code can move a hot toolhead and damage equipment or cause burns. Supervise calibration, keep hands clear, use the printer's emergency stop when motion is unsafe, and verify travel limits before running generated sequences.
|
|
181
|
+
- Example coordinates, temperatures, tolerances, and fade values are illustrative; measure the actual printer and follow the manufacturer’s limits.
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noaa-radar-satellite-fetching
|
|
3
|
+
description: "Retrieve NOAA NEXRAD and GOES products from public cloud archives using verified site, product, channel, sector, and scan-time selection."
|
|
4
|
+
category: data
|
|
5
|
+
risk: safe
|
|
6
|
+
source: self
|
|
7
|
+
source_type: self
|
|
8
|
+
date_added: "2026-09-24"
|
|
9
|
+
author: ShianMike
|
|
10
|
+
tags: [weather, noaa, nexrad, goes, radar, satellite, aws-s3]
|
|
11
|
+
tools: [claude, cursor, gemini, codex]
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# NOAA Radar and Satellite Fetching
|
|
15
|
+
|
|
16
|
+
## Overview
|
|
17
|
+
|
|
18
|
+
Retrieve NOAA NEXRAD radar and GOES satellite objects from public cloud
|
|
19
|
+
archives while preserving the product, platform, spatial sector, and scan-time
|
|
20
|
+
identity. Discover through narrow prefixes, choose by observation time rather
|
|
21
|
+
than upload time, and validate the downloaded scientific file before use.
|
|
22
|
+
|
|
23
|
+
This skill covers raw or provider-produced radar and satellite products. It
|
|
24
|
+
does not retrieve numerical model output or ordinary station observations.
|
|
25
|
+
|
|
26
|
+
## When to Use This Skill
|
|
27
|
+
|
|
28
|
+
- A task needs NEXRAD Level II archive volumes or real-time chunks.
|
|
29
|
+
- A task needs selected NEXRAD Level III products.
|
|
30
|
+
- GOES ABI, GLM, or another GOES-R product is needed for a time and region.
|
|
31
|
+
- A download must select the nearest completed scan within a tolerance.
|
|
32
|
+
- A public S3 key, satellite assignment, radar site, channel, or sector is
|
|
33
|
+
uncertain or has changed.
|
|
34
|
+
|
|
35
|
+
Do not activate this skill merely to display a third-party map tile or to answer
|
|
36
|
+
a general weather question.
|
|
37
|
+
|
|
38
|
+
## Define the Product Contract
|
|
39
|
+
|
|
40
|
+
For NEXRAD, specify:
|
|
41
|
+
|
|
42
|
+
- Level II archive, Level II real-time chunks, or a named Level III product;
|
|
43
|
+
- four-character radar site, such as `KTLX`;
|
|
44
|
+
- desired scan time in UTC and nearest/previous/next selection policy;
|
|
45
|
+
- maximum time tolerance;
|
|
46
|
+
- full volume or exact product code;
|
|
47
|
+
- raw file retention and decoder.
|
|
48
|
+
|
|
49
|
+
For GOES, specify:
|
|
50
|
+
|
|
51
|
+
- physical satellite number or operational role, such as East or West;
|
|
52
|
+
- instrument and product level/short name;
|
|
53
|
+
- Full Disk, CONUS, Mesoscale 1, or Mesoscale 2 sector where applicable;
|
|
54
|
+
- ABI channel or derived product;
|
|
55
|
+
- desired scan time in UTC and tolerance;
|
|
56
|
+
- calibration form needed, such as radiance or brightness temperature.
|
|
57
|
+
|
|
58
|
+
Resolve an operational role to a physical satellite using current NOAA
|
|
59
|
+
documentation at request time. East and West assignments change over a
|
|
60
|
+
satellite program's lifetime.
|
|
61
|
+
|
|
62
|
+
## Current Public AWS Routes
|
|
63
|
+
|
|
64
|
+
Consult the AWS Registry of Open Data before hard-coding a bucket. As of this
|
|
65
|
+
skill's publication:
|
|
66
|
+
|
|
67
|
+
- NEXRAD Level II archive: `s3://unidata-nexrad-level2/`
|
|
68
|
+
- NEXRAD Level II real-time chunks: `s3://unidata-nexrad-level2-chunks/`
|
|
69
|
+
- selected NEXRAD Level III real-time data: `s3://unidata-nexrad-level3/`
|
|
70
|
+
- GOES imagery and metadata: satellite-specific buckets such as
|
|
71
|
+
`s3://noaa-goes18/` and `s3://noaa-goes19/`
|
|
72
|
+
|
|
73
|
+
The former `noaa-nexrad-level2` bucket was deprecated and scheduled to become
|
|
74
|
+
unavailable on September 1, 2025. Do not copy that retired route from old
|
|
75
|
+
tutorials.
|
|
76
|
+
|
|
77
|
+
## Discover with Narrow Prefixes
|
|
78
|
+
|
|
79
|
+
NEXRAD Level II archive objects are organized by UTC date and site. GOES
|
|
80
|
+
objects are organized by product, year, day of year, and hour. List only the
|
|
81
|
+
smallest prefix that can contain the requested scan.
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
aws s3 ls --no-sign-request \
|
|
85
|
+
s3://unidata-nexrad-level2/YYYY/MM/DD/KXXX/
|
|
86
|
+
|
|
87
|
+
aws s3 ls --no-sign-request \
|
|
88
|
+
s3://noaa-goes19/ABI-L2-CMIPC/YYYY/DDD/HH/
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Replace every placeholder with a validated value. Include the adjacent hour or
|
|
92
|
+
UTC day only when the time tolerance crosses that boundary. Paginate listings
|
|
93
|
+
and bound the total keys inspected.
|
|
94
|
+
|
|
95
|
+
Use real-time chunk buckets only when the consumer is designed to assemble and
|
|
96
|
+
validate chunks. Prefer completed archive volumes for ordinary historical or
|
|
97
|
+
post-event processing.
|
|
98
|
+
|
|
99
|
+
## Select by Scan Time
|
|
100
|
+
|
|
101
|
+
Object `Last-Modified` is publication metadata, not the measurement time.
|
|
102
|
+
|
|
103
|
+
- For NEXRAD, parse the site and scan timestamp from the documented object name,
|
|
104
|
+
then confirm time and site from the decoded volume header.
|
|
105
|
+
- For GOES-R, filenames encode platform plus scan start (`s`), end (`e`), and
|
|
106
|
+
creation (`c`) timestamps. Select by scan start or interval overlap, not file
|
|
107
|
+
creation time.
|
|
108
|
+
- Default to the closest scan at or before the requested instant for a
|
|
109
|
+
retrospective view. Allow a future scan only when the caller explicitly asks
|
|
110
|
+
for nearest-in-either-direction behavior.
|
|
111
|
+
- Reject every candidate outside the stated tolerance.
|
|
112
|
+
- When sectors overlap, require the requested sector instead of choosing solely
|
|
113
|
+
by time.
|
|
114
|
+
|
|
115
|
+
This standard-library helper extracts GOES-R scan timestamps without opening
|
|
116
|
+
the NetCDF payload:
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
import re
|
|
120
|
+
from datetime import datetime, timezone
|
|
121
|
+
from pathlib import PurePosixPath
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
GOES_TIMES = re.compile(
|
|
125
|
+
r"_s(?P<start>\d{14})_e(?P<end>\d{14})_c(?P<created>\d{14})"
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def parse_goes_times(object_key):
|
|
130
|
+
match = GOES_TIMES.search(PurePosixPath(object_key).name)
|
|
131
|
+
if not match:
|
|
132
|
+
raise ValueError(f"unrecognized GOES-R filename: {object_key}")
|
|
133
|
+
|
|
134
|
+
def parse(value):
|
|
135
|
+
return datetime.strptime(value, "%Y%j%H%M%S%f").replace(
|
|
136
|
+
tzinfo=timezone.utc
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
return {name: parse(value) for name, value in match.groupdict().items()}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Treat filename parsing as discovery. After download, verify the corresponding
|
|
143
|
+
time coverage and platform attributes inside the file.
|
|
144
|
+
|
|
145
|
+
## Download and Validate
|
|
146
|
+
|
|
147
|
+
1. Record bucket, exact key, size, ETag, and `Last-Modified` before transfer.
|
|
148
|
+
2. Download to a request-owned temporary path. For public buckets, use unsigned
|
|
149
|
+
S3 access rather than supplying credentials.
|
|
150
|
+
3. If the object might still be publishing, require stable metadata or pin a
|
|
151
|
+
conditional request to the observed object identity.
|
|
152
|
+
4. Verify the received byte count and a provider checksum when one is supplied.
|
|
153
|
+
Otherwise compute and record a local SHA-256 digest.
|
|
154
|
+
5. Open the object with an existing format-aware decoder.
|
|
155
|
+
6. Verify site/platform, product, scan interval, and expected dimensions or
|
|
156
|
+
radar sweeps before renaming it into the cache.
|
|
157
|
+
|
|
158
|
+
Reuse an installed NEXRAD or NetCDF reader. Do not build a binary radar or
|
|
159
|
+
satellite decoder merely to fetch a file.
|
|
160
|
+
|
|
161
|
+
## Radar-Specific Checks
|
|
162
|
+
|
|
163
|
+
- Confirm that the radar site is the requested site and was operational at the
|
|
164
|
+
scan time.
|
|
165
|
+
- Treat Level II volumes, real-time chunks, and Level III products as different
|
|
166
|
+
contracts; they are not interchangeable encodings.
|
|
167
|
+
- Verify volume start time, end time when available, sweep count, moments, and
|
|
168
|
+
elevation angles after decoding.
|
|
169
|
+
- Keep range folding, missing gates, quality masks, and velocity ambiguity
|
|
170
|
+
explicit.
|
|
171
|
+
- A radar's nominal coverage radius does not guarantee useful low-level data at
|
|
172
|
+
a location. Consider distance, beam height, terrain blockage, and outages.
|
|
173
|
+
|
|
174
|
+
## Satellite-Specific Checks
|
|
175
|
+
|
|
176
|
+
- Confirm the physical satellite, instrument, product short name, sector, scan
|
|
177
|
+
mode, channel, and time coverage from NetCDF attributes.
|
|
178
|
+
- Do not treat ABI fixed-grid `x` and `y` coordinates as latitude and longitude.
|
|
179
|
+
Use the file's geostationary projection metadata.
|
|
180
|
+
- Apply scale/offset, fill values, data-quality flags, and product-specific
|
|
181
|
+
calibration according to NOAA documentation.
|
|
182
|
+
- A Mesoscale file is not identified only by its small dimensions; distinguish
|
|
183
|
+
Mesoscale 1 and 2 explicitly.
|
|
184
|
+
- Do not blend files from different scan modes, sectors, satellites, or product
|
|
185
|
+
levels without recording the transformation.
|
|
186
|
+
|
|
187
|
+
## Reliability, Cache, and Cleanup
|
|
188
|
+
|
|
189
|
+
- Cache by bucket, key, and object identity, not a friendly timestamp alone.
|
|
190
|
+
- Retry timeouts, `408`, `429`, and transient `5xx` responses with bounded
|
|
191
|
+
backoff and jitter.
|
|
192
|
+
- Bound time tolerance, adjacent-hour listings, pagination, concurrency, and
|
|
193
|
+
disk usage.
|
|
194
|
+
- Keep partial downloads separate from valid cache entries.
|
|
195
|
+
- Use event notifications for continuous real-time ingestion when appropriate;
|
|
196
|
+
do not repeatedly scan broad prefixes.
|
|
197
|
+
- For a one-shot image or export, delete request-owned raw objects in `finally`
|
|
198
|
+
only after the final artifact and provenance manifest are durable.
|
|
199
|
+
- Never delete a shared user cache as request cleanup.
|
|
200
|
+
|
|
201
|
+
## Verification Checklist
|
|
202
|
+
|
|
203
|
+
- The source bucket is current and documented by NOAA or its data distributor.
|
|
204
|
+
- Radar site or physical satellite and operational role are explicit.
|
|
205
|
+
- Product level, code/short name, sector, channel, and scan mode match the
|
|
206
|
+
request.
|
|
207
|
+
- The selected scan satisfies the direction policy and maximum time tolerance.
|
|
208
|
+
- Object size and identity match the downloaded bytes.
|
|
209
|
+
- The format-aware decoder opens the file and confirms internal metadata.
|
|
210
|
+
- Geolocation uses the product's real coordinate reference system.
|
|
211
|
+
- Temporary data cleanup preserves the requested artifact and manifest.
|
|
212
|
+
|
|
213
|
+
## Security & Safety Notes
|
|
214
|
+
|
|
215
|
+
- Fetch only public data or resources the user is authorized to access.
|
|
216
|
+
- Never embed AWS credentials, signed URLs, private endpoints, or notification
|
|
217
|
+
subscription tokens in code or logs.
|
|
218
|
+
- Keep TLS verification enabled; `--no-sign-request` does not disable TLS.
|
|
219
|
+
- Validate site, product, channel, sector, and time inputs against allowlists
|
|
220
|
+
before constructing object prefixes or local paths.
|
|
221
|
+
- Bound response size and available disk space before downloading high-rate
|
|
222
|
+
radar or satellite streams.
|
|
223
|
+
- Preserve NOAA attribution and do not imply NOAA endorsement.
|
|
224
|
+
|
|
225
|
+
## Common Pitfalls
|
|
226
|
+
|
|
227
|
+
- **Old NEXRAD examples return no data:** They reference the retired Level II
|
|
228
|
+
bucket. Resolve the current bucket from the registry.
|
|
229
|
+
- **The right minute shows the wrong image:** Creation time was used instead of
|
|
230
|
+
scan start/end time.
|
|
231
|
+
- **GOES East points to the wrong bucket:** An operational role was hard-coded
|
|
232
|
+
to a satellite number. Resolve the assignment for the requested date.
|
|
233
|
+
- **The map is displaced:** Fixed-grid scan angles were treated as geographic
|
|
234
|
+
coordinates. Apply the geostationary projection.
|
|
235
|
+
- **A real-time radar volume will not decode:** Individual chunks were treated
|
|
236
|
+
as a completed archive volume. Assemble and validate them or use the archive.
|
|
237
|
+
|
|
238
|
+
## Limitations
|
|
239
|
+
|
|
240
|
+
- Buckets, satellite assignments, product availability, scan modes, and naming
|
|
241
|
+
conventions can change; verify current official documentation.
|
|
242
|
+
- This skill retrieves source products but does not guarantee meteorological
|
|
243
|
+
suitability, perform mosaicking, or implement scientific calibration.
|
|
244
|
+
- Historical gaps and instrument or site outages cannot be repaired by provider
|
|
245
|
+
fallback.
|
|
246
|
+
|
|
247
|
+
## Additional Resources
|
|
248
|
+
|
|
249
|
+
- [NEXRAD on the AWS Registry of Open Data](https://registry.opendata.aws/noaa-nexrad/)
|
|
250
|
+
- [NOAA GOES on the AWS Registry of Open Data](https://registry.opendata.aws/noaa-goes/)
|
|
251
|
+
- [NOAA GOES-R documents](https://www.ospo.noaa.gov/resources/documents/goes-r.html)
|
|
252
|
+
- [NOAA STAR guide to satellite filenames](https://www.star.nesdis.noaa.gov/atmospheric-composition-training/satellite_data_decoding_data_file_names.php)
|
|
253
|
+
- [NOAA Radar Operations Center interface documents](https://www.roc.noaa.gov/wsr88d/BuildInfo/Files.aspx)
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: oneroster-csv-validator
|
|
3
|
+
description: "Validates 1EdTech / 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."
|
|
4
|
+
category: testing
|
|
5
|
+
risk: safe
|
|
6
|
+
source: community
|
|
7
|
+
source_repo: wwewtech/oneroster-csv-validator
|
|
8
|
+
source_type: community
|
|
9
|
+
date_added: "2026-09-22"
|
|
10
|
+
author: wwewtech
|
|
11
|
+
tags: [edtech, oneroster, csv-validation, ims-global, 1edtech, data-engineering]
|
|
12
|
+
tools: [claude, cursor, gemini, windsurf]
|
|
13
|
+
license: "MIT"
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# OneRoster CSV Validator: Strict 1EdTech Data Integrity & Pre-Flight Linter
|
|
17
|
+
|
|
18
|
+
Perform rigorous pre-flight validation on OneRoster v1.1 and v1.2 CSV roster bundles prior to SIS/Clever/ClassLink ingestion: audit foreign key referential integrity, eliminate orphan enrollments, enforce bulk vs delta partitioning, and sanitize encoding traps.
|
|
19
|
+
|
|
20
|
+
## When to Use This Skill
|
|
21
|
+
|
|
22
|
+
Activate this skill when:
|
|
23
|
+
- Validating or debugging student information system (SIS) roster export packages (`.zip` archives or CSV directories) for OneRoster v1.1 or v1.2 compliance.
|
|
24
|
+
- 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".
|
|
25
|
+
- Auditing relational integrity across `manifest.csv`, `orgs.csv`, `users.csv`, `courses.csv`, `classes.csv`, `enrollments.csv`, and `academicSessions.csv`.
|
|
26
|
+
- Preparing automated pre-flight CI/CD pipelines to catch corrupted student or teacher records before nightly SIS sync runs.
|
|
27
|
+
|
|
28
|
+
Do NOT use this skill when:
|
|
29
|
+
- Integrating OneRoster REST / OAuth 2.0 API endpoints (this skill focuses strictly on CSV table bindings).
|
|
30
|
+
- Generating synthetic or fabricated student PII without authorization.
|
|
31
|
+
- Working on generic non-standard CSV spreadsheets that do not follow 1EdTech specifications.
|
|
32
|
+
|
|
33
|
+
## Core Mental Models & Non-Negotiable Rules
|
|
34
|
+
|
|
35
|
+
1. **The Manifest Root Authority Law**:
|
|
36
|
+
- `manifest.csv` MUST reside strictly at the root level of the ZIP archive (never nested inside a subfolder).
|
|
37
|
+
- It MUST declare the exact specification version: `oneroster.version,1.1` or `oneroster.version,1.2`.
|
|
38
|
+
- 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.
|
|
39
|
+
|
|
40
|
+
2. **The Strict Bulk vs Delta Partition**:
|
|
41
|
+
- One exchange bundle = strictly ONE operational mode.
|
|
42
|
+
- **Bulk Exchange Mode**: Represents a complete master snapshot. In OneRoster 1.1, rows MUST NOT contain `dateLastModified` or `status` columns. In 1.2, all status fields MUST be `active`.
|
|
43
|
+
- **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`).
|
|
44
|
+
- Mixing bulk and delta formats within the same archive is a fatal validation failure.
|
|
45
|
+
|
|
46
|
+
3. **The Foreign Key Referential Integrity Hierarchy**:
|
|
47
|
+
- Relational dependency chain:
|
|
48
|
+
$$\mathbf{orgs.csv} \longleftarrow \mathbf{courses.csv} \longleftarrow \mathbf{classes.csv} \longleftarrow \mathbf{enrollments.csv} \longrightarrow \mathbf{users.csv}$$
|
|
49
|
+
- Every `schoolSourcedId` in `users.csv` and `classes.csv` MUST resolve to an existing `sourcedId` in `orgs.csv`.
|
|
50
|
+
- Every `courseSourcedId` in `classes.csv` MUST resolve to `courses.csv`.
|
|
51
|
+
- Every `userSourcedId` and `classSourcedId` in `enrollments.csv` MUST resolve to active primary keys in `users.csv` and `classes.csv`.
|
|
52
|
+
- **Zero Orphan Tolerance**: A single orphan link (e.g. enrolling a deleted student ID) invalidates the batch.
|
|
53
|
+
|
|
54
|
+
4. **Strict Role Enum Constraint Axioms**:
|
|
55
|
+
- In `users.csv`: `role` MUST strictly match the OneRoster enum:
|
|
56
|
+
- v1.1: `administrator`, `proctor`, `student`, `teacher`.
|
|
57
|
+
- v1.2: adds `aide`, `guardian`, `parent`, `staff`.
|
|
58
|
+
- In `enrollments.csv`: `role` is strictly constrained to `administrator`, `proctor`, `student`, `teacher`. Custom vendor roles (`substitute`, `dean`) must be mapped to valid specification enums.
|
|
59
|
+
- Primary `sourcedId` values MUST be case-sensitively unique strings (RFC 4122 UUID format strongly recommended).
|
|
60
|
+
|
|
61
|
+
5. **RFC 4180 CSV & UTF-8 BOM Sanitization**:
|
|
62
|
+
- Files MUST be encoded in **UTF-8 without Byte Order Mark (BOM)**.
|
|
63
|
+
- Microsoft Excel frequently prepends `\xEF\xBB\xBF` to CSV exports, corrupting the first column name (`sourcedId` $\neq$ `sourcedId`).
|
|
64
|
+
- Fields containing commas, double-quotes, or newlines MUST be enclosed in double quotes (`"`). Literal quotes inside fields must be escaped as `""`.
|
|
65
|
+
|
|
66
|
+
## Named Sins & Anti-Patterns (Что категорически ЗАПРЕЩЕНО)
|
|
67
|
+
|
|
68
|
+
| Anti-Pattern | Manifestation in CSV Packages | Mandatory Production Counter-Rule |
|
|
69
|
+
| :--- | :--- | :--- |
|
|
70
|
+
| **Nested Archive Packaging** | Zipping a parent folder (`district_roster/manifest.csv`). | Manifest and CSVs MUST reside at the archive root (`/manifest.csv`). |
|
|
71
|
+
| **The UTF-8 BOM Trap** | Excel export injecting `\xEF\xBB\xBF` into `sourcedId`. | Strip BOM headers during pre-flight sanitization pass. |
|
|
72
|
+
| **Orphan Enrollment Links** | `enrollments.csv` pointing to non-existent user IDs. | Enforce foreign key validation across all entity files before import. |
|
|
73
|
+
| **Bulk / Delta Column Bleed** | Including `dateLastModified` or `tobedeleted` in bulk. | Enforce schema separation: bulk files must not contain delta headers. |
|
|
74
|
+
| **Circular Org Hierarchies** | School A listed as parent of School B, and vice-versa. | Perform directed acyclic graph (DAG) cycle detection on `orgs.csv`. |
|
|
75
|
+
| **Illegal Role Strings** | Using non-standard strings like `counselor` or `sub`. | Map all roles to standard enums (`administrator`, `teacher`, `student`). |
|
|
76
|
+
| **Malformed ISO 8601 Timestamps** | Writing `"09/22/2026 14:00"` instead of ISO 8601. | Enforce strict RFC 3339 UTC format: `YYYY-MM-DDTHH:MM:SS.sssZ`. |
|
|
77
|
+
| **Unescaped CSV Commas** | Unquoted commas in user names (`Doe, John`) shifting columns. | Wrap text fields containing commas in standard RFC 4180 quotes. |
|
|
78
|
+
| **Missing Mandatory Core Files** | Exporting a bulk zip without `academicSessions.csv`. | Confirm all 7 mandatory core files exist in zip and manifest. |
|
|
79
|
+
| **Hallucinated SourcedIDs** | Inventing arbitrary IDs during reconciliation. | Preserve authoritative SIS primary keys without synthetic generation. |
|
|
80
|
+
|
|
81
|
+
## Concrete Archetypes / Presets
|
|
82
|
+
|
|
83
|
+
### Archetype 1: Pre-Flight Python Referential Integrity Linter
|
|
84
|
+
```python
|
|
85
|
+
import csv
|
|
86
|
+
import io
|
|
87
|
+
import re
|
|
88
|
+
import zipfile
|
|
89
|
+
from typing import Dict, List, Set
|
|
90
|
+
|
|
91
|
+
CORE_FILES = [
|
|
92
|
+
"manifest.csv", "orgs.csv", "users.csv", "courses.csv",
|
|
93
|
+
"classes.csv", "enrollments.csv", "academicSessions.csv"
|
|
94
|
+
]
|
|
95
|
+
VALID_ROLES_1_1 = {"administrator", "proctor", "student", "teacher"}
|
|
96
|
+
|
|
97
|
+
def validate_oneroster_archive(zip_path: str) -> dict:
|
|
98
|
+
errors = []
|
|
99
|
+
warnings = []
|
|
100
|
+
|
|
101
|
+
with zipfile.ZipFile(zip_path, 'r') as z:
|
|
102
|
+
names = z.namelist()
|
|
103
|
+
|
|
104
|
+
# 1. Manifest Root Check
|
|
105
|
+
if "manifest.csv" not in names:
|
|
106
|
+
return {"status": "FATAL", "errors": ["manifest.csv missing from zip root."]}
|
|
107
|
+
|
|
108
|
+
# 2. Extract and Parse Core Sets
|
|
109
|
+
data: Dict[str, List[dict]] = {}
|
|
110
|
+
keys: Dict[str, Set[str]] = {}
|
|
111
|
+
|
|
112
|
+
for filename in CORE_FILES:
|
|
113
|
+
if filename in names:
|
|
114
|
+
raw_bytes = z.read(filename)
|
|
115
|
+
# Strip UTF-8 BOM if present
|
|
116
|
+
if raw_bytes.startswith(b'\xef\xbb\xbf'):
|
|
117
|
+
warnings.append(f"{filename} contains UTF-8 BOM; stripped automatically.")
|
|
118
|
+
raw_bytes = raw_bytes[3:]
|
|
119
|
+
|
|
120
|
+
reader = csv.DictReader(io.StringIO(raw_bytes.decode('utf-8', errors='replace')))
|
|
121
|
+
rows = list(reader)
|
|
122
|
+
data[filename] = rows
|
|
123
|
+
if rows and "sourcedId" in rows[0]:
|
|
124
|
+
keys[filename] = {r["sourcedId"] for r in rows if "sourcedId" in r}
|
|
125
|
+
else:
|
|
126
|
+
errors.append(f"Mandatory core file missing: {filename}")
|
|
127
|
+
|
|
128
|
+
# 3. Foreign Key Checks
|
|
129
|
+
if "orgs.csv" in keys and "users.csv" in data:
|
|
130
|
+
for r in data["users.csv"]:
|
|
131
|
+
org_ref = r.get("orgSourcedIds") or r.get("schoolSourcedId")
|
|
132
|
+
if org_ref and org_ref not in keys["orgs.csv"]:
|
|
133
|
+
errors.append(f"users.csv: user {r.get('sourcedId')} references non-existent org {org_ref}")
|
|
134
|
+
|
|
135
|
+
if "users.csv" in keys and "classes.csv" in keys and "enrollments.csv" in data:
|
|
136
|
+
for r in data["enrollments.csv"]:
|
|
137
|
+
uid = r.get("userSourcedId")
|
|
138
|
+
cid = r.get("classSourcedId")
|
|
139
|
+
if uid not in keys["users.csv"]:
|
|
140
|
+
errors.append(f"enrollments.csv: orphan enrollment references missing user {uid}")
|
|
141
|
+
if cid not in keys["classes.csv"]:
|
|
142
|
+
errors.append(f"enrollments.csv: orphan enrollment references missing class {cid}")
|
|
143
|
+
|
|
144
|
+
role = r.get("role")
|
|
145
|
+
if role not in VALID_ROLES_1_1:
|
|
146
|
+
errors.append(f"enrollments.csv: illegal role '{role}' on user {uid}")
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
"status": "FAILED" if errors else "PASSED",
|
|
150
|
+
"errors": errors,
|
|
151
|
+
"warnings": warnings,
|
|
152
|
+
"total_records_checked": sum(len(v) for v in data.values())
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Archetype 2: Minimal Valid OneRoster v1.1 Manifest
|
|
157
|
+
```csv
|
|
158
|
+
propertyName,value
|
|
159
|
+
oneroster.version,1.1
|
|
160
|
+
file.orgs,bulk
|
|
161
|
+
file.users,bulk
|
|
162
|
+
file.courses,bulk
|
|
163
|
+
file.classes,bulk
|
|
164
|
+
file.enrollments,bulk
|
|
165
|
+
file.academicSessions,bulk
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Archetype 3: Delta Synchronization Row Example
|
|
169
|
+
```csv
|
|
170
|
+
sourcedId,status,dateLastModified,userSourcedId,classSourcedId,role,primary
|
|
171
|
+
enr_99201,active,2026-09-22T08:30:00.000Z,usr_0421,cls_bio101,student,true
|
|
172
|
+
enr_99202,tobedeleted,2026-09-22T08:30:00.000Z,usr_0884,cls_bio101,student,false
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## The Pre-Emit Quality Gate Checklist
|
|
176
|
+
|
|
177
|
+
Before emitting validation results, CSV transforms, or roster scripts:
|
|
178
|
+
- [ ] **Root Location**: Confirmed `manifest.csv` is located strictly at `/` without folder nesting.
|
|
179
|
+
- [ ] **BOM Scrubbing**: Verified all CSV headers are clean UTF-8 without `\xEF\xBB\xBF`.
|
|
180
|
+
- [ ] **Mode Consistency**: Verified no delta columns exist in bulk packages, and timestamps exist in deltas.
|
|
181
|
+
- [ ] **Referential Integrity**: 100% check of `userSourcedId`, `classSourcedId`, `orgSourcedIds`, and `courseSourcedId`.
|
|
182
|
+
- [ ] **Role Enum Validation**: Confirmed all role values match 1EdTech specifications.
|
|
183
|
+
- [ ] **Cycle-Free Orgs**: DAG validation run on `orgs.csv` to ensure no circular parent references.
|
|
184
|
+
- [ ] **RFC 4180 Escaping**: Commas, quotes, and multi-line values enclosed in double quotes.
|
|
185
|
+
|
|
186
|
+
## Self-Correction & Verification Loop
|
|
187
|
+
|
|
188
|
+
### Bad (Vibe-Coded AI Slop Output):
|
|
189
|
+
> "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."
|
|
190
|
+
*Problems: Nested zip breaks root discovery, blank manifest violates schema, inventing fake IDs corrupts student records, ignores UTF-8 BOM.*
|
|
191
|
+
|
|
192
|
+
### Good (Production OneRoster CSV Validator Output):
|
|
193
|
+
> 1. Extract zip to verify `/manifest.csv` exists at the root path; confirm `oneroster.version` is declared as `1.1`.
|
|
194
|
+
> 2. Scan all CSV headers: detect and strip UTF-8 BOM (`\xEF\xBB\xBF`) from `users.csv` to prevent header corruption.
|
|
195
|
+
> 3. Perform foreign key referential integrity pass:
|
|
196
|
+
> - Flag 4 orphan rows in `enrollments.csv` referencing deleted student ID `usr_9914`.
|
|
197
|
+
> - Flag invalid role `"substitute_teacher"` on row 14 of `enrollments.csv` (must map to `"teacher"`).
|
|
198
|
+
> 4. Verify bulk purity: confirm no `dateLastModified` or `tobedeleted` status flags are present in bulk mode.
|
|
199
|
+
> 5. Emit clean, line-numbered pre-flight audit report before committing to SIS ingestion.
|
|
200
|
+
|
|
201
|
+
## Limitations
|
|
202
|
+
|
|
203
|
+
- 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.
|
|
204
|
+
- 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.
|
|
205
|
+
- 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.
|