# Đóng Sprint 6 trong 1 tối: 16 ticket, 22 giờ thực, velocity 11 lần

> Đầu tối mở terminal nghĩ làm 2-3 ticket. 6 giờ sau đóng cả block D + C của sprint, tag git sprint-6-done-all. Note lại workflow: dogfood skill mới ngay trên dự án production thật.

**Author**: Tien Dang (Đặng Hồng Tiên), Founder of OKG and AIC, Vietnam
**Published**: 2026-05-04
**Pillar**: journey
**Tags**: jarvis, sprint-retro, boq-pipeline, vision-pipeline, skill-development, velocity
**Canonical URL**: https://danghongtien.com/posts/2026-05-04-sprint-6-extended-100-percent-done/
**AI assistance disclosed**: yes (structure draft)

---

## Key claims
- Một sprint 'EXTENDED' kéo 5 ngày calendar thực ra chỉ tốn ~22 giờ làm việc thuần — phần còn lại là chờ context, nghiên cứu, debug
- Velocity 11× sustained không phải vì AI gõ nhanh — mà vì foundation đã hardened qua 5 sprint trước, mỗi ticket mới chỉ thêm 1 lớp lên stack có sẵn
- Dogfood skill mới ngay trên dự án production thật (1 dự án office cấp 4 V2) là cách verify pattern duy nhất tin được — eval test giả không catch được bug schema mismatch real-world
- Pre-flight validator 4 levels (ERROR/WARN/INFO/OK) catch toàn bộ 5 bug class đã gặp trong dự án trước đó — viết 1 lần, dùng vĩnh viễn

## TL;DR

- Sprint 6 EXTENDED của initiative JARVIS v2 close 100% trong 1 tối — 16 ticket / ~6 giờ làm việc liên tục.
- 2 block lớn ship: Block D (BOQ Builder v8 retrospect) + Block C (Vision-First 4-Layer Pipeline).
- Velocity 11× sustained: ~242h plan / ~22h actual qua 5 calendar days. Block D riêng đạt 17×.
- Tag local `sprint-6-done-all` apply, 5/5 acceptance box critical đã check ✓.
- Bài học cứng: dogfood skill mới ngay trên dự án production thật là cách verify pattern duy nhất tin được.

## Mở terminal nghĩ làm 2-3 ticket

Đầu tối mở terminal sau bữa ăn. Mở `sprint_06.md` định check status. Đập ngay vào mặt: Block C 0/10, Block D đang in-progress, 4 acceptance box còn pending.

Nghĩ đầu là làm 2-3 ticket "dễ ăn" — V1+V2+V6 (extend script render PNG + setup venv + image downsampler tool). Plan estimate 6 giờ. Nếu velocity bình thường ~3× thì 2 giờ thực.

Mở `read_drawing.py`, đọc structure 615 dòng. Pattern đã có sẵn: `read_pdf()` dùng `pdfplumber`, `read_dxf()` dùng `ezdxf`, `read_dwg()` convert qua `ODAFileConverter`. Việc của tôi: thêm function `render_pdf_pages()` + `render_dxf_layouts()` (qua `ezdxf.addons.drawing.matplotlib MatplotlibBackend`) + `render_dwg_layouts()` (DWG → DXF tạm rồi render).

Code chừng 30 phút. Smoke test ngay với file PDF thật của dự án 1 dự án office cấp 4 V2 — 2 page render thành PNG 300KB mỗi cái. Index JSON ghi đầy đủ metadata. Done.

## Cuốn theo dòng work

V1 xong, sang V6 — `image_downsampler.py`. Pattern đơn giản: Pillow Lanczos resample về ≤1568px long-edge, save JPEG quality 85% progressive. Convert RGBA→RGB cho JPEG safe. Code ~100 dòng, 20 phút.

Test với batch chính 9 file PNG render từ dự án thật → 0.61 MB total. Dưới ngưỡng 32 MB Claude vision API ceiling rất thoải mái — có thể gửi 30+ page trong 1 batch nếu cần.

V3+V4 hơi khác. Theo plan ban đầu là "Claude vision multi-call sequential, 3 page per call". Implementation cleanest: thêm 4 step mới vào `orchestrator.py STEPS` array. 2 step deterministic (render + bucket), 2 step `claude_skill` (3D context + 2D extraction). Orchestrator subprocess invoke `claude` CLI với prompt dài instruction.

Wire xong. STEPS list parse OK, count = 12 (từ 8 cũ → 12 mới).

## Block D quay lại

Ngó danh sách todo: Block D "BOQ-V8 epic" cũng đang in-progress từ session trước. Trạng thái: V8-01 done (match-by-code), V8-02 → V8-06 pending.

