All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 4m43s
- H5 model-availability-fallback: SE da de-facto fallback Fable->Opus 4.8 1M (Fable down 06-12); book caveat agents/README + H5.1 check session-start BUOC 0.6; KHONG RCA/memory-permanent (external outage). adap-report 2026-06-13. - H6 governed-ultracode: H6.1 auto-HMW mode-ON (ultra-on + session-start T4) + H6.2 hmw.js role-less default 'opus'->inherit lead; H6.7 role/memory-fidelity da san tu S49 (document). adap-report 2026-06-15. - /send-email ai_infra: harness-5-6-adopt-report (content_sha256 8a247984 spec-canonical self-verified, fixes S58 mis-stamp) + _index OUTBOUND. - Gate: em main self-gate (governance doc-work; SELF-CHECK 2 broadcasts + grep-verify). CI runs (hmw.js not paths-ignore) - passes, no app-code change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
147 lines
11 KiB
JavaScript
147 lines
11 KiB
JavaScript
// 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)
|
||
]
|
||
|
||
// ─── H4.5 two-tier model (Harness-4 adopt 2026-06-10) ───────────────────────
|
||
// Promote-list (gate/verdict-class — frontmatter `model: inherit` = ăn Fable 5 1M từ lead):
|
||
// investigator-codebase (H4.3b) · reviewer (H4.3a) · database-agent (H4.3b).
|
||
// [harvest-curator (H4.3c) cũng promote nhưng là monitor — không fan-out qua hmw.]
|
||
// Demoted roles còn lại: frontmatter ĐÃ pin `claude-opus-4-8` → hmw KHÔNG set model (frontmatter tự lo).
|
||
// Role-less (H6.2 governed-ultracode adopt S63): default = inherit lead-model (top-tier) — KHÔNG pin rẻ làm default.
|
||
// Sweep/cost → per-task tier:'opus' escape-hatch (KHÔNG còn default 'opus'). Per-task 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 tier-map mặc định H4.5`)
|
||
// 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: promote=inherit Fable 5 · demoted=pin Opus 4.8 — 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'}, two-tier H4.5 promote-inherit/demoted-opus, 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), // H4.5 two-tier (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)
|