Files
solution-erp/.claude/workflows/hmw.js
pqhuy1987 4e09413fdb [CLAUDE] Skill: adopt Harness-8 all-inherit (11 sub -> inherit, go two-tier H4) + email-back ai_infra
AI_INFRA broadcast 2026-06-16 (BAT BUOC, PROJECT-FIT 6/6). Flip 7 demoted subs claude-opus-4-8 -> inherit (all 11 inherit; SE has no cheap helper/gopher); agents/README + hmw.js comments codify (resolveModel defers frontmatter). adap-report + email-back (content_sha256 fa7f690d round-trip MATCH). Nac executed-file VERIFIED-pending-restart (frontmatter no hot-reload). Runtime unchanged now (inherit=Opus 4.8 1M since Fable suspended H5); forward-looking + H5.6 restore simpler.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 12:48:37 +07:00

147 lines
11 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 wave-folder = EM MAIN @P1 (Harness 2 B3).
export const meta = {
name: 'hmw',
description: 'HMW P2 execute (SOLUTION_ERP) — fan-out 9-agent roster 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ẹ, sub KHÔNG ghi file, git-diff verify. (B) WAVE-MODE (args.wave) — workflow DÀI, em main scaffold .claude/workflows/wave-<tên>/ @P1, sub ghi full-detail vào CHỈ sub-MD mình (B4/B6), H2 harvest-curator gom wave→agent-memory @session-end (B5). 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). Two-tier model H4.5 (Harness-4 2026-06-10): promote-roles inherit Fable 5 · demoted-roles pin Opus 4.8 (frontmatter) · role-less \'opus\' · per-task tier:\'fable\'|\'opus\' override. 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)
// wave: { name:'<tên-wave>', dir:'.claude/workflows/wave-<tên>' } // OPTIONAL — bật WAVE-MODE (B). Folder + wave.md em main ĐÃ scaffold @P1 (script no-fs).
// }
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)
]
// ─── 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 (hiện Opus 4.8 1M do
// Fable suspended H5; tự lên Fable 5 khi về). 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' = force lead-tier khi hệ-trọng.
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)`)
// Invalid-role (typo ∉ VALID_ROLES, WARN đã log ở caller) → fail-UP inherit Fable 5 — H4.5 "chưa-phân-loại
// → nghiêng quality" (KHÔNG rơi 'opus': task có thể là gate-class gõ nhầm tên role).
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: 'WAVE-MODE: đường-dẫn sub-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).`)
}
const memoryPack = A.memoryPack || {}
const spec = A.spec || ''
// ─── WAVE-MODE (Harness 2 B) ─────────────────────────────────────────────────
// wave = { name, dir }. Folder + wave.md em main ĐÃ scaffold @P1 (script no-fs). Bật → sub ghi full-detail
// vào CHỈ sub-MD mình + return memoryDelta. Isolation B6 (gitignore wave-*/ + em main git-diff post-P2 + chunk-count).
const wave = (A.wave && A.wave.dir) ? A.wave : null
if (wave) log(`hmw: WAVE-MODE on → dir=${wave.dir} (sub ghi sub-MD isolated; em main scaffold @P1; H2 harvest-curator gom @session-end B5).`)
phase('Execute')
log(`HMW P2: fan-out ${A.taskList.length} task (${wave ? 'WAVE-MODE' : '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
const role = VALID_ROLES.includes(raw) ? raw : undefined // S4c whitelist; invalid → default subagent (fail-soft)
if (raw && !role) log(`⚠️ hmw: agentType "${raw}" ∉ VALID_ROLES → default subagent cho task #${i}`)
const mem = role && memoryPack[role] ? memoryPack[role] : ''
const subMd = wave ? `${wave.dir}/sub-${role || 'task'}-${i}.md` : null
// 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
? [
`## ✍️ WAVE-MODE ghi sub-MD (Harness 2 B4/B6) — 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, 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 code-disjoint = vi-phạm.`,
`- LUÔN return: findings (FULL) + checklistEvidence + memoryDelta (4-field) + subMdPath="${subMd}". H2 harvest-curator gom @session-end (B5) → 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/wave-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 vẫn chạy default subagent, KHÔNG skip)
// về em main → P3 VERIFY + harvest + P4 checklist
return results.filter(Boolean)