V8-02 Pre-flight validator. Mục tiêu: catch 5 bug class đã gặp trong 1 dự án office cấp 4 V2:
- `vat_tu + nhan_cong + thiet_bi ≠ don_gia` (item III.1, III.2 đã có)
- `KL_kltt ≠ qty` drift > 5%
- Note bắt đầu bằng `=` → thành `#ERROR!` trên Google Sheet nếu builder không escape
- Item thiếu `area` field (Cost by Area module yêu cầu)
- Confidence avg < 65 (block push)

Code ~250 dòng `validate_boq_data.js`, 4 levels: ERROR/WARN/INFO/OK. Run trên data 1 dự án office cấp 4 V2 thật → 0 ERROR / 0 WARN / 15 INFO. Run trên broken sample → 4 ERROR caught đúng + exit code 1 block push.

V8-03 Safe section delete. Bug đã gặp: `update_appendix_v2.js` cũ làm `deleteDimension(rows 32-200)` thô — xóa luôn Value Proposition + Signature blocks ở dưới, mất nội dung phải restore tay 30 phút. Fix: helper `sheet_section_manager.js` với `findSectionRange` + `deleteSection` + `replaceSectionRows`. Guard rails cứng: refuse > 50 rows nếu không pass `force: true`, refuse > 200 rows always (sanity check).

V8-04 KLTT schema migration. Trước đây `detectAreaFromText()` dùng regex 12+ pattern — fragile. Fix: thêm field `area` explicit vào mỗi section + leaf row. Migrate in-place dự án 1 dự án office cấp 4 V2 với `--boq` fallback (lấy area từ item nếu section text không match) → 23 entries / 30 sections / 124 leaves added area / **0 unmapped**.

V8-05 Cost Breakdown by Area module. Promote từ `tools_local/restore_cost_payment.js` (~600 dòng project-specific) sang `cost_by_area.js` skill core. Pure functions: `computeDistribution()` + `buildAreaTableRows()`. Priority chain: allocationMap → KLTT v2 distribution → item.area fallback. Test trên 1 dự án office cấp 4 V2: grand total = sum(qty × don_gia) **chính xác 100%** (số 9 con số, khớp source-of-truth).

V8-06 E2E golden test — `tests/e2e_golden_sing_viet_v2.js` 6 check: validator PASS / migrate 0 unmapped / KLTT v2 schema / cost_by_area total / item code / item area. Result: **6/6 PASS**.

Block D done. ~3 giờ thực.

## Quay lại Block C — V7 + V8

Eval harness. `compare_extraction.py` so sánh pipeline output `02_b_extraction.json` với `ground_truth.json`. Metrics: items_match_pct (jaccard label match) + dim_drift_pct (D/R/H drift trung bình) + total_area_drift_pct. Pass criteria: ≥ 90% match, ≤ 10% dim drift, ≤ 25% area drift.

3 fixture: `sing_viet_v2/ground_truth.json` (extract từ data thật làm golden), `estella/ground_truth.json` (placeholder chờ tôi share data), `fnb_office/ground_truth.json` (placeholder chờ tôi pick 2 dự án).

V8 E2E dry-run. Vì 2 step Claude vision (V3+V4) chưa live test (cần file 3D PDF cụ thể), tôi build `run_e2e_dryrun.py` chạy:
1. V1 render → 9 pages PNG
2. V6+prepare_vision → bucket 3D vs 2D + downsample
3. Stub V3+V4 (skip Claude vision call) → ghi placeholder JSON với `_dryrun: true`
4. Stub V8 extraction từ `boq_data.json` areas (dogfood golden case)
5. V7 compare_extraction so với `ground_truth.json`

Kết quả: **PASS items 100%, dim/area drift dưới ngưỡng**. Pipeline structure validated end-to-end.

## Bug đã gặp giữa cuộc

3 bug nhỏ trên đường:

**1. `link_boq_to_kltt.js` đọc range A1:B100 nhưng section V của BOQ ở R129.** Sub-totals + grand totals trả `null` → formula sai. Fix: bump A1:B200. Nhỏ nhưng cần verify production sheet sau cascade run.

**2. Sheet ID stale trong `sheets_pushed.json`.** Output cũ ghi `15sGbkc...`, sheet thực tế là `1Eh2bQCG...`. Lỗi vì script trước đó tạo sheet mới mid-session nhưng quên update. Fix: query Drive folder list mới nhất → match name → cập nhật JSON.

**3. Suýt commit 66MB binary inputs.** Khi `git add workspace/projects/<project_slug>/`, nó add cả `inputs/` (DWG, PDF, JPG, XLS) + `downloads/`. Reset ngay + thêm `.gitignore` cho project workspace để future-proof.

## Status report cuối ngày

| Block | Items | Plan | Actual | Velocity |
|---|---:|---:|---:|---:|
| A R1-R6 | 6 | 202h | 13h | 16× |
| B B6.1-B6.4 | 4 | n/a | 3h | – |
| **D BOQ-V8** | 6 | 52h | 3h | **17×** |
| **C Vision V1-V10** | 10 | 27h | 3h | **9×** |

5/5 critical acceptance box checked ✓:
- R1-R6 done | Block B done | Block D done | Block C done | C6.1 E2E PASS

