Files
solution-erp/.claude/workflows/hmw.js
pqhuy1987 00a1c67e55 [CLAUDE] Infra: W2 GOP-1-COMMIT - San-1 gate + PA-2a + roster 12->14 (2 monitor H24) + h24_cadence + counter + STATUS 2 row
Wave adap-6-broadcast W2 = 1 OWNER / 1 COMMIT co chu dich. Cac manh phu-thuoc nhau, tach ra la vo
duong dang chay (R2-C3): gate o W2 ma co o W3 => moi /fable-clone >=3 lane THROW ngay truoc khi W3 kip va.

(1) hmw.js - 1 EDIT, 4 viec
    - SAN-1 GATE co-khi-hoa: taskList>=3 && !args.run => THROW STOP-HARD.
      Truoc S121 san nay chi la CHU trong doc ("script no-fs KHONG tu ep") => phu thuoc lead NHO.
      Do that @S119: git grep args.run .claude/commands/ = 0 hit => 4/4 wrapper KHONG co => san danh-nghia.
      Vi sao >=3: chet giua run dai ma khong co runs/<run-id>/ => per-turn returns mat theo phien,
      CHI file tren dia song sot => /tiep §4 khong co vat-lieu cat-got => chay lai TU DAU (S118
      raw-Agent 7-spawn => 0 run-folder). THROW chu khong fail-soft (fail-soft = dut am-tham).
    - VALID_ROLES 12->14 (+lead-view-auditor +lead-omission-auditor).
    - PA-2a: TIER2_EXPECTED_FULL_ID = 'claude-opus-4-8'. La HANG-SO KHAI-BAO, KHONG phai co-che neo
      (neo-tai-spawn bat-kha-thi: param chi nhan alias; SE 0 full-id vi H8 all-inherit). Doc boi
      scripts/spawn-model-audit.ps1 (W1b) - thieu hang-so => script FAIL-LOUD.
    - H23 note CHUA-VERIFY giu nguyen nac: precedence spawn-param vs frontmatter khi CA HAI hien-dien
      = CHUA TUNG TEST; phan-biet duoc CHI o phien lead=Fable => DEFER, khong ban qua nac. + fix#8a chua thu.

(2) 4 co wrapper CUNG COMMIT voi gate (San-1 (c))
    fable-clone.md / fable-real.md / ultra-on.md => args.run: 0 -> 3 hit.
    Wrapper thu 4 = raw-invoke em-main: KHONG co file de gan co => duoc GATE chan truc tiep
    (chinh cu fault-inject duoi day di duong do). Khai that: gate chi phu hmw.js; nguong "hoac uoc
    >5 phut" KHONG co-khi-hoa duoc (script khong do truoc thoi-luong) => van la ky-luat lead;
    raw Workflow/Agent tool = ke CON-LAI khong gate duoc.
    + go so roster stale (view-stale-role-desc): "roster 12"/"12 sub"/"8-agent"/"9-agent" -> tro
    canonical docs/STATUS.md §Sub-agents (B1). session-start.md con "roster 12" = W3 so-huu, KHONG dung.

(3) 2 vai MOI - H24 §2(1), cookie-cutter frontmatter monitor san
    lead-view-auditor  = view LECH source (5 class view-*)
    lead-omission-auditor = cai BI THIEU (5 class gap-*)
    model:inherit / memory:project / maxTurns:18 / 8 tool read-only / NO Write-Edit / propose-only.
    KHONG gop vao tooling-auditor(H1)/harvest-curator(H2): H24 "gop la mat tieu-diem; can cap mat
    CHI danh cho lead". Tu-vung view/omission chon tranh va-cham (detectors:15 da so-huu
    derived-staleness; GAP qua-tai). 2 file 0-CR + block-scalar (giu ': ' N/A).

