@piagent/platform 1.36.1 → 1.37.1

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.
Files changed (92) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +1 -1
  3. package/README.vi.md +1 -1
  4. package/docs/command-reference-vietnamese.md +2 -2
  5. package/docs/distribution-standard.md +6 -6
  6. package/docs/languages.json +2 -2
  7. package/docs/operator-manual-vietnamese.md +7 -7
  8. package/docs/publishing-for-teams.md +2 -0
  9. package/docs/release-install-policy.md +2 -2
  10. package/docs/team-onboarding.md +2 -2
  11. package/docs-site/architecture.html +1 -1
  12. package/docs-site/benchmark.html +1 -1
  13. package/docs-site/commands.html +1 -1
  14. package/docs-site/content/en/index.html +4 -4
  15. package/docs-site/content/en/whats-new.html +12 -0
  16. package/docs-site/content/index.html +4 -4
  17. package/docs-site/content/whats-new.html +12 -0
  18. package/docs-site/context-engine.html +1 -1
  19. package/docs-site/ecosystem.html +1 -1
  20. package/docs-site/en/architecture.html +1 -1
  21. package/docs-site/en/benchmark.html +1 -1
  22. package/docs-site/en/commands.html +1 -1
  23. package/docs-site/en/context-engine.html +1 -1
  24. package/docs-site/en/ecosystem.html +1 -1
  25. package/docs-site/en/index.html +5 -5
  26. package/docs-site/en/mcp-approval.html +1 -1
  27. package/docs-site/en/mcp-auth.html +1 -1
  28. package/docs-site/en/mcp-servers.html +1 -1
  29. package/docs-site/en/mcp.html +1 -1
  30. package/docs-site/en/permissions.html +1 -1
  31. package/docs-site/en/profiles.html +1 -1
  32. package/docs-site/en/quickstart.html +1 -1
  33. package/docs-site/en/runtime.html +1 -1
  34. package/docs-site/en/scope.html +1 -1
  35. package/docs-site/en/security.html +1 -1
  36. package/docs-site/en/team.html +1 -1
  37. package/docs-site/en/whats-new.html +13 -1
  38. package/docs-site/en/windows.html +1 -1
  39. package/docs-site/en/workflows.html +1 -1
  40. package/docs-site/index.html +5 -5
  41. package/docs-site/mcp-approval.html +1 -1
  42. package/docs-site/mcp-auth.html +1 -1
  43. package/docs-site/mcp-servers.html +1 -1
  44. package/docs-site/mcp.html +1 -1
  45. package/docs-site/permissions.html +1 -1
  46. package/docs-site/profiles.html +1 -1
  47. package/docs-site/quickstart.html +1 -1
  48. package/docs-site/runtime.html +1 -1
  49. package/docs-site/scope.html +1 -1
  50. package/docs-site/security.html +1 -1
  51. package/docs-site/team.html +1 -1
  52. package/docs-site/whats-new.html +13 -1
  53. package/docs-site/windows.html +1 -1
  54. package/docs-site/workflows.html +1 -1
  55. package/evals/long-task-soak-v1/README.md +96 -0
  56. package/evals/long-task-soak-v1/lib/judge.mjs +67 -0
  57. package/evals/long-task-soak-v1/lib/persona.mjs +63 -0
  58. package/evals/long-task-soak-v1/lib/report.mjs +35 -0
  59. package/evals/long-task-soak-v1/lib/session-loop.mjs +165 -0
  60. package/evals/long-task-soak-v1/lib/workspace.mjs +115 -0
  61. package/evals/long-task-soak-v1/runner.mjs +89 -0
  62. package/evals/long-task-soak-v1/scenarios/autonomous-node-inventory.json +15 -0
  63. package/evals/long-task-soak-v1/scenarios/business-python-billing.json +34 -0
  64. package/evals/long-task-soak-v1/scenarios/framework-go-docker.json +40 -0
  65. package/evals/long-task-soak-v1/scenarios/framework-playwright-e2e.json +15 -0
  66. package/evals/long-task-soak-v1/scenarios/harness-guardrails-secrets.json +23 -0
  67. package/evals/long-task-soak-v1/scenarios/long-horizon-polyglot-rename.json +30 -0
  68. package/evals/long-task-soak-v1/scenarios/long-node-inventory-platform.json +64 -0
  69. package/evals/long-task-soak-v1/scenarios/long-polyglot-platform.json +288 -0
  70. package/evals/long-task-soak-v1/scenarios/long-postgres-booking-platform.json +210 -0
  71. package/evals/long-task-soak-v1/scenarios/long-python-billing-platform.json +84 -0
  72. package/evals/long-task-soak-v1/scenarios/mixed-requirement-change.json +20 -0
  73. package/evals/long-task-soak-v1/scenarios/stability-flaky-slow-suite.json +22 -0
  74. package/evals/long-task-soak-v1/scenarios/thirdparty-postgres-integration.json +30 -0
  75. package/evals/long-task-soak-v1/surfaces/terminal-rpc.mjs +150 -0
  76. package/evals/long-task-soak-v1/surfaces/webui-gateway.mjs +214 -0
  77. package/evals/private-holdout-v1/public-exposure.v2.json +2 -2
  78. package/package.json +2 -1
  79. package/packages/piagent-core/managed/helper-run.mjs +11 -1
  80. package/packages/piagent-core/managed/helper-settlement.mjs +17 -6
  81. package/packages/piagent-core/managed/ip-policy.mjs +50 -0
  82. package/packages/piagent-core/managed/memory-lessons.mjs +4 -1
  83. package/packages/piagent-core/managed/memory.mjs +1 -1
  84. package/packages/piagent-core/managed/package-proxy.mjs +12 -14
  85. package/packages/piagent-core/managed/session.mjs +4 -5
  86. package/packages/piagent-core/managed/skill-feedback.mjs +9 -1
  87. package/packages/piagent-core/managed/studio-link.mjs +16 -0
  88. package/packages/piagent-core/managed/web-fetch.mjs +3 -14
  89. package/packages/piagent-core/package.json +1 -1
  90. package/packages/piagent-core/runtime/managed-failure.mjs +5 -0
  91. package/scripts/company-terminal.mjs +2 -1
  92. package/scripts/piagent-studio.mjs +13 -3