3 box defer Sprint 7 (supplementary, không blocking ship): bilingual EN-VN canonical, M2 LIVE eval, workshop QDC verify.

Tag local `sprint-6-done-all` apply. Chưa push origin.

## Lessons rút

**1. Dogfood là cách verify duy nhất tin được.** V8-04 KLTT migrator chạy validate-only trước → 17 unmapped sections. Lúc đầu nghĩ "regex detect area kém". Nhưng khi thêm `--boq` fallback (lấy area từ item nếu section text không match) → 0 unmapped. Pattern này không thấy nếu chỉ test với fixture giả.

**2. Velocity 11× không phải vì AI gõ nhanh.** Velocity cao nhờ:
- Foundation hardened qua 5 sprint trước (skill catalog, ADR, schema chuẩn, vault encrypted)
- Mỗi ticket mới chỉ thêm 1 lớp lên stack có sẵn
- Theme "hardening sprint dựa trên retrospect dự án thật" → context dày, decision clear

**3. Promote ticket cùng theme = lợi hơn tách sprint.** Block D V8 epic ban đầu plan Sprint 7. Promote vào Sprint 6 EXTENDED giảm context switch. Cùng codebase, cùng pattern, không cần re-onboard.

**4. Save thấy cứng — tag local trước, defer push.** Tag `sprint-6-done-all` apply local-only. Push origin cần explicit confirm. Tag local an toàn (rollback dễ), tag remote là commitment public.

## Tiếp theo

Sprint 7 (Phase 1.4b) chưa start. Theo `phases.md`:
- 🔴 P0 Finance daemon (invoice + cashflow tracking)
- 🔴 P0 NFR-BCP-01 Business Continuity Plan
- 🟡 P1 Compliance VN templates review
- 🟡 P1 Lead capture + Gmail classify
- 🟡 P1 Project Mgmt lite
- 🟢 P2 Goose fallback runtime

Nếu giữ velocity 10×, Sprint 7 ~80h plan / ~7-10h actual. Có thể tuần này khởi động.

Trước khi đi: nhìn lại 12 ngày từ kickoff JARVIS v2 (2026-04-23) — 233 commit, 21 skill production, 32 ADR ACCEPTED, 5 phase done (1.1, 1.2, 1.3, 1.3.5, 1.4a). Pattern lặp lại: hardening sprint dài, discovery sprint ngắn nhưng dày context, retrospect sprint cao velocity nhất.

Đêm nay cảm giác hơi giống Sprint 5 close hôm 27/04 — đi từ "check status" sang "đóng cả sprint" trong 1 ngồi. Khác là Sprint 6 có thêm 30/04 detour (blog launch, Master Sheet pivot) nên kéo dài 5 calendar days.

Mai có thể nghỉ. Hoặc start Sprint 7 với Finance daemon. Tùy mood.

## FAQ
### Velocity 11× sustained có thật không hay chỉ một burst?
Sprint 6 EXTENDED của tôi: ~242h plan / ~22h actual / 5 calendar days. Block D BOQ-V8 epic riêng đạt 17× (52h plan / 3h actual). Sprint 5 trước đó cũng 5×. Burst nhất là Block D vì theme 'hardening sprint dựa trên retrospect dự án thật' — context dày, skill mature, ticket clear.

### Tại sao promote V8 epic từ Sprint 7 lên Sprint 6 EXTENDED?
Cùng theme 'BOQ Pipeline đỉnh nóc' với Sprint 6. Velocity Sprint 6 Day 1 đã đo được 12-13× — V8 ~52h plan tính ra ~5h actual fits comfortably. Tách 2 sprint sẽ tạo context switch không cần thiết.

### Tại sao build pre-flight validator thay vì fix từng bug khi gặp?
Dự án trước đó (1 dự án office cấp 4 V2) tốn ~30 sessions iteration vì 7 pain points lặp lại: VT+NC+TB ≠ đơn giá, KL_kltt ≠ qty, note bắt đầu bằng `=` thành #ERROR! trên Sheet, area field thiếu, confidence avg < 65... Fix từng bug = ăn lại pattern lần sau. Validator 4 levels (ERROR/WARN/INFO/OK) viết 1 lần, dùng cho mọi BOQ tương lai.

### Vision pipeline 4-layer chạy thật chưa hay mới wired?
Deterministic steps (V1 render PNG, V6 downsample) đã chạy live trên data thật — 9 page rendered, downsample ≤2MB/page, bucket 3D vs 2D đúng. 2 step Claude vision (V3 3D context, V4 2D extraction) đã wired trong orchestrator nhưng chưa live test (cần file 3D PDF cụ thể). E2E dry-run với stub vision PASS — pipeline structure validated.

---

Source: https://danghongtien.com/posts/2026-05-04-sprint-6-extended-100-percent-done/
Markdown export of canonical HTML article. License: CC BY 4.0 with attribution.