(4) memory-budget.json - single-source H24 §2(3)
    h24_cadence { light_every:6, deep_every:15, jump_on_class_repeat:3 } (anh chot Q2).
    lead_self_audit.flag_classes = enum DONG 10 class (5 view-* + 5 gap-*) - vai CAM tu che class
    (v2 M4: enum song trong dau 1 lane => worker khong bao gio thay => tu che => loang thong-ke).
    Honest-caveat giu de bao hub: hub dan "dung chep 3 con-so nay"; SE chon trung 6/15/3 = anh chot
    CO CHU-DICH lam khoi-diem, KHONG phai chep vo-thuc; re-tune sau chu-ky-2.

(5) .claude/governance/.session-counter.json (NEW) - seed UNTICKED trung thuc
    Content-based (idempotent-key = HEAD sha), KHONG blind-increment: hook wal-flush auto-commit
    agent-memory/ MOI TURN => dem mu se vo nghia. Dat o .claude/governance/ CO CHU-DICH = ngoai
    3 path hook (.claude/WAL.md, agent-memory/, workflows/runs/) => khong bi quet vao commit wal:.
    counter=0 + last_ticked_*=null = SU THAT tai S121 (nghi-thuc tick land o W3); seed tick gia
    se lam phep do cadence dau tien noi doi - dung thu H24 sinh ra de bat.

(6) STATUS.md 2 row - CUNG LANDING roster (R3-M2: lech => detector BAO tren chinh thay doi cua H24)
    | Sub-agents | 12 -> 14 |
    | Policies | 216 | ROW MOI - GAP-3 can no de resolve token 'policy'.
    216 GROUND TU MA NGUON, khong chep tu spec: |MenuKeys.All| x |MenuKeys.Actions| = 54 x 4
    (Program.cs:82-89 AddPolicy loop; MenuKeys.cs:167 Actions=[Read,Create,Update,Delete]).
    => Policies la DERIVED = Menu keys x 4 => 2 row BUOC doi cung nhau (ghi thang vao row).

VERIFY LEAD TU DO (khong tin return):
  14/14/14 (roster / VALID_ROLES / agents-*.md tru README) = acceptance ② EOL.
  Glob tho = 15 => xac nhan R2-C1: acceptance CU "ls agents/*.md = 14" se FAIL tren impl DUNG.
  Hop-dong cheo W1<->W2 khep CA HAI chieu, do bang chinh script W1:
    detector H24-2: "h24_cadence missing" -> "M = h24_cadence.light_every = 6 (read from config)"
    spawn-model-audit: "expected constant missing" -> "[OK] expected = claude-opus-4-8 = owner-ratified"
    GAP-3 policy: MISSING -> 216, va BAT NGAY permission-matrix/SKILL.md:3 "writes 240 policy but
      canonical=216" (+:81 "writes 48") => TOTAL 49->50 (-1 fail-loud cadence, +2 policy) = so cong khop.
  eol-check tu nhan 2 vai moi: scoped 37->39 (dem bang glob, khong hardcode).
  FAULT-INJECT SAN-1 2 CHIEU (bai hoc W0.3: chi test 1 chieu thi dieu-kien viet nham van PASS):
    (b) 3 task + KHONG args.run -> THROW, agent_count=0, subagent_tokens=0, 14ms (chan TRUOC spawn)
    (a) 2 task + KHONG args.run -> gate IM, workflow launch binh thuong => khong over-fire, nguong
        dung la >=3. Neu viet nham `if (!wave)` thieu nguong thi /fable-real 1-lane se chet oan.

CHUA VERIFY (khai that, KHONG gia vo):
  14/14 spawn-probe SONG = PHAI chay SAU RESTART (registry = anh-chup khoi-phien; S120+S121 da do
  2 lan xac nhan KHONG hot-reload agent MOI). Static-check KHONG du (broadcast ④d).
  => 2 vai moi hom nay moi chi TON TAI tren dia + trong VALID_ROLES, chua chung duoc SPAWN duoc.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 13:04:21 +07:00

