February Fest Live-Ops Hourly jsonl jq Append Fails When Row Missing build_label - How to Fix
Problem: During February 2027 Steam Next Fest live week, facilitators (or a GitHub Actions cron) still append lines to february_fest_live_ops_hourly_log.jsonl, but verify_february_live_ops.sh exits 3, jq hourly row-diff fails, or Thursday row review stays RED because one or more hourly rows are missing build_label (empty string, null, or the field renamed to build_id / version only).
Who is affected now: Unity / multi-engine teams using Unity hourly jsonl append preflight (Guide #40) and Lesson 280. This is hourly writer schema recovery, not HOTFIX list blocking (Help HOTFIX exception), not fest-day depot mismatch after setlive (setlive case study / Help #38), and not SteamPipe “queued forever” (Help #14 forward).
Fastest safe fix: Stop stand-up GREEN theater → find bad jsonl lines → normalize writer to always emit build_label from Unity Application.version (or engine cousin) → rewrite LATEST receipt → run verify → append three consecutive good hours → set BUILD_RECEIPT february_fest_live_ops_hourly_ok: true.
Direct answer
Missing build_label on hourly jsonl means the append script (or a per-laptop copy) wrote a partial object. Thursday jq and verify_february_live_ops.sh J3 expect a non-empty label that is not a live-week dev string. Fix the shared writer, backfill broken lines, then prove three green cycles—do not flip february_fest_live_ops_hourly_ok by hand.
Why this issue spikes in February 2027 live week hour three
- Per-facilitator scripts drift — Someone ship a laptop one-liner that drops fields under time pressure.
- Field rename confusion — Steam depot
buildidis not receiptbuild_label; writers mix them. - Partial hours — “Nothing changed” shortcuts omit the label entirely instead of documenting
delta: unchanged. - Corrupt jsonl — Missing trailing newline or truncated JSON breaks
tail -n 1before J3 even runs. - Editor vs player — Append runs from Editor Play with blank
Application.versionwhile players are on HOTFIX depot.
Symptoms and search phrases
verify_february_live_ops.shexit 3 (J3 build_label policy).jq: error/ empty.build_labelonFEBRUARY_LIVE_OPS_RECEIPT_LATEST.json.- Thursday review RED on
february_fest_live_ops_hourly_okafter “successful” cron appends. february_fest_live_ops_hourly_log.jsonllines with"build_label":""or nobuild_labelkey.- Stand-up paste shows prior-hour label while receipt
hour_utcadvanced. - GitHub Actions hourly job green but local verify red (artifact vs laptop path).
Root causes (check in order)
- Optional field in writer — schema treated
build_labelas nullable. build_id/Application.versiononly — wrong field name in jq compact object.- Corrupt / truncated jsonl line — partial write without newline.
- jq assume full schema on prior hour — diff reads prior line with missing label and cascades.
- Unity smoke never exported — append invents snapshot without label (Guide #40 order violated).
- Bool flipped without verify — theater GREEN; CI still exit 3.
Beginner path (first 15 minutes)
Prerequisites: jq, write access to release-evidence/february-live/, Guide #40 scripts in repo (or paste from jq blog).
- List bad lines:
jq -c 'select((.build_label // "") | length == 0)' \
release-evidence/february-live/february_fest_live_ops_hourly_log.jsonl
- Open
FEBRUARY_LIVE_OPS_RECEIPT_LATEST.json→ setbuild_labelfrom the exported player footer / UnityApplication.version. - Run verify → append once with the shared
append_february_live_ops_hour.sh. - Repeat for the next two hours (or simulate with documented hour_utc bumps in dry-run branch).
- Promote BUILD_RECEIPT only after three exit 0 cycles.
Common mistake: Editing only BUILD_RECEIPT while leaving corrupt jsonl lines in place—Sunday closeout will still fail integrity checks.
Fastest safe fix path
Step 1 — Inventory bad hours
LOG=release-evidence/february-live/february_fest_live_ops_hourly_log.jsonl
# Lines missing or empty build_label
jq -c 'select((.build_label // "") | length == 0) | {hour_utc, fest_day, keys: keys}' "$LOG"
# Duplicate hour stamps (corruption cousin)
jq -s '[.[].hour_utc] | length as $n | unique | length == $n' "$LOG"
Move verified-bad lines aside (do not delete without backup):
cp "$LOG" "$LOG.bak-$(date -u +%Y%m%dT%H%M%SZ)"
Step 2 — Normalize the shared writer schema
Canonical compact object (must match Guide #40 / Lesson 280):
jq -c '{hour_utc, fest_day, build_label, february_fest_live_ops_hourly_ok, receipt_path: $rec}' \
--arg rec "$REC" "$REC"
Required: build_label is a non-empty string matching live policy (fest-demo-2027-02-* / team convention)—never omit the key.
Reject laptop copies that write:
{"hour_utc":"...","fest_day":"D3","build_id":4829111}
build_id can live under snapshot if useful; J3 keys on build_label.
Step 3 — Repair LATEST receipt (J1–J3)
{
"schema": "february_fest_live_ops_receipt_v1",
"fest_window": "2027-02-next-fest-live",
"fest_day": "D3",
"hour_utc": "2027-02-18T18:00:00Z",
"build_label": "february-fest-2027-live-rc2",
"snapshot": {
"hour_one_launch_rate": 0.38,
"february_live_queue_wall_clock_ok": true,
"fest_stream_overlay_safe_zone_ok": true
},
"row_diff": [
{"field": "build_label", "before": "february-fest-2027-live-rc2", "after": "february-fest-2027-live-rc2", "delta": "unchanged"}
],
"february_fest_live_ops_hourly_ok": false
}
Unity source of truth:
Debug.Log($"build_label={Application.version}");
Gate before append:
BL=$(jq -r '.build_label // empty' release-evidence/february-live/FEBRUARY_LIVE_OPS_RECEIPT_LATEST.json)
test -n "$BL" && [[ "$BL" != *dev* ]]
Step 4 — Verify + append (fail-closed)
REC=release-evidence/february-live/FEBRUARY_LIVE_OPS_RECEIPT_LATEST.json
./scripts/verify_february_live_ops.sh "$REC"
./scripts/append_february_live_ops_hour.sh "$REC"
Pass: verify prints OK (exit 0); jsonl gains a line with non-empty build_label.
Fail exit 3: fix label / HOTFIX crosswalk—do not append.
If label changed for a HOTFIX binary, file hotfix_id in receipt notes and align HOTFIX exception help—do not invent a silent label drift.
Step 5 — Three consecutive GREEN cycles + BUILD_RECEIPT latch
| Cycle | Check |
|---|---|
| 1 | verify 0 + append with label |
| 2 | Prior hour now has label; row_diff recomputes |
| 3 | No empty-label lines in last three jsonl rows |
Then:
jq -e '
[.[] | select(.hour_utc != null)]
| .[-3:]
| all(.[]; (.build_label // "") | length > 0)
' release-evidence/february-live/february_fest_live_ops_hourly_log.jsonl
Promote:
"february_fest_live_ops_hourly_ok": true,
"cousin_receipts": {
"february_live_ops_latest": "release-evidence/february-live/FEBRUARY_LIVE_OPS_RECEIPT_LATEST.json",
"february_live_ops_hourly_log": "release-evidence/february-live/february_fest_live_ops_hourly_log.jsonl"
}
Verification
| Check | Pass when |
|---|---|
| Scanner | Zero empty-label lines in active fest window jsonl |
| Verify | Exit 0 on LATEST |
| Append | Last jsonl line includes build_label |
| Three-cycle | Last three lines all labeled |
| BUILD_RECEIPT | february_fest_live_ops_hourly_ok: true |
| Thursday | Hourly column no longer RED for missing label |
Alternative fixes (edge cases)
Branch A — Corrupt truncated line
Rewrite file from .bak excluding the bad last line; ensure every write ends with \n. Never use editors that strip final newlines on save.
Branch B — Label changed without HOTFIX row
J3 is correctly RED. Complete HOTFIX exception fix / Guide #41 cousin before claiming hourly GREEN.
Branch C — CI append green, laptop red
Artifact upload omitted FEBRUARY_LIVE_OPS_RECEIPT_LATEST.json contents; workflow invented empty label. Fail the job when test -n "$(jq -r .build_label "$REC")".
Branch D — March weekly schema accidentally used
march_post_fest_sprint_* paths during February fail J1—route back to february_fest_live_ops_receipt_v1 (Guide #40); March weekly is Guide #46 / Lesson 287 later.
Branch E — Sunday closeout already started
Backfill recoverable hours with honest labels; document unrecoverable gaps as waivers on Sunday closeout—do not invent labels from memory.
Prevention
- One repo-pinned
append_february_live_ops_hour.sh—delete facilitator copies. - Pre-append assert: non-empty
build_label/ not*dev*. - Cron only on fest live branch (Guide #40
if:). - Teach facilitators jq blog exit table (3 = label policy).
- Latch Lesson 280 BUILD_RECEIPT milestone in the project course CI before D1.
Related problems
- Unity hourly jsonl append preflight (Guide #40) — prevention ritual.
- Lesson 280 — hourly jsonl on BUILD_RECEIPT — project milestone.
- jq February live-ops hourly row diff — strategy + exit codes.
- Thursday BUILD_RECEIPT row review — weekly cousin.
- HOTFIX blocked without exception list — when J3 fails because label changed.
- Visible build label opinion — why the string must exist.
- Sunday February closeout — consumes hourly jsonl.
- Help #38 forward — depot mismatch after setlive (different mode).
- Help #14 forward — SteamPipe queue delay (upload already in flight).
- Official: Unity Application.version, jq manual.
FAQ
Can we set february_fest_live_ops_hourly_ok true to unblock Thursday?
No—verify must exit 0 and last jsonl lines must carry real build_label values.
Is missing build_label the same as wrong Steam buildid?
No—buildid is SteamPipe; build_label is your team string. Both can be wrong independently.
Do we delete the whole jsonl file?
Prefer backup + surgical rewrite of bad lines; full delete loses Sunday closeout trail.
Unity team only?
Writer fields are engine-agnostic—map build_label from your visible version string.
Three cycles required?
Yes for this recovery article—one lucky hour can hide a still-broken writer.
Bookmark this fix for live-week hour three. Share it with whoever owns the append cron if the verify exit table saved your Thursday review.