@@ -0,0 +1,64 @@
1
+ {
2
+ "id": "long-node-inventory-platform",
3
+ "title": "A 60-120 minute session: inventory spec, then warehouses, HTTP API, audit log, a legacy refactor with a 20k-row import, and a CLI, as follow-ups in one conversation",
4
+ "category": ["long-horizon", "mixed", "business", "autonomous"],
5
+ "language": "vi",
6
+ "fixture": "node-inventory-long",
7
+ "requires": [],
8
+ "prompt": "Làm giúp anh toàn bộ SPEC.md cho thư viện kho-hang: cài đặt đủ 9 mục trong src/, viết test cho từng mục trong test/, chạy npm test đến khi qua hết. Đặc tả đã đủ, không cần hỏi lại, cứ làm đến xong.",
9
+ "oracle": { "hidden": "hidden", "checks": [{ "name": "main: SPEC.md acceptance", "run": "node --test 'test/*.test.js'", "timeoutSec": 600 }] },
10
+ "reference": "reference",
11
+ "phases": [
12
+ {
13
+ "id": "p2",
14
+ "prompt": "Ổn rồi. Giờ làm tiếp phần đa kho (warehouse) cho kho-hang, vẫn trong src/index.js, giữ nguyên mọi hành vi cũ (gọi kiểu cũ không truyền kho thì hiểu là kho 'MAIN'):\n\n1. receive(sku, qty, { lot, expiresOn, warehouse }): warehouse mặc định 'MAIN', phải là chuỗi khác rỗng (sai → INVALID_INPUT). Mỗi lô thuộc về một kho.\n2. onHand(sku, warehouse?) và available(sku, warehouse?): có warehouse thì tính riêng kho đó; không có thì là tổng các kho (available tổng = cộng available của từng kho).\n3. reserve(orderId, sku, qty, { warehouse }): mặc định 'MAIN'. Một đơn chỉ thuộc một kho: giữ chỗ cùng đơn ở kho khác → INVALID_INPUT. commit lấy hàng theo FEFO trong kho của đơn, kết quả vẫn là [{ sku, lot, qty }].\n4. Điều chuyển kho: transfer(transferId, sku, qty, { from, to, lot }) tạo phiếu chuyển đang chờ và giữ chỗ hàng ở kho nguồn (available kho nguồn giảm ngay, onHand chưa đổi). lot không bắt buộc: có lot thì chuyển đúng lô đó, không có thì lấy theo FEFO. Sai dữ liệu (qty không nguyên dương, from = to, thiếu from/to, transferId trùng một phiếu đang chờ) → INVALID_INPUT; không đủ hàng → INSUFFICIENT_STOCK. completeTransfer(transferId) chuyển hàng thật: trừ ở kho nguồn, cộng vào kho đích giữ nguyên tên lô và expiresOn (trùng lô thì cộng dồn), trả về [{ sku, lot, qty, expiresOn }] theo thứ tự đã lấy. cancelTransfer(transferId) huỷ phiếu và trả lại giữ chỗ. Phiếu lạ → InventoryError code 'UNKNOWN_TRANSFER'.\n5. stockReport({ warehouse }): mỗi cặp (kho, sku) đang có ít nhất một lô là một dòng { warehouse, sku, onHand, reserved, available }; reserved = giữ chỗ của các đơn + phiếu chuyển đang chờ đi ra từ kho đó; available = max(0, hàng chưa hết hạn − reserved). Không truyền warehouse thì lấy mọi kho.\n6. CSV và báo cáo: importCsv(text, { warehouse }) nhận thêm cột warehouse (không bắt buộc; không có cột hoặc ô trống thì lấy options.warehouse, mặc định 'MAIN'). exportCsv() giữ đúng định dạng cũ nhưng chỉ xuất kho MAIN; exportCsv({ warehouse: 'HN' }) xuất một kho theo định dạng cũ; exportCsv({ all: true }) có header warehouse,sku,lot,qty,expiresOn, sắp theo warehouse, sku, lot. lowStock(threshold, { warehouse }) và expiringWithin(days, { warehouse }) thêm tuỳ chọn lọc kho; không lọc thì expiringWithin gộp cùng sku + lot ở các kho thành một dòng (cộng qty), dòng vẫn có dạng như cũ.\n7. toJSON / fromJSON giữ cả kho, giữ chỗ và phiếu chuyển đang chờ.\n\nCó hai điểm nghiệp vụ anh chưa chốt, em hỏi anh trước khi code phần đó, đừng tự đoán:\n(a) lô đã hết hạn có được điều chuyển sang kho khác không (ví dụ gom về kho huỷ), nếu không thì báo lỗi thế nào;\n(b) lúc commit mà hàng chưa hết hạn không còn đủ cho phần đơn đã giữ (vì có lô hết hạn sau khi giữ chỗ) thì có cho tồn âm / backorder phần thiếu không, đơn xử lý thế nào.\n\nViết test cho từng mục trong test/, bổ sung SPEC.md (mục mới cho đa kho), chạy npm test đến khi qua hết.",
15
+ "oracle": { "hidden": "hidden-p2", "checks": [{ "name": "p2: warehouses, transfers, member answers", "run": "node --test 'test/*.test.js'", "timeoutSec": 600 }] },
16
+ "reference": "reference-p2"
17
+ },
18
+ {
19
+ "id": "p3",
20
+ "prompt": "Tốt. Tiếp theo anh cần một API HTTP JSON cho kho-hang, chỉ dùng node:http (không thêm thư viện ngoài nào):\n\n- src/server.js export createServer({ inventory }) trả về http.Server chưa listen (test sẽ tự listen(0)). Mọi phản hồi là JSON, content-type application/json; charset=utf-8.\n- Routes:\n POST /receive, body { sku, qty, lot, expiresOn?, warehouse? } → 201 { ok: true }\n POST /orders/:id/reserve, body { sku, qty, warehouse? } → 200 { ok: true }\n POST /orders/:id/release → 200 { ok: true } (đơn lạ cũng 200)\n POST /orders/:id/commit → 200 { taken: [...] }\n POST /transfers, body { id, sku, qty, from, to, lot? } → 201 { ok: true }\n POST /transfers/:id/complete → 200 { moved: [...] }\n POST /transfers/:id/cancel → 200 { ok: true }\n GET /stock/:sku?warehouse=HN → 200 { sku, warehouse, onHand, available } (không có warehouse thì warehouse: null và tính mọi kho; sku trong URL được URL-encode)\n GET /reports/low-stock?threshold=N, GET /reports/expiring?days=N, GET /reports/stock?warehouse= → 200, mảng y như thư viện trả\n- Kiểm tra đầu vào: không tự ép kiểu (qty \"5\" là sai), threshold và days bắt buộc và phải là số nguyên.\n- Lỗi trả body { error: { code, message } }. Ánh xạ: INVALID_INPUT 400; body không phải JSON hợp lệ → 400 INVALID_JSON; JSON nhưng không phải object → 400 INVALID_INPUT; UNKNOWN_ORDER, UNKNOWN_TRANSFER 404; không có route → 404 NOT_FOUND; route có nhưng sai method → 405 METHOD_NOT_ALLOWED; INSUFFICIENT_STOCK 409; EXPIRED_LOT 422; body trên 1 MB → 413 PAYLOAD_TOO_LARGE (vẫn trả JSON lỗi tử tế, đừng cắt kết nối giữa chừng); lỗi bất ngờ → 500 INTERNAL, không lộ stack.\n- bin/server.js chạy server: PORT (mặc định 3000, 0 = cổng trống bất kỳ), KHO_TODAY=YYYY-MM-DD để cố định ngày (dùng cho test); khi sẵn sàng in ra stdout đúng một dòng `listening on <port>`. Graceful shutdown: nhận SIGTERM hoặc SIGINT thì ngừng nhận kết nối mới, chờ request đang chạy xong rồi thoát mã 0, không được treo vì kết nối keep-alive.\n\nViết test tích hợp trong test/: khởi động server trên cổng 0, gọi bằng fetch, phủ các route, các mã lỗi và SIGTERM. Chạy npm test đến khi qua hết.",
21
+ "oracle": { "hidden": "hidden-p3", "checks": [{ "name": "p3: HTTP API", "run": "node --test 'test/*.test.js'", "timeoutSec": 600 }] },
22
+ "reference": "reference-p3"
23
+ },
24
+ {
25
+ "id": "p4",
26
+ "prompt": "Tiếp nhé, kế toán cần audit log và chống ghi trùng.\n\n1. Audit log trong thư viện: mọi thay đổi trạng thái thành công ghi đúng một sự kiện vào log chỉ ghi thêm (append-only); thao tác lỗi hoặc không đổi gì (ví dụ release đơn lạ) thì không ghi. Mỗi sự kiện có seq (1, 2, 3… tăng dần) và type thuộc đúng các loại: received, reserved, released, committed, transfer_created, transfer_completed, transfer_cancelled, cộng các trường em cần (phải serialize được bằng JSON).\n - inventory.events() trả về bản sao sâu của log: sửa bản sao không được ảnh hưởng log.\n - new Inventory({ clock, onEvent }): onEvent(event) được gọi đúng một lần cho mỗi sự kiện mới, theo thứ tự.\n - Inventory.replay(events, { clock, onEvent }) dựng lại y nguyên trạng thái (lô, giữ chỗ, phiếu chuyển đang chờ) từ danh sách sự kiện đã qua JSON. Quan trọng: replay áp dụng đúng kết quả đã ghi (lô nào, bao nhiêu) chứ không chạy lại FEFO, vì lúc replay ngày đã khác và có lô đã hết hạn. Inventory sau replay giữ nguyên log cũ, sự kiện mới tiếp tục seq; onEvent không bị gọi lại cho các sự kiện cũ.\n2. Lưu bền ở server: bin/server.js đọc biến KHO_LOG (đường dẫn file JSON lines). Khởi động thì dựng lại trạng thái từ file; mỗi sự kiện mới được ghi thêm vào cuối file ngay khi xảy ra. File chỉ được ghi thêm, không bao giờ viết lại.\n3. Idempotency cho mọi route POST qua header Idempotency-Key: cùng key + cùng method, path và cùng nội dung body JSON (so sánh sau khi parse, không phụ thuộc thứ tự khoá hay khoảng trắng) → trả lại đúng status và body của lần đầu, kèm header Idempotent-Replayed: true, không chạy lại. Cùng key nhưng khác request → 422 IDEMPOTENCY_KEY_REUSED. Chỉ lưu phản hồi thành công (2xx); request lỗi thì gửi lại cùng key vẫn được thực thi lại. Key phải còn hiệu lực sau khi server khởi động lại với cùng KHO_LOG (lưu thế nào tuỳ em, miễn đọc lại được). Không có header thì như cũ.\n\nViết test cho cả ba phần (có test khởi động lại server với KHO_LOG), bổ sung SPEC.md, chạy npm test đến khi qua hết.",
27
+ "oracle": { "hidden": "hidden-p4", "checks": [{ "name": "p4: audit log, replay, idempotency", "run": "node --test 'test/*.test.js'", "timeoutSec": 600 }] },
28
+ "reference": "reference-p4"
29
+ },
30
+ {
31
+ "id": "p5",
32
+ "prompt": "Phần tiếp theo là dọn nợ kỹ thuật, hơi nặng, em làm cẩn thận:\n\nA. src/legacy/report-engine.js (khoảng 3.750 dòng code cũ, nhiều hàm copy-paste). Tách thành các module trong src/reports/ mà KHÔNG đổi output:\n - src/legacy/report-engine.js chỉ còn là lớp nối (re-export), tối đa 150 dòng, vẫn export đủ VERSION và mọi hàm rpt_* hiện có với cùng chữ ký (rows, opts).\n - Trong src/reports/ có ít nhất 5 module, mỗi file không quá 600 dòng; gộp các helper và phần tính toán / định dạng bị lặp, tổng số dòng (src/reports/ + report-engine.js) không quá 60% file cũ.\n - Output của mọi hàm phải giữ y nguyên từng ký tự với mọi opts, kể cả những chỗ trông như bug hay không nhất quán giữa các định dạng: phòng kế toán và hai cửa hàng đang parse các output này. Nên chụp output của bản cũ (golden) trước khi tách rồi so lại.\nB. Nhập file data/stock-2025.csv (khoảng 20.000 dòng, xuất từ hệ thống cũ qua Excel) bằng thư viện:\n - src/import.js export async importFile(inventory, path, options) → { imported, errors: [{ line, message }] }, bên dưới dùng importCsv; line là số dòng vật lý trong file (header là dòng 1, dòng trống vẫn được đếm).\n - Siết importCsv cho dữ liệu thật: bỏ BOM UTF-8 đầu file; xử lý CRLF; dòng có số cột khác header là lỗi; qty phải là số nguyên dương; sku và lot không rỗng; expiresOn rỗng hoặc là ngày có thật dạng YYYY-MM-DD (2025-02-30 hay 31/12/2025 là lỗi), receive cũng phải từ chối ngày không có thật; các cột khác theo tên (name, category, …) thì bỏ qua.\n - Hiệu năng: nhập cả file phải dưới 5 giây.\n\nViết test cho cả hai phần, chạy npm test đến khi qua hết.",
33
+ "oracle": { "hidden": "hidden-p5", "checks": [{ "name": "p5: legacy split with golden outputs, big CSV import", "run": "node --test 'test/*.test.js'", "timeoutSec": 600 }] },
34
+ "reference": "reference-p5"
35
+ },
36
+ {
37
+ "id": "p6",
38
+ "prompt": "Phần cuối: gói tất cả thành công cụ dòng lệnh và viết tài liệu.\n\n1. bin/kho.js (chạy bằng node bin/kho.js …). Trạng thái lưu trong cùng định dạng log sự kiện với server: --log <file> (mặc định biến KHO_LOG, rồi ./kho.log.jsonl). Tham số chung: --today YYYY-MM-DD để cố định ngày, --json để in JSON (không có thì in bảng / chữ dễ đọc). Tham số có thể đứng trước hoặc sau đối số.\n - receive <sku> <qty> --lot <lô> [--expires YYYY-MM-DD] [--warehouse <kho>]\n - reserve <đơn> <sku> <qty> [--warehouse <kho>]; release <đơn>; commit <đơn> (--json in mảng taken)\n - transfer <mã> <sku> <qty> --from <kho> --to <kho> [--lot <lô>]; transfer-complete <mã> (--json in mảng moved); transfer-cancel <mã>\n - import <file.csv> [--warehouse <kho>]: in `imported <số dòng>` ra stdout, mỗi dòng lỗi in ra stderr dạng `line <số dòng>: <lý do>`\n - stock <sku> [--warehouse <kho>] (--json: { sku, onHand, available })\n - report low-stock --threshold <n> | report expiring --days <n> | report stock, đều nhận [--warehouse <kho>] (--json in đúng mảng như thư viện)\n - serve [--port <n>]: chạy API trên cùng log, in `listening on <port>`, SIGTERM thì thoát mã 0\n - --help in hướng dẫn liệt kê mọi lệnh, thoát 0.\n Mã thoát: 0 thành công; 1 lỗi nghiệp vụ, stderr bắt đầu bằng mã lỗi (ví dụ `INSUFFICIENT_STOCK: ...`); 2 sai cú pháp (lệnh lạ, thiếu tham số); 3 import có dòng lỗi (các dòng đúng vẫn được nhập).\n2. Viết lại README.md bằng tiếng Việt cho người mới vào: cài đặt và chạy test, thư viện, API HTTP (mọi route, bảng mã lỗi → HTTP, Idempotency-Key), audit log và KHO_LOG, CLI (mọi lệnh, có ví dụ), module báo cáo legacy report-engine sau khi tách.\n3. Rà lại toàn bộ: mọi quy tắc anh đã chốt từ đầu buổi đến giờ phải đúng ở cả thư viện, API và CLI. Thêm test cho CLI, chạy npm test đến khi qua hết rồi báo anh.",
39
+ "oracle": { "hidden": "hidden-p6", "checks": [{ "name": "p6: CLI, README, mid-session rule, full regression", "run": "node --test 'test/*.test.js'", "timeoutSec": 600 }] },
40
+ "reference": "reference-p6"
41
+ }
42
+ ],
43
+ "questionsExpected": false,
44
+ "decisions": [
45
+ {
46
+ "id": "expired-transfer", "mustAsk": true,
47
+ "topic": ["hết hạn[\\s\\S]{0,80}(điều chuyển|chuyển kho|chuyển sang|chuyển đi|chuyển lô|được chuyển|transfer)", "(điều chuyển|chuyển kho|chuyển sang|chuyển lô|transfer)[\\s\\S]{0,80}hết hạn", "kho hu[ỷủ]", "expired[\\s\\S]{0,80}(transfer|move)", "(transfer|move)[\\s\\S]{0,80}expired"],
48
+ "reply": "Không được chuyển lô đã hết hạn (lô hết hạn khi expiresOn < hôm nay), kể cả về kho huỷ. Cụ thể: (1) transfer chỉ định lot mà lô đó đã hết hạn → InventoryError code 'EXPIRED_LOT'. (2) transfer không chỉ định lot: nếu hàng chưa hết hạn ở kho nguồn (sau khi trừ phần đang giữ chỗ) không đủ nhưng tính thêm hàng đã hết hạn thì đủ → cũng 'EXPIRED_LOT', để kho biết là do hàng hết hạn; tính cả hàng hết hạn vẫn không đủ → 'INSUFFICIENT_STOCK'. (3) completeTransfer kiểm tra lại y như vậy: nếu trong lúc chờ chuyển mà lô hết hạn làm không đủ hàng thì từ chối 'EXPIRED_LOT', không thay đổi gì, phiếu vẫn ở trạng thái chờ (huỷ được)."
49
+ },
50
+ {
51
+ "id": "no-backorder", "mustAsk": true,
52
+ "topic": ["tồn âm", "âm kho", "số âm", "back.?order", "negative (stock|inventory|on.?hand|quantit)", "giao thiếu", "xuất thiếu", "commit[\\s\\S]{0,80}(không (còn )?đủ|thiếu|insufficient|not enough|short)", "(không (còn )?đủ|thiếu hàng|insufficient|not enough)[\\s\\S]{0,80}commit"],
53
+ "reply": "Không cho tồn âm, không backorder, cũng không xuất thiếu một phần. commit phải từ chối cả đơn với code 'INSUFFICIENT_STOCK' (kể cả khi các SKU khác trong đơn vẫn đủ hàng), không trừ gì cả, và GIỮ NGUYÊN giữ chỗ của đơn (không tự release) để bên bán tự xử lý; sau đó nhập thêm hàng rồi commit lại thì phải được."
54
+ }
55
+ ],
56
+ "approvals": "allow",
57
+ "userActions": [
58
+ { "when": { "minutes": 12 }, "do": "steer", "text": "À, anh chốt thêm một quy tắc cho báo cáo tồn theo kho stockReport (phần đa kho, nếu em chưa làm tới thì nhớ áp dụng khi làm): các dòng sắp theo available giảm dần; bằng nhau thì theo mã kho tăng dần, rồi tới sku tăng dần. Áp dụng ở mọi nơi trả báo cáo này về sau (thư viện, API, CLI). Em cứ làm tiếp việc đang làm." },
59
+ { "when": { "minutes": 30 }, "do": "stop_then_continue", "text": "Anh lỡ bấm dừng, em làm tiếp phần đang dở nhé." },
60
+ { "when": { "minutes": 50 }, "do": "restart", "hard": true, "text": "Anh vừa cập nhật Piagent nên Gateway khởi động lại, em làm tiếp nhé." },
61
+ { "when": { "minutes": 75 }, "do": "stop_then_continue", "text": "Anh bấm dừng xem tiến độ thôi, em tiếp tục từ chỗ đang làm nhé." }
62
+ ],
63
+ "budget": { "maxMinutes": 150, "maxNudges": 4, "stallMinutes": 15 }
64
+ }
@@ -0,0 +1,288 @@
1
+ {
2
+ "id": "long-polyglot-platform",
3
+ "title": "One long session on a polyglot monorepo: rename + contract v2, Go ledger with shared conformance, contract v3 with property tests, a 25k-line log investigation, a 3k-line legacy router refactor, CI and release 3.0.0",
4
+ "category": [
5
+ "long-horizon",
6
+ "polyglot",
7
+ "autonomous",
8
+ "framework"
9
+ ],
10
+ "language": "vi",
11
+ "fixture": "polyglot-monorepo-long",
12
+ "requires": [
13
+ "python",
14
+ "docker"
15
+ ],
16
+ "prompt": "Em làm giúp anh hết TODO.md nhé: đổi domain \"customer\" sang \"account\" trong toàn bộ monorepo (contract JSON, pricing-py, web-js, scripts, docs, fixtures), bump order contract lên v2 kèm compat shim đọc payload v1, cập nhật CHANGELOG cho release 2.0.0. Spec trong TODO.md đã chốt rồi, không cần hỏi lại. Làm xong thì chạy unittest Python, npm test bên web-js, release.sh --dry-run và grep lại cho sạch chữ customer, tick hết checklist. Mấy spec trong docs/specs/ là việc của các bước sau, anh sẽ giao dần trong cuộc trò chuyện này, giờ chưa đụng tới.",
17
+ "questionsExpected": false,
18
+ "decisions": [],
19
+ "approvals": "allow",
20
+ "userActions": [
21
+ {
22
+ "when": {
23
+ "minutes": 12
24
+ },
25
+ "do": "steer",
26
+ "text": "À anh bổ sung một rule từ phía Account API, áp dụng ngay và cho mọi service (pricing-py, web-js, và cả ledger-go khi làm tới): payload có account_id với khoảng trắng ở đầu hoặc cuối (vd \" A-1\", \"A-1 \", tab hay newline) phải bị reject với error code INVALID, ở mọi version payload (cả v1 đi qua compat shim). Không được tự trim. Khoảng trắng ở giữa (\"A 1\") vẫn hợp lệ. Các lỗi version/mixed fields vẫn ưu tiên như cũ. Nhớ ghi rule này vào CHANGELOG của release có nó."
27
+ },
28
+ {
29
+ "when": {
30
+ "minutes": 30
31
+ },
32
+ "do": "stop_then_continue",
33
+ "text": "Sorry anh lỡ bấm Stop. Em làm tiếp phần đang dở nhé, không cần làm lại từ đầu."
34
+ },
35
+ {
36
+ "when": {
37
+ "minutes": 50
38
+ },
39
+ "do": "restart",
40
+ "hard": true,
41
+ "text": "Gateway bên công ty vừa restart nên phiên bị ngắt giữa chừng. Em xem lại repo đang tới đâu (git status, chạy test) rồi làm tiếp cho xong việc đang làm nhé."
42
+ },
43
+ {
44
+ "when": {
45
+ "minutes": 78
46
+ },
47
+ "do": "stop_then_continue",
48
+ "text": "Anh bấm Stop chỉ để xem tiến độ thôi, ổn rồi. Em tiếp tục cho xong nhé, xong nhớ chạy lại test."
49
+ }
50
+ ],
51
+ "budget": {
52
+ "maxMinutes": 150,
53
+ "maxNudges": 4,
54
+ "stallMinutes": 15
55
+ },
56
+ "oracle": {
57
+ "hidden": "hidden",
58
+ "checks": [
59
+ {
60
+ "name": "pricing-py unittest (hidden acceptance + own tests)",
61
+ "run": "cd services/pricing-py && /usr/bin/python3 -m unittest discover -s tests -v",
62
+ "timeoutSec": 300,
63
+ "stopOnFail": false
64
+ },
65
+ {
66
+ "name": "web-js node --test (hidden acceptance + own tests)",
67
+ "run": "cd services/web-js && node --test 'test/*.test.js'",
68
+ "timeoutSec": 300,
69
+ "stopOnFail": false
70
+ },
71
+ {
72
+ "name": "release.sh and dev-env.sh use ACCOUNT_API_URL, release 2.0.0 / contract v2",
73
+ "run": "bash .soak/check_release.sh",
74
+ "timeoutSec": 120,
75
+ "stopOnFail": false
76
+ },
77
+ {
78
+ "name": "no leftover customer outside CHANGELOG/TODO/compat; bookkeeping done",
79
+ "run": "/usr/bin/python3 .soak/check_rename.py",
80
+ "timeoutSec": 120,
81
+ "stopOnFail": false
82
+ }
83
+ ]
84
+ },
85
+ "reference": "reference",
86
+ "referenceSetup": [
87
+ "rm -f services/pricing-py/pricing/customer.py services/pricing-py/fixtures/customers.json services/pricing-py/tests/test_customer.py services/web-js/src/customer.js services/web-js/test/customer.test.js docs/customer-tiers.md"
88
+ ],
89
+ "phases": [
90
+ {
91
+ "id": "p2",
92
+ "prompt": "Release 2.0.0 ổn rồi, cảm ơn em. Giờ làm tiếp docs/specs/2-ledger-go.md nhé: service mới services/ledger-go (Go, stdlib only) đọc file event order (JSON Lines) và tính balance theo account, có CLI; đồng thời dựng bộ shared contract conformance để cùng một bộ payload chạy ở Python, Node và Go, và sửa luôn bug SHOP-412 về currency. Máy không cài Go, chạy go test / go vet qua Docker như trong spec. Spec đã chốt, không cần hỏi lại; xong thì chạy đủ test cả ba service.",
93
+ "oracle": {
94
+ "hidden": "hidden-p2",
95
+ "checks": [
96
+ {
97
+ "name": "pricing-py unittest (hidden acceptance + own tests)",
98
+ "run": "cd services/pricing-py && /usr/bin/python3 -m unittest discover -s tests -v",
99
+ "timeoutSec": 300,
100
+ "stopOnFail": false
101
+ },
102
+ {
103
+ "name": "web-js node --test (hidden acceptance + own tests)",
104
+ "run": "cd services/web-js && node --test 'test/*.test.js'",
105
+ "timeoutSec": 300,
106
+ "stopOnFail": false
107
+ },
108
+ {
109
+ "name": "ledger-go go vet + go test (Docker, hidden acceptance + own tests)",
110
+ "run": "bash .soak/go.sh 'timeout 540 go vet ./... && timeout 540 go test -count=1 ./...'",
111
+ "timeoutSec": 600,
112
+ "stopOnFail": false
113
+ },
114
+ {
115
+ "name": "ledger CLI books the hidden event file exactly",
116
+ "run": "bash .soak/p2/check_cli.sh",
117
+ "timeoutSec": 600,
118
+ "stopOnFail": false
119
+ }
120
+ ]
121
+ },
122
+ "reference": "reference-p2"
123
+ },
124
+ {
125
+ "id": "p3",
126
+ "prompt": "Tiếp spec 3 (docs/specs/3-contract-v3.md): order contract v3 thêm settlement_currency và fx_rate_ppm. Cả ba service phải đọc được v1/v2/v3 theo luật compat trong spec, writer ghi version thấp nhất đủ chở order, settlement total phải chính xác tuyệt đối, ledger tính balance theo settlement currency. Viết thêm property-style tests với random payload có seed cố định ở cả Python, Node và Go. Làm hết rồi chạy lại đủ test ba service nhé.",
127
+ "oracle": {
128
+ "hidden": "hidden-p3",
129
+ "checks": [
130
+ {
131
+ "name": "pricing-py unittest (hidden acceptance + own tests)",
132
+ "run": "cd services/pricing-py && /usr/bin/python3 -m unittest discover -s tests -v",
133
+ "timeoutSec": 300,
134
+ "stopOnFail": false
135
+ },
136
+ {
137
+ "name": "web-js node --test (hidden acceptance + own tests)",
138
+ "run": "cd services/web-js && node --test 'test/*.test.js'",
139
+ "timeoutSec": 300,
140
+ "stopOnFail": false
141
+ },
142
+ {
143
+ "name": "ledger-go go vet + go test (Docker, hidden acceptance + own tests)",
144
+ "run": "bash .soak/go.sh 'timeout 540 go vet ./... && timeout 540 go test -count=1 ./...'",
145
+ "timeoutSec": 600,
146
+ "stopOnFail": false
147
+ },
148
+ {
149
+ "name": "1500 seeded v1/v2/v3 payloads agree with the spec in Python, Node and Go",
150
+ "run": "bash .soak/p3/crosscheck.sh",
151
+ "timeoutSec": 600,
152
+ "stopOnFail": false
153
+ }
154
+ ]
155
+ },
156
+ "reference": "reference-p3"
157
+ },
158
+ {
159
+ "id": "p4",
160
+ "prompt": "Giờ chuyển qua vụ sự cố hôm 14/10: làm spec 4 (docs/specs/4-log-report.md). Viết scripts/log_report.py đọc logs/prod-2025-10.log, ra top error signature, p95 latency theo endpoint và khung giờ incident đúng theo định nghĩa trong spec, số liệu sẽ được đối chiếu chính xác từng con số. Không sửa file log. Viết test cho tool và ghi postmortem vào docs/incidents/2025-10-14.md.",
161
+ "oracle": {
162
+ "hidden": "hidden-p4",
163
+ "checks": [
164
+ {
165
+ "name": "log_report.py exact numbers (prod log + unseen log) and postmortem",
166
+ "run": "/usr/bin/python3 .soak/p4/check_log_report.py",
167
+ "timeoutSec": 600,
168
+ "stopOnFail": false
169
+ },
170
+ {
171
+ "name": "scripts/tests unittest",
172
+ "run": "/usr/bin/python3 -m unittest discover -s scripts/tests -v",
173
+ "timeoutSec": 300,
174
+ "stopOnFail": false
175
+ }
176
+ ]
177
+ },
178
+ "reference": "reference-p4"
179
+ },
180
+ {
181
+ "id": "p5",
182
+ "prompt": "Tiếp spec 5: tách services/web-js/src/legacy/router-old.js (~3000 dòng) thành các module nhỏ trong src/router/, hành vi phải y hệt bản cũ kể cả mấy quirk ghi ở đầu file và message lỗi. Nhớ ghi golden từ router cũ TRƯỚC khi đụng vào code, rồi mới refactor và xoá file legacy. Xong chạy npm test.",
183
+ "oracle": {
184
+ "hidden": "hidden-p5",
185
+ "checks": [
186
+ {
187
+ "name": "web-js node --test (hidden acceptance + own tests)",
188
+ "run": "cd services/web-js && node --test 'test/*.test.js'",
189
+ "timeoutSec": 300,
190
+ "stopOnFail": false
191
+ },
192
+ {
193
+ "name": "legacy router removed, split into small modern modules, own golden test",
194
+ "run": "node .soak/p5/check_structure.mjs",
195
+ "timeoutSec": 120,
196
+ "stopOnFail": false
197
+ }
198
+ ]
199
+ },
200
+ "reference": "reference-p5",
201
+ "referenceSetup": [
202
+ "rm -rf services/web-js/src/legacy"
203
+ ]
204
+ },
205
+ {
206
+ "id": "p6",
207
+ "prompt": "Bước cuối, spec 6 (docs/specs/6-ci-release-3.md): viết scripts/ci.sh chạy mọi thứ (Go qua Docker), cập nhật docs, CHANGELOG và bump release 3.0.0 cho cả ba service. Trước khi chốt em rà lại toàn bộ: mọi yêu cầu anh đã thêm giữa chừng trong cuộc trò chuyện này phải có đủ ở cả ba service (kể cả ledger-go), và chạy bash scripts/ci.sh tới khi in ci: all ok.",
208
+ "oracle": {
209
+ "hidden": "hidden-p6",
210
+ "checks": [
211
+ {
212
+ "name": "pricing-py unittest (hidden acceptance + own tests)",
213
+ "run": "cd services/pricing-py && /usr/bin/python3 -m unittest discover -s tests -v",
214
+ "timeoutSec": 300,
215
+ "stopOnFail": false
216
+ },
217
+ {
218
+ "name": "scripts/tests unittest",
219
+ "run": "/usr/bin/python3 -m unittest discover -s scripts/tests -v",
220
+ "timeoutSec": 300,
221
+ "stopOnFail": false
222
+ },
223
+ {
224
+ "name": "web-js node --test (hidden acceptance + own tests)",
225
+ "run": "cd services/web-js && node --test 'test/*.test.js'",
226
+ "timeoutSec": 300,
227
+ "stopOnFail": false
228
+ },
229
+ {
230
+ "name": "ledger-go go vet + go test (Docker, hidden acceptance + own tests)",
231
+ "run": "bash .soak/go.sh 'timeout 540 go vet ./... && timeout 540 go test -count=1 ./...'",
232
+ "timeoutSec": 600,
233
+ "stopOnFail": false
234
+ },
235
+ {
236
+ "name": "ledger CLI books the hidden event file exactly",
237
+ "run": "bash .soak/p2/check_cli.sh",
238
+ "timeoutSec": 600,
239
+ "stopOnFail": false
240
+ },
241
+ {
242
+ "name": "1500 seeded v1/v2/v3 payloads agree with the spec in Python, Node and Go",
243
+ "run": "bash .soak/p3/crosscheck.sh",
244
+ "timeoutSec": 600,
245
+ "stopOnFail": false
246
+ },
247
+ {
248
+ "name": "log_report.py exact numbers (prod log + unseen log) and postmortem",
249
+ "run": "/usr/bin/python3 .soak/p4/check_log_report.py",
250
+ "timeoutSec": 600,
251
+ "stopOnFail": false
252
+ },
253
+ {
254
+ "name": "legacy router removed, split into small modern modules, own golden test",
255
+ "run": "node .soak/p5/check_structure.mjs",
256
+ "timeoutSec": 120,
257
+ "stopOnFail": false
258
+ },
259
+ {
260
+ "name": "release 3.0.0 / contract v3 across three services, v3 samples and ledger-go version checked",
261
+ "run": "bash .soak/check_release.sh",
262
+ "timeoutSec": 300,
263
+ "stopOnFail": false
264
+ },
265
+ {
266
+ "name": "no leftover customer outside CHANGELOG/TODO/compat (whole repo, 3.0.0)",
267
+ "run": "/usr/bin/python3 .soak/check_rename.py",
268
+ "timeoutSec": 120,
269
+ "stopOnFail": false
270
+ },
271
+ {
272
+ "name": "CHANGELOG 3.0.0 (incl. the mid-task account_id rule), docs/ledger.md, docs/contract.md v3, README",
273
+ "run": "/usr/bin/python3 .soak/p6/check_docs.py",
274
+ "timeoutSec": 60,
275
+ "stopOnFail": false
276
+ },
277
+ {
278
+ "name": "scripts/ci.sh runs every step, passes, and fails on a failing Python or Go test",
279
+ "run": "bash .soak/p6/check_ci.sh",
280
+ "timeoutSec": 1800,
281
+ "stopOnFail": false
282
+ }
283
+ ]
284
+ },
285
+ "reference": "reference-p6"
286
+ }
287
+ ]
288
+ }
@@ -0,0 +1,210 @@
1
+ {
2
+ "id": "long-postgres-booking-platform",
3
+ "title": "Long session (60–120 min): meeting-room booking platform on real PostgreSQL grown over six phases — SPEC, recurring bookings, node:http API with roles, window-function reporting on 50k rows with an index, a 20k-row legacy CSV migration, operations scripts and final regression — with a mid-task rule change, Stops and a Gateway restart",
4
+ "category": [
5
+ "long-horizon",
6
+ "third-party",
7
+ "business",
8
+ "integration"
9
+ ],
10
+ "language": "vi",
11
+ "fixture": "node-postgres-long",
12
+ "requires": [
13
+ "docker",
14
+ "network"
15
+ ],
16
+ "prompt": "Hôm nay anh em mình làm cả buổi trên repo dat-phong-hop này. Bắt đầu với toàn bộ SPEC.md cho dịch vụ đặt lịch phòng họp nhé: thêm migration mới, hoàn thiện repository (phòng họp và đặt lịch), chống trùng lịch an toàn khi nhiều người đặt cùng lúc, huỷ đặt lịch, danh sách có phân trang, và viết test tích hợp chạy với PostgreSQL thật (dùng scripts/db-up.sh hoặc docker-compose.yml). Chỗ nào SPEC ghi \"cần chủ sản phẩm chốt\" thì hỏi anh trước khi làm phần đó, còn lại em cứ tự làm đến khi npm run migrate && npm test qua hết. Xong thì dừng container Postgres em đã bật. Các phần sau anh sẽ giao tiếp trong docs/backlog/, em chưa cần làm trước.",
17
+ "questionsExpected": false,
18
+ "decisions": [
19
+ {
20
+ "id": "legacy-conflict-import-cancelled",
21
+ "mustAsk": true,
22
+ "topic": [
23
+ "legacy",
24
+ "h[eệ] th[oố]ng c[uũ]",
25
+ "d[uữ] li[eệ]u c[uũ]",
26
+ "d[oò]ng c[uũ]",
27
+ "l[iị]ch c[uũ]",
28
+ "b[aả]n ghi c[uũ]",
29
+ "import",
30
+ "nh[aậ]p (d[uữ] li[eệ]u|l[iị]ch|v[aà]o)",
31
+ "\\bCSV\\b",
32
+ "migrat(e|ion) (data|of the old)"
33
+ ],
34
+ "option": "legacy-conflict",
35
+ "reply": "Dòng cũ đang hiệu lực mà trùng giờ với đặt lịch đã có trong hệ thống mới thì KHÔNG bỏ qua: vẫn nhập vào nhưng ở trạng thái đã huỷ, cancelled_at là thời điểm chạy import, và thêm cột cancelled_reason (text) với giá trị 'legacy-conflict' để sau này tra lại. Đặt lịch đang có trong hệ thống mới giữ nguyên. Những dòng này tính vào imported và conflicts, không nằm trong problems."
36
+ },
37
+ {
38
+ "id": "series-conflict-reject-all",
39
+ "mustAsk": true,
40
+ "topic": [
41
+ "chu[oỗ]i",
42
+ "series",
43
+ "recurr",
44
+ "[dđ][iị]nh k[yỳ]",
45
+ "l[aặ]p l[aạ]i",
46
+ "l[aầ]n l[aặ]p",
47
+ "bu[oổ]i (tr[uù]ng|b[iị] tr[uù]ng|l[aặ]p)",
48
+ "occurrence"
49
+ ],
50
+ "option": "từ chối (cả|toàn bộ)|reject (the )?(whole|entire|all)|whole series|entire series|all.or.nothing|không tạo buổi nào",
51
+ "reply": "Từ chối cả chuỗi, không tạo buổi nào: ném BOOKING_CONFLICT, và error.conflicts liệt kê đầy đủ mọi buổi bị trùng ({ startsAt, endsAt }, theo thứ tự thời gian) để người đặt biết mà chọn giờ khác. Không bỏ qua riêng buổi trùng."
52
+ },
53
+ {
54
+ "id": "cancel-soft-delete",
55
+ "mustAsk": true,
56
+ "topic": [
57
+ "hu[ỷy]",
58
+ "h[ủu]y",
59
+ "cancel",
60
+ "x[oó][aáà]",
61
+ "delete",
62
+ "soft",
63
+ "xoá mềm|xóa mềm|xoá hẳn|xóa hẳn"
64
+ ],
65
+ "option": "soft|mềm|cancelled_at|giữ lại|đánh dấu|mark",
66
+ "reply": "Huỷ là xoá mềm: giữ lại bản ghi và đặt cancelled_at = thời điểm huỷ (cột cancelled_at trong bảng bookings). Đặt lịch đã huỷ không tính khi kiểm tra trùng và không hiện trong listBookings mặc định, nhưng listBookings({ includeCancelled: true }) vẫn phải trả về chúng (kèm cancelledAt), getBooking vẫn trả về được, và huỷ lần nữa thì báo ALREADY_CANCELLED."
67
+ },
68
+ {
69
+ "id": "back-to-back",
70
+ "mustAsk": true,
71
+ "topic": [
72
+ "li[eề]n k[eề]",
73
+ "liền nhau|nối tiếp|sát nhau",
74
+ "back.?to.?back",
75
+ "adjacent",
76
+ "end.*start",
77
+ "kết thúc.*bắt đầu",
78
+ "ch[aạ]m",
79
+ "\\[\\s*\\)|half.?open|nửa mở"
80
+ ],
81
+ "option": "không (bị |được )?(tính là |coi là )?(trùng|xung đột)|cho phép|allow|not (a )?conflict|no conflict|don.?t conflict|half.?open|\\[\\)",
82
+ "reply": "Hai lịch liền kề (giờ kết thúc lịch này bằng giờ bắt đầu lịch kia, ví dụ 09:00–10:00 và 10:00–11:00) KHÔNG tính là trùng, phải cho phép đặt. Chỉ khi hai khoảng thật sự giao nhau mới là BOOKING_CONFLICT."
83
+ }
84
+ ],
85
+ "approvals": "allow",
86
+ "userActions": [
87
+ {
88
+ "when": {
89
+ "minutes": 15
90
+ },
91
+ "do": "steer",
92
+ "text": "Anh bổ sung một yêu cầu, áp dụng từ giờ cho mọi danh sách đặt lịch (listBookings, và sau này cả API): sắp theo giờ bắt đầu, cùng giờ bắt đầu thì theo tên phòng A→Z (không phân biệt hoa thường), rồi mới tới id. Phân trang con trỏ phải đúng theo thứ tự mới, không lặp, không sót. Yêu cầu này giữ nguyên cho các phần sau."
93
+ },
94
+ {
95
+ "when": {
96
+ "minutes": 32
97
+ },
98
+ "do": "stop_then_continue",
99
+ "text": "Anh lỡ tay bấm Stop, xin lỗi em. Em làm tiếp phần đang dở nhé, không cần làm lại từ đầu."
100
+ },
101
+ {
102
+ "when": {
103
+ "minutes": 52
104
+ },
105
+ "do": "restart",
106
+ "hard": true,
107
+ "text": "Anh vừa phải khởi động lại gateway của công ty nên cuộc trò chuyện bị ngắt giữa chừng. Em kiểm tra lại trạng thái repo (git status, chạy test) rồi làm tiếp phần đang dở cho xong nhé."
108
+ },
109
+ {
110
+ "when": {
111
+ "minutes": 80
112
+ },
113
+ "do": "stop_then_continue",
114
+ "text": "Anh dừng em một chút để xem tiến độ, ổn rồi. Em tiếp tục phần đang làm cho đến khi xong hết nhé."
115
+ }
116
+ ],
117
+ "budget": {
118
+ "maxMinutes": 160,
119
+ "maxNudges": 4,
120
+ "stallMinutes": 15
121
+ },
122
+ "oracle": {
123
+ "hidden": "hidden",
124
+ "checks": [
125
+ {
126
+ "name": "main: SPEC acceptance on a throwaway PostgreSQL",
127
+ "run": "bash scripts/oracle.sh",
128
+ "timeoutSec": 600
129
+ }
130
+ ]
131
+ },
132
+ "reference": "reference",
133
+ "phases": [
134
+ {
135
+ "id": "p2",
136
+ "prompt": "Phần SPEC ổn rồi, cảm ơn em. Tiếp theo em làm docs/backlog/02-lich-dinh-ky.md (đặt lịch định kỳ hằng ngày/hằng tuần) nhé, cùng cách làm như trước: chỗ nào ghi \"cần chủ sản phẩm chốt\" thì hỏi anh, còn lại tự làm đến khi npm run migrate && npm test qua hết (test cũ vẫn phải qua). Xong thì dừng container Postgres.",
137
+ "oracle": {
138
+ "hidden": "hidden-p2",
139
+ "checks": [
140
+ {
141
+ "name": "p2: recurring bookings + all earlier acceptance",
142
+ "run": "bash scripts/oracle.sh",
143
+ "timeoutSec": 600
144
+ }
145
+ ]
146
+ },
147
+ "reference": "reference-p2"
148
+ },
149
+ {
150
+ "id": "p3",
151
+ "prompt": "Ok anh xem rồi. Giờ tới docs/backlog/03-http-api.md: API HTTP JSON bằng node:http (không dùng framework), token đơn giản với vai trò admin/member, member chỉ huỷ được lịch của chính mình. Test tích hợp khởi động server trên cổng 0. Làm đến khi toàn bộ test qua, xong dừng container.",
152
+ "oracle": {
153
+ "hidden": "hidden-p3",
154
+ "checks": [
155
+ {
156
+ "name": "p3: node:http API with roles + all earlier acceptance",
157
+ "run": "bash scripts/oracle.sh",
158
+ "timeoutSec": 600
159
+ }
160
+ ]
161
+ },
162
+ "reference": "reference-p3"
163
+ },
164
+ {
165
+ "id": "p4",
166
+ "prompt": "Tiếp theo là docs/backlog/04-bao-cao.md: báo cáo công suất phòng theo tuần tính bằng SQL (hàm cửa sổ), index bookings_report_idx, script seed 50.000 đặt lịch và route /reports/utilisation cho admin. Em tự kiểm tra bằng EXPLAIN là báo cáo dùng đúng index và chạy dưới 2 giây trên dữ liệu seed nhé. Xong dừng container.",
167
+ "oracle": {
168
+ "hidden": "hidden-p4",
169
+ "checks": [
170
+ {
171
+ "name": "p4: utilisation report, index use and speed on 50k rows + all earlier acceptance",
172
+ "run": "bash scripts/oracle.sh",
173
+ "timeoutSec": 900
174
+ }
175
+ ]
176
+ },
177
+ "reference": "reference-p4"
178
+ },
179
+ {
180
+ "id": "p5",
181
+ "prompt": "Phần lớn nhất đây: docs/backlog/05-chuyen-du-lieu-cu.md, chuyển dữ liệu từ hệ thống cũ (data/legacy-bookings.csv, khoảng 20.000 dòng). Em đọc kỹ legacy/import-old.js để hiểu đúng cách hệ thống cũ đọc dữ liệu, nhưng không sửa file đó và không sửa file CSV. Viết importer mới có dry-run và báo cáo, chạy thử dry-run trên file thật rồi gửi anh số liệu. Chỗ cần chủ sản phẩm chốt thì hỏi anh. Xong dừng container.",
182
+ "oracle": {
183
+ "hidden": "hidden-p5",
184
+ "checks": [
185
+ {
186
+ "name": "p5: legacy CSV importer (dry-run, report, re-run) + all earlier acceptance",
187
+ "run": "bash scripts/oracle.sh",
188
+ "timeoutSec": 1200
189
+ }
190
+ ]
191
+ },
192
+ "reference": "reference-p5"
193
+ },
194
+ {
195
+ "id": "p6",
196
+ "prompt": "Cuối cùng là docs/backlog/06-ban-giao.md: db:reset, db:seed, mục \"Vận hành\" trong README bằng tiếng Việt, rồi kiểm tra hồi quy toàn bộ trên một database mới: mọi yêu cầu từ đầu buổi tới giờ, kể cả những điều anh dặn thêm giữa chừng. Xong nhớ dừng mọi container em đã bật.",
197
+ "oracle": {
198
+ "hidden": "hidden-p6",
199
+ "checks": [
200
+ {
201
+ "name": "p6: operations scripts, README, the mid-task ordering rule + full regression",
202
+ "run": "bash scripts/oracle.sh",
203
+ "timeoutSec": 1200
204
+ }
205
+ ]
206
+ },
207
+ "reference": "reference-p6"
208
+ }
209
+ ]
210
+ }