218 lines
19 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// hmw.js — HMW P2 (Execute) workflow cho SOLUTION_ERP. CHẠY bởi Workflow runtime (body wrap async →
// top-level await/return hợp lệ); KHÔNG node-runnable trực tiếp (`node hmw.js` sẽ lỗi await).
// Em main lo P0/P1/P3/P4 NGOÀI workflow; script này CHỈ lo P2 fan-out.
// Invoke bằng {scriptPath} (no hot-reload — restart/re-invoke sau khi sửa). Scope = repo SOLUTION_ERP ONLY (S1).
// ⚠️ Script chạy JS-sandbox KHÔNG filesystem → KHÔNG tự tạo folder/ghi file. Scaffold run-folder runs/<run-id>/ (TRACKED) = EM MAIN @P1 (Harness-10, supersedes Harness 2 B3 wave-folder).
export const meta = {
name: 'hmw',
description: 'HMW P2 execute (SOLUTION_ERP) — fan-out roster ĐẦY-ĐỦ (số vai canonical → docs/STATUS.md §Sub-agents; KHÔNG hardcode số ở đây — lesson S108 comment stale làm worker đọc sai runtime) có MEMORY-PACK slice (qua args vì script không đọc file) + return findings + checklistEvidence + memoryDelta (spawn-record 4-field). 2 MODE (Harness 2, 06-07): (A) DEFAULT return-delta-only — fan-out NHỎ (<3 task VÀ ≤5 phút), sub KHÔNG ghi file, git-diff verify. (B) RUN-TRACE mode (args.run, Harness-10) — 🔴 BẮT BUỘC khi run ≥3 task HOẶC ước >5 phút (sàn H22 S111; script no-fs KHÔNG tự ép — enforcement = em-main scaffold @P1 + set args.run) — workflow DÀI, em main scaffold .claude/workflows/runs/<run-id>/ TRACKED FLAT (run.md + sub-<role>-<i>.md + <stage>-synthesis.md phẳng cùng cấp) @P1, sub ghi full-detail vào CHỈ sub-<role>-<i>.md mình (B4/B6), harvest per-turn primary (C4) + H2 gom @session-end = backstop verify-idempotent. taskList thoải mái (queue theo slot, không cap cứng). memoryDelta KHÔNG tự ghi — em main VERIFY + APPEND-only @P3 (no-overwrite-unverified, B3). Model H8 all-inherit (Harness-8 2026-06-16): role-có-frontmatter inherit top-tier lead · role-less inherit · per-task tier:\'fable\'|\'opus\' escape-hatch. Scope = repo SOLUTION_ERP ONLY (S1 — KHÔNG fan-out repo/corpus khác).',
phases: [{ title: 'Execute', detail: 'fan-out memory-pack-injected agents, structured return' }],
}
// ─── args (em main bơm @P2 sau khi P1 chốt) ──────────────────────────────────
// args = {
// memoryPack: { 'investigator-codebase':'<slice>', 'implementer-backend':'...', reviewer:'...', ... }, // SLICE liên quan, KHÔNG full
// spec: '<acceptance-criteria / context chung>',
// checkpointApproved: true, // em main set SAU khi BÁO {số agent·vai·task} @inform (S2)
// taskList: [ { role:<VALID_ROLES|null>, label:'..', prompt:'..', tier:'fable'|'opus'? }, ... ] // tier = per-task model override (H4.5)
// run: { name:'<run-id>', dir:'.claude/workflows/runs/<run-id>' } // 🔴 BẮT BUỘC khi run ≥3 task HOẶC ước >5 phút (sàn H22 S111 — chết giữa run còn vật-liệu đĩa cho /tiep; dưới ngưỡng = tùy chọn) — bật RUN-TRACE mode (B, Harness-10 TRACKED). Folder + run.md em main ĐÃ scaffold @P1 (script no-fs). [legacy alias: args.wave]
// }
const VALID_ROLES = [
'investigator-codebase', 'investigator-api',
'implementer-backend', 'implementer-frontend',
'test-specialist', 'reviewer', 'cicd-monitor', 'frontend-designer',
'database-agent', // +S57 — S56 dùng 3× qua fail-soft WARN; read-advisory DB lens (DB1-DB11)
'office-document', // +S107 — Office document WRITE specialist (OD1-OD10; docx/xlsx/pptx/pdf + form-engine)
'tooling-auditor', 'harvest-curator', // +S110 anh-directed (H21 roster-parity lệnh-B ensemble; supersede
// điểm H6-design "monitor ngoài workflow" — monitor VẪN INFORM-only, lane RETURN-only nên không xung đột)
'lead-view-auditor', 'lead-omission-auditor', // +S121 W2 (H24 lead-self-audit) — 2 monitor MỚI, INFORM-only propose-only.
// 🔴 KHÔNG gộp vào tooling-auditor(H1)/harvest-curator(H2): H24 §2(1) soi LEAD (view-* = lead nhìn sai / gap-* =
// lead bỏ sót), khác trục H1 (tooling-freshness) và H2 (harvest-integrity). Class-flag = enum ĐÓNG ở
// memory-budget.json → lead_self_audit.flag_classes (10 class: 5 view-* + 5 gap-*) — vai KHÔNG tự chế class.
// Từ-vựng "view"/"omission" chọn CÓ CHỦ-ĐÍCH tránh va-chạm: governance-detectors.ps1:15 đã sở-hữu
// "derived-staleness" ⇒ cấm tên *-stale-role; "GAP" quá-tải (session-end build-gap · memory-selfimprove CRITERIA+GAP).
]
// ─── PA-2a — hằng-số kỳ-vọng model tier-2 (H23 model-at-spawn, owner-chốt O1 @S121 W2) ──
// 🔴 ĐÂY LÀ HẰNG-SỐ KHAI-BÁO, KHÔNG PHẢI CƠ-CHẾ NEO. Nó KHÔNG ép model lúc spawn.
// Vì sao chỉ là hằng-số: neo-model-tại-spawn BẤT-KHẢ-THI ở SE — param `model:` chỉ nhận ALIAS
// ('opus'/'fable'/...), không nhận full-id; và SE có 0 agent pin full-id (H8 all-inherit 14/14).
// ⇒ owner chốt PA-2 = PA-2a (hằng-số này, để audit có thứ ĐỐI-CHIẾU) + PA-2b (scripts/spawn-model-audit.ps1
// so resolved-vs-expected). Đọc bởi: scripts/spawn-model-audit.ps1 (thiếu hằng-số ⇒ script FAIL-LOUD).
// 🔸 CHƯA VERIFY (khai thật, KHÔNG giả-vờ đã test — v2:271): thứ-tự ưu-tiên giữa "tham-số tại spawn" và
// "pin ở tệp định-nghĩa" khi CẢ HAI cùng hiện-diện = CHƯA TỪNG ĐƯỢC TEST. Ở SE cả hai đều hiện-diện
// (resolveModel() trả alias vào agent(...,{model:...}) TRONG KHI frontmatter 14/14 = inherit).
// Nếu frontmatter THẮNG ⇒ mọi escape-hatch tier: = NO-OP. Phép thử phân-biệt được CHỈ chạy được ở
// phiên lead=Fable (lead=Opus thì 2 nhánh ra cùng kết-quả ⇒ không phân-biệt) ⇒ DEFER, không bán quá nấc.
// 🔸 CHƯA THỬ (fix#8a): param `model:` có TỪ-CHỐI full-id không — doc nói enum-alias; chưa ai thử thật.
const TIER2_EXPECTED_FULL_ID = 'claude-opus-4-8'
// ─── H8 all-inherit top-tier (Harness-8 adopt 2026-06-16 — thay thế two-tier H4.5) ──
// MỌI sub-agent có memory → frontmatter `model: inherit` = ăn top-tier lead (runtime PER-SESSION
// outage-driven — BƯỚC 0.6 probe mỗi phiên; flip-chain canonical → docs/STATUS.md §Sub-agents.
// KHÔNG hardcode model hiện-hành ở comment — lesson S108: comment stale làm 2/4 worker đọc sai runtime).
// KHÔNG còn demote-pin `claude-opus-4-8` (H4.5 đã gỡ).
// hmw KHÔNG set model cho role-có-frontmatter → return undefined, frontmatter (inherit) tự lo.
// Role-less (H6.2 governed-ultracode): default = inherit lead-model (top-tier) — KHÔNG pin rẻ làm default.
// H8.2 "workflow nhanh nhất" = song song tối đa + xuất nhanh, KHÔNG hạ model làm rẻ.
// Escape-hatch per-task tier:'opus' GIỮ cho sweep/cost (ngoại lệ có chủ đích); tier:'fable' CHỈ hợp lệ
// trong lượt lệnh engine-đắt anh gõ kèm vai (/fable-real|/fable-clone <vai> <đề-bài> — H21 S110);
// lead KHÔNG tự ý elevate theo phán-đoán riêng (MTv3 §1.3: đường DUY NHẤT nâng 1 vai lên hạng-nhất = lệnh owner).
function resolveModel(role, rawRole, tier, i) {
if (tier === 'fable' || tier === 'opus') return tier
if (tier) log(`⚠️ hmw: tier "${tier}" lạ (task #${i}) → bỏ qua, dùng mặc định H8 (inherit top-tier)`)
// Role-less (null) → inherit lead; invalid non-empty role đã THROW up-front (STOP-HARD, DIRECTED REC-3 2026-07-13)
// → nhánh này giờ CHỈ đạt bởi role-less thật (rawRole null/''); giữ làm defensive no-op.
if (!role && rawRole) return undefined
if (!role) { log(`hmw: task #${i} role-less → inherit lead-model (H6.2 governed-ultracode; per-task tier:'opus' = escape-hatch sweep/cost)`); return undefined }
return undefined // role có frontmatter: tất cả `inherit` (H8 all-inherit top-tier) — KHÔNG override
}
const SCHEMA = {
type: 'object',
required: ['findings', 'memoryDelta'],
properties: {
findings: { type: 'string', description: 'Kết quả chính. MỌI claim kèm evidence file:line. KHÔNG narrative suông.' },
checklistEvidence: { type: 'string', description: 'Bằng chứng cho acceptance-checklist P1 (số đo / PASS-FAIL / verdict).' },
subMdPath: { type: 'string', description: 'RUN-TRACE mode FLAT: đường-dẫn sub-<role>-<i>.md agent đã ghi (em main/H2 đọc on-demand). DEFAULT-mode: bỏ trống.' },
memoryDelta: {
type: 'object',
description: 'Spawn-record 4-field — RETURN-only để EM MAIN harvest @P3. Agent KHÔNG tự ghi ký ức (KHÔNG file MEMORY.md, KHÔNG store_memory/RAG). Em main VERIFY + APPEND-only (KHÔNG overwrite entry cũ nếu chưa kiểm tra — B3).',
required: ['task', 'verdict', 'learned', 'surprise'],
properties: {
task: { type: 'string', description: 'việc gì (1 dòng)' },
verdict: { type: 'string', description: 'kết luận / PASS-FAIL / root-cause' },
learned: { type: 'string', description: 'pattern / anti-pattern rút ra' },
surprise: { type: 'string', description: 'edge-case / bất ngờ (chống mất discovery)' },
},
},
},
}
// S4b — args có thể tới OBJECT hoặc JSON-STRING (harness đôi khi stringify object args) → normalize defensive
const A = (typeof args === 'string') ? JSON.parse(args) : (args || {})
if (!A || !Array.isArray(A.taskList) || A.taskList.length === 0) {
throw new Error('hmw: cần args.taskList (mảng {role,label,prompt}) — em main bơm @P1. (đã thử JSON.parse nếu args bị stringify.)')
}
// S2 mechanize: checkpoint TRƯỚC P2 = gate CỨNG, không chỉ prompt. Em main BÁO {số agent·vai·task} @inform
// (KHÔNG chờ confirm) → set args.checkpointApproved=true → fan-out. Flag GIỮ = anti-accidental-fire
// (call quên-set-flag → throw, chặn 515K-class accidental).
if (A.checkpointApproved !== true) {
throw new Error('hmw: checkpointApproved chưa set — em-main BÁO {số agent·vai·task} @inform rồi set args.checkpointApproved=true (guard = anti-accidental-fire).')
}
// S4 — số task THOẢI MÁI: harness chạy theo slot, dư tự queue, KHÔNG cap cứng (soft notice only).
if (A.taskList.length > 16) {
log(`hmw: taskList=${A.taskList.length} (>16) → harness queue theo slot (thoải mái, không cap cứng).`)
}
// ─── STOP-HARD role-whitelist (DIRECTED 6c32df89 REC-3, 2026-07-13 · S115) ────
// Vai ∉ VALID_ROLES (typo / rename-drift) = DỪNG HẲN + báo owner — KHÔNG fail-soft
// về default subagent (fail-soft = "đứt âm-thầm" đã gây lỗi thật hub-side; sim 5-ca).
// PHÂN-BIỆT (backward-compat H6.2): role null/omitted/'' = HỢP-LỆ role-less (inherit
// lead) → KHÔNG throw. CHỈ non-empty-string ∉ whitelist mới throw. Validate UP-FRONT
// (trước parallel) — halt KHÔNG phụ-thuộc semantics parallel()/.filter(Boolean) (per-lane
// throw có thể bị nuốt thành null = STOP-SILENT, tệ hơn). Mirror checkpointApproved tripwire.
const badRoles = A.taskList
.map((t, i) => ({ i, raw: t && t.role }))
.filter(x => x.raw != null && x.raw !== '' && !VALID_ROLES.includes(x.raw))
if (badRoles.length > 0) {
const detail = badRoles.map(x => `#${x.i}="${x.raw}"`).join(', ')
throw new Error(
`hmw: vai ∉ VALID_ROLES (${detail}) — DỪNG HẲN (STOP-HARD). ` +
`Sửa typo HOẶC báo owner thêm vai vào VALID_ROLES [${VALID_ROLES.join(', ')}]. ` +
`KHÔNG fail-soft về default subagent (DIRECTED 6c32df89 REC-3 2026-07-13).`)
}
const memoryPack = A.memoryPack || {}
const spec = A.spec || ''
// ─── RUN-TRACE mode (Harness-10, supersedes Harness-2 wave-mode B) ────────────
// run = { name, dir }. Folder runs/<run-id>/ (TRACKED) + run.md em main ĐÃ scaffold @P1 (script no-fs). Bật → sub ghi full-detail
// vào CHỈ sub-<role>-<i>.md phẳng mình + return memoryDelta. Containment: tracked-change NGOÀI run-folder + code-disjoint = vi-phạm
// (run-folder TRACKED → HIỆN trong git-diff = audit trực-tiếp; em main git-diff post-P2 + chunk-count RAG). [legacy alias args.wave]
const wave = (A.run && A.run.dir) ? A.run : ((A.wave && A.wave.dir) ? A.wave : null)
if (wave) log(`hmw: RUN-TRACE mode on → dir=${wave.dir} (TRACKED FLAT; sub ghi sub-<role>-<i>.md phẳng isolated; em main scaffold @P1; harvest per-turn primary C4, H2 gom @session-end = backstop).`)
// ─── SÀN-1 GATE — RUN-TRACE BẮT BUỘC khi ≥3 task (H22 wal-defect-fix, adopt S121 W2) ─────
// TRƯỚC S121 sàn này chỉ là CHỮ trong doc ("script no-fs KHÔNG tự ép") ⇒ phụ-thuộc em-main NHỚ set
// args.run. Đo thật @S119: `git grep args.run .claude/commands/` = 0 hit ⇒ 4/4 wrapper KHÔNG cờ
// ⇒ sàn = danh-nghĩa. Nay CƠ-KHÍ-HOÁ (mirror tripwire checkpointApproved).
// Vì sao ≥3: chết giữa run DÀI mà không có run-folder ⇒ /tiep §4 KHÔNG có vật-liệu trên đĩa để cắt-gọt
// ⇒ phải chạy lại TỪ ĐẦU (per-turn returns mất theo phiên; chỉ file trên đĩa sống sót).
// Bằng-chứng: S118 raw-Agent 7-spawn ⇒ 0 run-folder ⇒ mất trace.
// 🔴 THROW chứ KHÔNG fail-soft: fail-soft = "đứt âm-thầm" (cùng lý-lẽ STOP-HARD role-whitelist bên trên).
if (!wave && A.taskList.length >= 3) {
throw new Error(
`hmw: SÀN-1 — ${A.taskList.length} task (≥3) mà THIẾU args.run ⇒ DỪNG HẲN. ` +
`RUN-TRACE là BẮT BUỘC ở ngưỡng này (sàn H22 S111): chết giữa run mà không có runs/<run-id>/ ` +
`thì /tiep §4 không có vật-liệu đĩa để relaunch cắt-gọt, phải chạy lại từ đầu. ` +
`SỬA: em-main scaffold .claude/workflows/runs/<run-id>/ (run.md + taskList snapshot) @P1 rồi truyền ` +
`args.run = { name:'<run-id>', dir:'.claude/workflows/runs/<run-id>' }. ` +
`(Script chạy JS-sandbox KHÔNG filesystem ⇒ KHÔNG tự tạo folder được — vì thế mới gate ở đây.) ` +
`Dưới 3 task = MODE A return-delta-only, args.run tùy chọn.`)
}
// 🔸 KHAI THẬT — gate này CHỈ phủ đường `hmw.js`. Ngưỡng "hoặc ước >5 phút" KHÔNG cơ-khí-hoá được
// (script không đo trước được thời-lượng) ⇒ phần đó vẫn là kỷ-luật người, không phải cổng.
// 🔸 KẼ CÒN-LẠI (F2, KHÔNG gate được từ đây): raw `Workflow` tool và raw `Agent` tool đi THẲNG, không
// qua file này ⇒ mất luôn writeGuard + run-trace. S72 raw-Workflow ⇒ reviewer ghi MEMORY +2850B over-cap;
// S118 raw-Agent 7-spawn ⇒ 0 run-folder; và chính run S119 cũng đi raw Workflow. SE KHÔNG có CLI-launcher
// để whitelist ⇒ 3 nguyên-tắc detector của broadcast là N/A **vì không có launcher**, KHÔNG phải vì "hết kẽ".
phase('Execute')
log(`HMW P2: fan-out ${A.taskList.length} task (${wave ? 'RUN-TRACE' : 'return-delta-only'}, H8 all-inherit top-tier, memory-pack-injected, scope=SOLUTION_ERP repo only)`)
const results = await parallel(A.taskList.map((t, i) => () => {
const raw = t && t.role
// STOP-HARD validate up-front (DIRECTED REC-3 2026-07-13): raw ở đây CHỈ có thể ∈ VALID_ROLES HOẶC null/'' (role-less H6.2).
const role = VALID_ROLES.includes(raw) ? raw : undefined // role-less (null/'') → undefined → inherit lead
const mem = role && memoryPack[role] ? memoryPack[role] : ''
const subMd = wave ? `${wave.dir}/sub-${role || 'task'}-${i}.md` : null // Harness-10 FLAT (h10-refine 2026-06-18): sub-<role>-<i>.md phẳng cùng cấp dưới runs/<run-id>/ — KHÔNG sub-md/ subdir
// Write-guard TOOL-AWARE theo MODE (B6 isolation). SE read-only sub (KHÔNG Write tool): investigator-codebase/api,
// reviewer, cicd-monitor (+ monitor tooling-auditor/harvest-curator). Write sub: implementer-backend/frontend, test-specialist, frontend-designer.
const writeGuard = wave
? [
`## ✍️ RUN-TRACE ghi sub-<role>-<i>.md FLAT (Harness-10 h10-refine, supersedes subfolder sub-md/) — TOOL-AWARE (chống mojibake G-009):`,
`- Full-detail công-việc của mày → ĐÚNG 1 file: \`${subMd}\` (folder đã scaffold sẵn — KHÔNG tạo folder).`,
` • NẾU mày CÓ Write/Edit tool (implementer-backend/frontend, test-specialist, frontend-designer): GHI TRỰC TIẾP via Write/Edit. 🔴 KHÔNG Bash-write MD ($-expansion/mojibake).`,
` • NẾU mày CHỈ có Bash (read-only sub: investigator-codebase/api, reviewer, cicd-monitor — KHÔNG Write tool): 🔴 TUYỆT ĐỐI KHÔNG Bash-write MD → để full-detail trong "findings" + đặt subMdPath="${subMd}"; EM MAIN scribe @P3 (single-writer Write-tool, no-corruption).`,
`- 🔴 ISOLATION (B6→Harness-10, AUDIT): CHỈ ghi \`${subMd}\` (+ code-file-disjoint nếu task giao). TUYỆT ĐỐI KHÔNG ghi/sửa: agent-memory/* (MEMORY.md BẤT KỲ sub) · MD canonical (CLAUDE/README/STATUS/agents) · sub-MD agent khác. Em main git-status/diff audit sau P2 — tracked-file đổi NGOÀI run-folder (runs/<run-id>/) + code-disjoint = vi-phạm (run-folder TRACKED → HIỆN trong diff).`,
`- LUÔN return: findings (FULL) + checklistEvidence + memoryDelta (4-field) + subMdPath="${subMd}". Harvest per-turn primary (C4); H2 gom @session-end = backstop verify-idempotent → agent-memory/${role || 'sub'}.`,
`- 🔴 KHÔNG store_memory/RAG-write · KHÔNG Bash curl/HTTP Qdrant (:6333 = git-diff MÙ, chỉ chunk-count bắt) · KHÔNG ghi file NGOÀI repo/run-folder. RAG single-writer=em main; containment = git-diff(in-repo)+chunk-count(RAG) [G-015].`,
].join('\n')
: [
`## OUTPUT write-guard (DEFAULT return-delta-only):`,
`- ⚠️ KHÔNG ghi ký ức kênh nào: KHÔNG file MEMORY.md, KHÔNG store_memory/RAG-write (Qdrant). CHỈ return memoryDelta → em main VERIFY + APPEND-only @P3 (B3). KHÔNG overwrite file/chunk của sub khác.`,
`- ⚠️ Nếu task có WRITE file: CHỈ file-disjoint được giao (em main git-diff verify sau P2). KHÔNG đụng file ngoài phạm vi. Scope = repo SOLUTION_ERP only (S1).`,
].join('\n')
const prompt = [
mem ? `## MEMORY-PACK (${role}) — SLICE ký ức tích lũy của sub này, ĐỌC trước khi làm (KHÔNG phải full memory):\n${mem}\n` : '',
spec ? `## SPEC / acceptance-criteria (chung):\n${spec}\n` : '',
`## TASK:\n${(t && t.prompt) || '(thiếu prompt — lỗi taskList @P0)'}`,
[
'## OUTPUT (structured bắt buộc):',
'- findings: kết quả + evidence file:line (KHÔNG narrative suông).',
'- checklistEvidence: số đo / verdict cho acceptance-checklist.',
'- memoryDelta {task·verdict·learned·surprise}: để EM MAIN harvest @P3.',
wave ? '- subMdPath: đường-dẫn sub-MD mày đã ghi.' : '',
].filter(Boolean).join('\n'),
writeGuard,
].filter(Boolean).join('\n')
return agent(prompt, {
agentType: role || undefined,
schema: SCHEMA,
label: (t && t.label) || `hmw:${role || 'task'}-${i}`,
model: resolveModel(role, raw, t && t.tier, i), // H8 all-inherit (undefined = theo frontmatter = inherit)
})
}))
// trả mảng kết quả (lọc null nếu agent lỗi/null — invalid-role đã STOP-HARD up-front; .filter(Boolean) chỉ lọc null-lane do agent lỗi runtime)
// về em main → P3 VERIFY + harvest + P4 checklist
return results.filter(Boolean)