0. About this guidebook
This guide is a practical workflow for diagnosing and fixing the import failures and runtime errors that occur when you take a CXP package exported from Blackboard (ArchiveExFile_*.zip), edit it locally, and re-import it into the same or a different course. It collects in one place the 8-point recipe distilled during the 2026-05-04 [Summer Term] fix marathon (DOCT-α v1→v11, UND-β v1→v6, GRAD-γ v1→v5), every successful Python script, and the absolute do-not-do list.
- instructors / TAs who revise and re-import a course each term
- anyone who needs a recipe to point a Codex/Claude Code-style coding agent at
- new PMs / instructional designers migrating a course to Blackboard ULTRA for the first time
Use cases this guide solves
- You received a course export ZIP and on import see a "content item failed to convert" error.
- Import succeeded but students click an assignment / discussion and nothing opens (silent runtime failure).
- Each click appends a base64 access-code log reference like
MjYwNTA0L*. - Due dates are still those of an older term.
- You want to naturally add new content / images / URLs to a module.
- You want to selectively import only one section for a quick update.
Scope limits
- Blackboard's UI-side import dialog is not covered (varies by institution).
- Building Block / LTI tool registration must be done by an admin.
- The 8 defect classes here are limited to this session's incidents; other failure modes (LDAP, gradebook calculation columns, external-tool OAuth, etc.) require separate investigation.
1. Anatomy of a CXP package
1.1 Files in the ZIP
Unzipping an archive ZIP exported from Blackboard yields the following structure:
After unzipping ArchiveExFile_*.zip:
├─ imsmanifest.xml <- package index (must be valid)
├─ .bb-package-info <- Blackboard system metadata
├─ .bb-log-info <- package build log (informational)
├─ .bb-package-sig <- signature (no need to recompute after edits — Blackboard ignores it)
├─ res00001.dat <- first resource (XML)
├─ res00002.dat
├─ ...
├─ resNNNNNN.dat <- Nth resource
└─ csfiles/
└─ home_dir/
└─ __xid-NNNNNN_1.png <- embedded image / PDF / Word / Excel etc.
__xid-NNNNNN_1.png.xml <- sidecar metadata
1.2 imsmanifest.xml — the package index
imsmanifest.xml follows the IMS Common Cartridge standard manifest format, with Blackboard extension attributes (bb:file, bb:title, bb:type) added. Two key blocks:
<manifest ...>
<metadata>...</metadata>
<organizations default="toc00001">
<organization identifier="toc00001">
<item identifier="itm00001" identifierref="res00007">
<title>ROOT</title>
<item identifier="itm00016" identifierref="res00047">
<title>Module 6: Capstone</title>
...
</item>
</item>
</organization>
</organizations>
<resources>
<resource bb:file="res00001.dat" bb:title="Child Courses"
identifier="res00001" type="course/x-bb-childcourses"
xml:base="res00001"/>
<resource bb:file="res00047.dat" bb:title="Module 6: Capstone"
identifier="res00047" type="resource/x-bb-document"
xml:base="res00047"/>
...
</resources>
</manifest>
<organizations>= The tree the student sees (Module → Lesson → Item structure)<resources>= Declarations for every .dat file and csfile- Each
<item>'sidentifierrefmust match a<resource>'sidentifier - Each
<resource>'sbb:filemust match the actual file
1.3 The LINK chain — how visible items connect to underlying data
asmt-test-link / forumlink / courselink CONTENT items do NOT carry their data directly. a separate LINK resource binds the wrapper to the underlying data:
res00071 (CONTENT, asmt-test-link, ULTRA marker) ← visible item clicked by the student
│
↓ (LINK is walked)
│
res00118 (LINK, ISAVAILABLE=true) ← binding
│
↓ (REFERREDTO)
│
res00100 (COURSE_ASSESSMENT) ← assignment metadata
│
↓ (ASMTID)
│
res00015 (questestinterop) ← actual assessment data
2. Standard workflow
Course revision proceeds in the following order:
- Export — archive from the original course (Blackboard UI: Packages and Utilities → Export Course).
- Backup — store the exported ZIP separately under e.g.
03_Original-ZIPs-Backup/. Never modify this file. - Extract — unzip into a working directory.
- Audit (sec. 4) — run the 8-point recipe to surface baseline defects.
- Modify — edit content, add new material, update dates.
- Re-audit — run the 8-point again after editing. Edits themselves can introduce new defects.
- Repackage — compress back to a ZIP (sec. 9).
- Sandbox import — actually import into the student-facing sandbox course and click every clickable item to verify. Inspect both convert-time logs and runtime behavior.
- Production import — once sandbox passes, push to production.
v1, v2, …) — that guarantees rollback and a paper trail. DOCT-α went all the way from v1 to v11.
3. The 8-Point Defect Catalog
The 8 defect classes below were observed and resolved directly in this session. FAIL = user impact, WARN = informational, INFO = meta. Tag:
- CONVERT — Fails with explicit error at Blackboard import time
- RUNTIME — Imports cleanly but breaks when the user clicks / uses it
- TOOLING — Common pitfalls when writing fixes
Manifest declares it but the actual .dat file is missing from the ZIP, or vice versa. Fails at convert time with a "missing referenced file"-style error, or silently skipped.
Diagnostic
comm -23 \
<(grep -oE 'identifier="res[0-9]+"' imsmanifest.xml | sort -u) \
<(ls *.dat | sed 's/\.dat$//' | sort -u)
Fix
- If the file should exist: copy from the original archive
- If it should not exist: remove the
<resource>declaration from the manifest
Real case
DOCT-α v1: Codex stripped res00104~126 (23 LINKs) + res00022~25 (4 announcements) from the .dat files but left the declarations in the manifest. 27 orphan declarations → package integrity broken.
Blackboard ULTRA does not support Journal, Blog, Wiki tools. <CONTENTHANDLER value="resource/x-bb-journallink"/> using such a retired handler causes explicit import failure.
Symptom (import log)
[Journal Item] — Blogs aren't supported at this time and were removed.
[Journal Item] — The content item failed to convert.
Cascade chain
- Blackboard sees
res00013(x-bb-blog) → "Blogs aren't supported, removed" → drops res00013 - res00122 LINK points to res00013 (now gone) → orphan LINK silently dropped
- res00068 CONTENT has
journallinkhandler expecting target → can't convert
Fix (3 things together)
- Change the visible CONTENT's
CONTENTHANDLERtoresource/x-bb-document+ changeRENDERTYPEtoREGULAR - Delete the tool-data resource file (BLOG, JOURNAL, WIKI payload)
- Delete the LINK file that connected them
- Strip both deletions'
<resource>declarations from the manifest
📝 Script: fix_journal_blog_dependency.py
The manifest's declared bb:type does not match the actual root XML element of the .dat file.
Canonical example
| res ID | Manifest bb:type | Actual root | Result |
|---|---|---|---|
| res00128 | course/x-bb-coursemessagefolder | <questestinterop> | FAIL |
How it happens
A coding agent decides 'let's put a new assessment here' and overwrites an existing res ID with a different-type payload. The manifest is not touched → mismatch.
Fix
Restore the .dat file from the original. Never overwrite an existing resource ID with a different type; add a new resource with a fresh ID instead.
A student-visible <CONTENT> item that has:
<DESCRIPTION value=""/>(empty description) AND<TEXT/>(empty body) ANDISFOLDER value="false"(not a folder) AND<FILES><FILEabsent (no file attachment either)
Blackboard converter rejects it.
False positives — be careful
asmt-test-link/forumlink/courselinkwrappers are intentionally empty (they point to a target via LINK) — skipULTRA_ASSESSMENT_MARKER=trueon an ULTRA assignment surface — skip (see defect 7)resource/x-bb-folder/x-bb-lesson— folder itself — skip
Fix
- Restore description + body from the original archive
- Or change type to folder
- Or add minimum content directly (e.g., use TITLE alone as the message)
The <LINK> connecting a visible click-target (asmt-test-link / forumlink / courselink) to its underlying assessment/forum is set to <ISAVAILABLE value="false"/>. Clicking does nothing.
Symptom
No convert-time error. Gradebook column is created. Clicking does nothing or MjYwNTA0L* -style base64 access-code reference appears (decoded: 260504/_666818_1/2.log — a server log path, not the actual error message).
Origin (Spring archive carryover)
An instructor pre-built assignments and disabled them until release in a prior term; the disabled flag carries over on export.
Fix
import re
c = re.sub(
r'<ISAVAILABLE\s+value="false"\s*/>',
'<ISAVAILABLE value="true"/>',
c,
)
📝 Script: fix_disabled_links.py
Real case
DOCT-α v5: 6 core assignments ([User Testing Assignment], [Final Capstone Submission], [Storyboard Challenge], [Design Draft], [Usability Plan], [Case Study]) all blocked by disabled LINKs.
More severe than defect 5: both the LINK file AND its manifest declaration were stripped together. The package stays internally consistent (no orphan declarations) → defect 1's audit cannot detect it.
Diagnostic
Find any visible click-target CONTENT (handler = asmt-test-link / forumlink / courselink) that has no available LINK pointing to it as REFERRER.
Fix
Restore the LINK file from the original archive, flip ISAVAILABLE=true, add the declaration to the manifest.
📝 Script: restore_missing_links.py
Real case
- UND-β: Original 31 LINKs → current 24. 7 stripped (all of them were disabled and Codex decided they were "useless" and removed them). 7 assignments fail to open.
- GRAD-γ: Original 17 LINKs → current 0. All deleted during the Option A restructure. 14 assignments + 2 discussion/courselinks fail to open.
The hardest case. Occurs when an asmt-test-link item carries <EXTENDEDDATA><ENTRY key="ULTRA_ASSESSMENT_MARKER">true</ENTRY></EXTENDEDDATA> AND the questestinterop file at the end of the LINK chain has <item> content inside its <section>.
Symptom
The item opens normally. But each render appends a sequential entry to the server log: 260504/_666818_1/3.log, 4.log, 5.log, ...
Root cause
The marker tells ULTRA, "render this as a ULTRA-native assignment surface (file upload + text submission)." But the underlying QTI is in the classic Blackboard test format (<questestinterop><assessment><section><item>). Every render, ULTRA bridges the two models and emits a conversion-trace log entry.
Fix — empty section preservation
Remove every <item> from inside the QTI body's <section>. Keep the marker. Empty section = "this is a ULTRA assignment; don't try to convert classic questions."
<questestinterop>
<assessment title="...">
<assessmentmetadata>...</assessmentmetadata>
<rubric>...</rubric>
<presentation_material>...</presentation_material>
<section>
<sectionmetadata>...</sectionmetadata>
<!-- Must be empty; no <item> -->
</section>
</assessment>
</questestinterop>
asmt-test-link + has a ULTRA marker + the intent is a ULTRA assignment (file upload). If real multiple-choice quiz items live inside (e.g., GRAD-γ's Module 1-6 Quizzes), never delete the <item>s (the quiz breaks).
📝 Script: fix_ultra_qti_conflict.py
v6 failure case (DO NOT REPEAT)
If you try to silence this log by removing the ULTRA marker itself → the item stops opening in ULTRA. The marker is the trigger for ULTRA rendering. Emptying the QTI body is the right answer, not removing the marker.
The most common trap when writing fix scripts for defects 5/6.
The trap
Blackboard's LINK XML appears in two formats:
Single-line:
<FLAGS><ISAVAILABLE value="false"/></FLAGS>
Multi-line:
<FLAGS><ISAVAILABLE
value="false"/></FLAGS>
Both formats can co-exist within the same archive. A naive single-line literal replace silently misses the multi-line case.
Wrong (single-line literal)
c = c.replace(
'<ISAVAILABLE value="false"/>',
'<ISAVAILABLE value="true"/>'
)
# Misses 5 of 7 multi-line cases
Right (regex with whitespace tolerance)
import re
c = re.sub(
r'<ISAVAILABLE\s+value="false"\s*/>',
'<ISAVAILABLE value="true"/>',
c
)
# Matches every case
Real case
UND-β v2 fix: only 2 of 7 disabled LINKs were fixed; 5 remained. Discovered during the v4 audit → reapplied with a multi-line-tolerant regex.
4. Diagnostic Tool — 8-Point Audit Script
The script below audits all 8 defect classes in a single pass. Extract the package to a folder, pass that path as an argument, and it prints a PASS/FAIL report. Exit code 0 = clean, 1 = needs work.
python audit_8point.py /path/to/extracted/package
Example output (all PASS):
======================================================================
8-Point Blackboard ULTRA Import Recipe Audit -- /tmp/cat100_audit
======================================================================
[PASS] 1. Orphan manifest decls
decls=211 files=211 missing=0 orphan=0
[PASS] 2. Retired-tool handlers in CONTENT
hits=0 []
[PASS] 3. Wrong-type substitution
violations=0 []
[PASS] 4. Empty leaf content items
leaves=0 []
[PASS] 5. Disabled LINKs blocking visible items
disabled=0 []
[PASS] 6. Click-targets without available LINK
orphan_clicks=0 []
[PASS] 7. ULTRA-marker x classic-QTI conflict
conflicts=0
[PASS] 8. Whitespace-tolerant ISAVAILABLE coverage
multiline_misses=0
Overall: ALL PASS
5. Troubleshooting Decision Tree
Trace sandbox-import results in the following order:
6. What NOT to do
Either broken in this session or expensive to recover from when made:
03_Original-ZIPs-Backup/ArchiveExFile_*.zip Never modify this file. All fixes happen on the working copy. The original is ground truth + rollback material.
bb:type and the .dat file's root XML element must match. If you need new content, issue a fresh res ID and add a new declaration.
<item>.
<response_lid>) destroys the entire quiz.
c.replace('<ISAVAILABLE value="false"/>', ...) silently misses multi-line FLAGS XML cases. Always use re.sub(r'<ISAVAILABLE\s+value="false"\s*/>', ...).
<FORUM> data + forumlink wrapper + LINK + manifest declaration + item-tree placement — all five. Doing this by hand risks ID collisions and broken chains. Reusing an existing generic Discussion board with subject-prefix conventions ([M1 [Tension Mapper]] etc.) is much safer.
2026-05-30 23:59:00 CDT. From May to the first Sunday of November = CDT (Central Daylight Time, UTC-5); otherwise CST (UTC-6). Summer term is all CDT. Blackboard's parser doesn't validate the timezone string but consistency is still better.
.bb-package-sig
This signature file can be ignored (Blackboard import does not validate it). Recomputing is impossible anyway (it would need Blackboard's private key). Leave it as-is and ZIP.
7. Real Cases — [Summer Term] Fix Marathon
The distillation source for this guide. 11+ iterations across three courses recorded in a single day (2026-05-04).
DOCT-α (LXD doctoral capstone) — 11 iterations
| Build | Defect / change | Result |
|---|---|---|
v1 | baseline (Codex 5/3 build) | D6 + D5 latent |
v2 | [Journal Item] payload restored | D2 surfaced |
v3 | D2 fix (Journal/Blog dependency removed) | discussions work, assignments do not open |
v4 | content reorganization (AI Coding Assistants section nested under Module 6) | still does not open |
v5 | D5 fix (6 disabled LINK flip) | opens, but access-code log appears |
v6 | Failed — attempted to remove the ULTRA marker | items stopped opening (reverted) |
v7/v8 | byte-exact restore from v5 | returned to v5 state |
v9 | D7 fix verified on 2 items | access-code log disappeared |
v10 | D7 fix applied to all 6 ULTRA items | all pass |
v11 | embedded 3 generated diagrams (design.md, 7-step, guardrails) | final / 25.2 MB |
UND-β (Communication, undergrad 1-2) — 6 iterations
| Build | Defect / change |
|---|---|
| baseline | 5/2 build |
v2 | D6 fix (7 missing LINKs restored + COURSE_ASSESSMENT verified) |
v3 | all 17 graded items updated to [Summer Term] dates (no Sundays, no Juneteenth, ENFORCE_DUE_DATE matched to baseline) |
v4 | Module 6 Lesson 6.2 — AI + GitHub Pages content + Playwright screenshots; D8 fix (5 additional multi-line ISAVAILABLE cases found) |
v5 | rewritten in undergrad-1/2 tone + curated YouTube / Medium / GitHub Blog links |
v6 | embedded 7 ChatGPT-generated diagrams, M5 dual-path AI avatar (SadTalker + ElevenLabs vs PowerPoint Recording + Gemini Live), [Teaching Sim]→[Teaching Sim] naming unified, M1 [Tension Mapper]+[Ethics Tutor] URL+screenshot, M2 "GitHub Codex" wording clarified. final / 18.3 MB |
GRAD-γ (Critical/Cultural Aspects of Tech, master's) — 5 iterations
| Build | Defect / change |
|---|---|
| baseline | 5/2 Option A restructure |
v2 | D6 fix — restored all 17 LINKs that had been stripped (the most severe case) |
v3 | M1 [Tension Mapper] / M2 [Teaching Sim] / M4 [Ethics Tutor] guidance + Playwright screenshots + [Discussion Board] prefix-tagged discussion pattern |
v4 | 17 graded items updated to [Summer Term] dates |
v5 | added 2 generated concept diagrams ([Tension Mapper] + [Ethics Tutor] anchors). final / 38.9 MB |
<item> cannot be removed. Functional impact zero (students take quizzes normally), the runtime telemetry log accumulation is ignored. Documented caveat.
8. Beyond defect fixes
8.1 Image / media embedding
To inline-render a slide, infographic, or screenshot in a content page, three things must happen together:
csfiles/home_dir/with the__xid-NNNNNN_1.<ext>filename (xid ≥ 990000 recommended to avoid collisions)- Generate a same-named sidecar XML (
__xid-NNNNNN_1.<ext>.xml) imsmanifest.xml's<resources>block — add the<resource>declaration
Then reference it in the content page's body HTML via Blackboard's embed syntax:
<a data-bbid="img-NNNNNN_1"
data-bbfile="{"linkName":"diagram.png",
"mimeType":"image/png",
"alternativeText":"Description",
"isDecorative":false,
"render":"inline"}"
href="@X@EmbeddedFile.requestUrlStub@X@bbcswebdav/xid-NNNNNN_1">
</a>
📝 Script: embed_image.py — all three steps in one.
8.2 Replacing content page body HTML
Blackboard stores content-page bodies as entity-encoded HTML inside <CONTENT>...<BODY><TEXT>HTML</TEXT></BODY>. < → <, > → >, etc.
📝 Script: replace_body.py — Replaces only the body; everything else (DATES, FLAGS, CONTENTHANDLER, PARENTID...)is preserved.
python replace_body.py res00157.dat new_body.html \
--updated "2026-05-04 04:00:00 CDT"
8.3 Bulk due-date update
Gradebook resource's <OUTCOMEDEFINITION> blocks contain <DUE> fields. Define the schedule in JSON and apply in bulk:
{
"Module 1: Quiz": "2026-05-27 23:59:00 CDT",
"Module 1: Assess - [Tension Rationale]": "2026-05-30 23:59:00 CDT",
...
}
📝 Script: update_due_dates.py
Constraints: no Sundays, no Juneteenth (2026-06-19), within the term window. ENFORCE_DUE_DATE stays at baseline (the instructor decides item-by-item in the UI).
8.4 Slim package — selective import
When you want to update just one module without re-importing the whole course.
Strategy: build a slim ZIP containing just the Module's item subtree + system resources (res00001 Child Courses + res00010 Content Handler Data + .bb-package-info / .bb-log-info / .bb-package-sig). DOCT-α example: 20MB → 294KB.
Or more simply: in the Blackboard UI's selective import dialog, import the full package but check only the desired module.
8.5 Discussion-board connection — without adding new FORUMs
Adding a new <FORUM> resource for module-specific discussions is high-risk (ID collisions, item-tree breakage). The safe pattern:
- Reuse the existing course-wide Discussion board (e.g., "[Course Discussion Board]")
- Agree on a subject-prefix convention in each module's Explore page (
[M1 [Tension Mapper]],[M2 [Teaching Sim]],[M4 [Ethics Tutor]]) - Specify the post format (e.g., 100–150 words, ≥ 1 peer reply)
Discussion connection without structural change. The pattern used in GRAD-γ.
9. Packaging & verification
9.1 PowerShell Compress-Archive
Safest way on Windows:
$src = "C:\path\to\extracted_package_dir"
$dst = "C:\path\to\output\package_v6.zip"
if (Test-Path $dst) { Remove-Item $dst }
Compress-Archive -Path "$src\*" -DestinationPath $dst -CompressionLevel Optimal
$hash = (Get-FileHash $dst -Algorithm SHA256).Hash
$size = (Get-Item $dst).Length
Write-Output "Packaged: $size bytes / SHA256 $hash"
Compress-Archive may write backslash separators in the ZIP. unzip warns ("appears to use backslashes as path separators") but Blackboard import works fine. Ignorable.
9.2 Bash zip (Linux / Git Bash)
cd /path/to/extracted_package_dir
zip -r ../package_v6.zip . -x "*.DS_Store"
9.3 Verification checklist
- Re-extract the package and run the 8-point audit (verify no corruption from compression)
- Manifest declared count == .dat file count
- SHA-256 recorded (rollback + reproducibility)
- Import into the sandbox course → check the convert log
- Click every visible item personally and verify it opens correctly
- Gradebook column creation + due-date correctness
- Verify once more from the student view (or instructor preview)
10. FAQ
What is the MjYwNTA0L* access-code log?
260504/_666818_1/2.log. It is not the actual error message — the contents live in the log file at that path. To inspect, give the path to your Blackboard admin and ask them to pull the file. It is not shown in the student view, so there may be no functional impact.Why does a ULTRA assignment surface end up with <item>s in its underlying QTI?
- The original course started in classic Blackboard, and when migrated to ULTRA only the marker was added — the body's questestinterop structure remained as-is.
- Someone (e.g., Codex) hand-added
<item>s to silence a convert-time warning like "Empty assessments converted to Question Banks."
<item>s (when the intent is an assignment).My fix script produced two <organizations> blocks in the manifest
find('<organizations>') misses the multi-line attribute case (e.g. <organizations\n default="...">). Match with a regex like r'<organizations\b.*?</organizations>' to fix.I only want to update one module — selective import vs slim package, which is better?
res00001 + res00010 plus a freshly built manifest, which is hand-intensive. If the package is very large (tens of MB) or your institution has disabled selective import, slim is the only option.How do I pick xid numbers consistently?
__xid-190865580_1). When adding new media, use a much larger prefix to avoid collisions. This session used the 990000~999999 range (992xxx, 993xxx, 994xxx per course). Check the existing course's xid distribution first, then pick a range clearly above it.Why do I not need to rebuild .bb-package-sig?
I want students to submit a file via the ULTRA assignment surface — how?
- The wrapper's
CONTENTHANDLERisresource/x-bb-asmt-test-link - EXTENDEDDATA carries
<ENTRY key="ULTRA_ASSESSMENT_MARKER">true</ENTRY> - The questestinterop file at the end of the LINK chain has an empty
<section>(only<sectionmetadata>, no<item>)
Student data (submissions, grades) is mixed into the package
res*ATTEMPT*.datres*SUBMISSION*.datusers.xmlcsfiles/home_dir/users/<ATTEMPT>blocks inside the Gradebook
Can these fix scripts be applied to other LMSes (Canvas, Moodle)?
bb:file, bb:title, bb:type). Canvas and Moodle use their own export formats with different manifest structures. The 8-point recipe's concepts transfer (orphan declarations, retired tools, disabled flags, …) but the code does not.11. Downloads
Blackboard CXP Fix Skill — for Claude Code / Codex CLI
The 8-point recipe + all fix scripts + reference notes packaged as an auto-invocable agent skill. No personal information — pure skill registration. Any instructor / TA / coding agent can drop it in and use it.
unzip blackboard-cxp-fix-skill.zip -d ~/.claude/skills/All scripts — MIT-style ("use freely with attribution"). Python 3.8+ with standard library only.
Recommended workflow
audit_8point.pybaseline- Defect found → apply the corresponding fix script
audit_8point.pyre-verify- all PASS → packaging
12. References
Primary session record
Desktop\24_Teaching\Summer2026_CourseRevisions_2026-05-01\AIL606_IMPORT_FIX_RECORD_2026-05-04.md— full v1→v11 incident record~\.claude\projects\C--Users-jewoo\memory\reference_blackboard_ultra_import_fixes.md— auto-memory recipeObsidianVault\wiki\sources\[Summer Term] Course Prep Import Fix Marathon 2026-05-04.md— vault session record
Standards
- IMS Common Cartridge — manifest standard base
- Blackboard ULTRA Course Materials — ULTRA-supported content types
- WCAG 2.2 Quick Reference — accessibility
Related entity pages (vault)
wiki/entities/[Summer Term] UA Course Prep.mdwiki/entities/[Tension Mapping App].mdwiki/entities/[Ethics Tutor].md
Auto-memory entries
project_summer2026_courses.mdreference_blackboard_ultra_import_fixes.md← distillation source for this guidefeedback_html_guides.md— HTML guidebook pattern (the format this page follows)
Author
Jewoong Moon, Ph.D.Assistant Professor of Instructional Technology
Educational Leadership, Policy, and Technology Studies (ELPTS)
University of Alabama
jmoon19@ua.edu
Disclosure
This is a practical onboarding guide based on Blackboard ULTRA behavior observed on 2026-05-04. Blackboard internals change over time, and this guide covers only the 8 defect classes reproduced in this session. Other failure modes (LDAP integration, gradebook calculation columns, external-tool OAuth) require separate investigation. Validate every fix in a sandbox before applying to production.