Trang HTML tự đứng một mình, mở bằng trình duyệt là xem được, không cần mạng.
Chia toàn bộ phần việc của team trong plan.md (R02, R07-T06, R08-T07…T10, R09,
CASAN Check 1) cho 3 người:
- Một mục chung nhóm trưởng làm trước, xong mới chia nhánh: dựng khung 5 thư
mục đích (hiện là 0 file), interface + fake cho Config/Secrets, chốt số phận
api_key, script CASAN Check 1, đưa 3 check vào CI, quyết số phận 24 checker
UI sẽ vỡ khi file bị dời.
- Ba nhánh tính năng ngang nhau, mỗi nhánh ~2.700 dòng: N1 cấu hình và vỏ ứng
dụng (nhóm trưởng giữ, vì chạm app.py / config.py / theme.py / i18n.py),
N2 giám sát, N3 Co4E.
- Bảy quy ước cho N2 và N3, ba trong đó là bắt buộc.
Số dòng code, 156 lời gọi ctx.config, 24 lời gọi audit_log.record và baseline
90 test đều đo trực tiếp trên main ngày 21/08, không lấy từ tài liệu.
Footer ghi rõ phần nào là đề xuất, phần nào lấy từ ba tài liệu gốc — mục chung,
cách chia nhánh, quy ước và nghiệm thu là đề xuất.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
613 lines
29 KiB
HTML
613 lines
29 KiB
HTML
<!doctype html>
|
||
<html lang="vi">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>Phân Việc Refactor Team Gamma</title>
|
||
</head>
|
||
<body>
|
||
<style>
|
||
:root {
|
||
--ground: #FAF9FC; --surface: #FFFFFF; --surface-2: #F3F0F8;
|
||
--ink: #191325; --muted: #665E7C; --line: #E4DEEE;
|
||
--accent: #6D3A9E;
|
||
--lead: #6D3A9E; --m1: #14707F; --m2: #A65418;
|
||
--warn: #A81F1A; --ok: #1B6B40;
|
||
--lead-wash: #F1E9F9; --m1-wash: #E2F1F3; --m2-wash: #F8EDE2;
|
||
}
|
||
@media (prefers-color-scheme: dark) {
|
||
:root:not([data-theme="light"]) {
|
||
--ground: #121020; --surface: #1B1830; --surface-2: #241F3D;
|
||
--ink: #EFEBF7; --muted: #A69EBD; --line: #2F2848;
|
||
--accent: #C08CF0;
|
||
--lead: #C08CF0; --m1: #56C6D8; --m2: #E29A56;
|
||
--warn: #F08A84; --ok: #6FD69C;
|
||
--lead-wash: #2A1F42; --m1-wash: #133038; --m2-wash: #38270F;
|
||
}
|
||
}
|
||
:root[data-theme="dark"] {
|
||
--ground: #121020; --surface: #1B1830; --surface-2: #241F3D;
|
||
--ink: #EFEBF7; --muted: #A69EBD; --line: #2F2848;
|
||
--accent: #C08CF0;
|
||
--lead: #C08CF0; --m1: #56C6D8; --m2: #E29A56;
|
||
--warn: #F08A84; --ok: #6FD69C;
|
||
--lead-wash: #2A1F42; --m1-wash: #133038; --m2-wash: #38270F;
|
||
}
|
||
|
||
* { box-sizing: border-box; }
|
||
body {
|
||
margin: 0; background: var(--ground); color: var(--ink);
|
||
font-family: "Segoe UI", -apple-system, system-ui, "Helvetica Neue", sans-serif;
|
||
font-size: 15px; line-height: 1.62; -webkit-font-smoothing: antialiased;
|
||
}
|
||
.wrap { max-width: 1120px; margin: 0 auto; padding: 0 28px 96px; }
|
||
code, .mono, td.day, th.day, .num {
|
||
font-family: Consolas, "Cascadia Mono", "SF Mono", ui-monospace, monospace;
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
|
||
header.top { border-bottom: 2px solid var(--ink); padding: 56px 0 22px; margin-bottom: 34px; }
|
||
.eyebrow { font-size: 12px; letter-spacing: .16em; text-transform: uppercase;
|
||
color: var(--accent); font-weight: 700; margin: 0 0 14px; }
|
||
h1 { font-size: clamp(30px, 4.4vw, 44px); line-height: 1.1; margin: 0 0 16px;
|
||
font-weight: 700; letter-spacing: -.02em; text-wrap: balance; }
|
||
.lede { font-size: 17px; color: var(--muted); margin: 0; max-width: 64ch; }
|
||
.alias { margin: 18px 0 0; padding: 12px 16px; max-width: 74ch;
|
||
background: var(--surface-2); border-left: 3px solid var(--accent);
|
||
border-radius: 3px; font-size: 14px; color: var(--muted); }
|
||
.alias b { color: var(--ink); }
|
||
.facts { display: flex; flex-wrap: wrap; gap: 28px; margin-top: 26px;
|
||
padding-top: 20px; border-top: 1px solid var(--line); }
|
||
.fact .k { font-size: 11px; letter-spacing: .13em; text-transform: uppercase;
|
||
color: var(--muted); display: block; margin-bottom: 3px; }
|
||
.fact .v { font-size: 15px; font-weight: 600; }
|
||
|
||
h2 { font-size: 23px; margin: 52px 0 6px; letter-spacing: -.01em; font-weight: 700; text-wrap: balance; }
|
||
h2 + .sub { color: var(--muted); margin: 0 0 22px; max-width: 70ch; }
|
||
h3 { font-size: 17px; margin: 30px 0 10px; font-weight: 700; }
|
||
|
||
/* gate = việc phải xong trước khi chia nhánh */
|
||
.gatebox { background: var(--surface); border: 1px solid var(--line);
|
||
border-left: 4px solid var(--warn); border-radius: 3px; padding: 4px 26px 22px; }
|
||
.gatebox h2 { margin-top: 22px; }
|
||
|
||
.steps { list-style: none; counter-reset: s; padding: 0; margin: 0; }
|
||
.steps > li { counter-increment: s; position: relative; padding: 14px 0 14px 46px;
|
||
border-bottom: 1px solid var(--line); }
|
||
.steps > li:last-child { border-bottom: none; }
|
||
.steps > li::before {
|
||
content: counter(s); position: absolute; left: 0; top: 14px;
|
||
width: 26px; height: 26px; border-radius: 50%; background: var(--accent);
|
||
color: #fff; font-size: 13px; font-weight: 700; display: flex;
|
||
align-items: center; justify-content: center;
|
||
font-family: Consolas, ui-monospace, monospace;
|
||
}
|
||
.steps b { display: block; margin-bottom: 2px; }
|
||
.steps small { color: var(--muted); font-size: 13.5px; display: block; }
|
||
.est { float: right; font-size: 12px; color: var(--muted); font-weight: 600;
|
||
font-family: Consolas, ui-monospace, monospace; }
|
||
|
||
.cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; }
|
||
@media (max-width: 940px) { .cards { grid-template-columns: 1fr; } }
|
||
.card { background: var(--surface); border: 1px solid var(--line);
|
||
border-top: 3px solid var(--c); border-radius: 3px; padding: 20px;
|
||
display: flex; flex-direction: column; }
|
||
.card.lead { --c: var(--lead); --w: var(--lead-wash); }
|
||
.card.one { --c: var(--m1); --w: var(--m1-wash); }
|
||
.card.two { --c: var(--m2); --w: var(--m2-wash); }
|
||
.card .tag { font-size: 11px; letter-spacing: .13em; text-transform: uppercase;
|
||
font-weight: 700; color: var(--c); margin-bottom: 6px; }
|
||
.card h3 { margin: 0 0 4px; font-size: 18px; }
|
||
.card .who { font-size: 13px; color: var(--muted); margin-bottom: 14px; }
|
||
.card .branch { font-size: 12.5px; background: var(--w); color: var(--c);
|
||
padding: 5px 9px; border-radius: 3px; display: inline-block;
|
||
margin-bottom: 16px; word-break: break-all; font-weight: 600; }
|
||
.card h4 { font-size: 11px; letter-spacing: .12em; text-transform: uppercase;
|
||
color: var(--muted); margin: 16px 0 7px; font-weight: 700; }
|
||
.card ul { margin: 0; padding-left: 17px; font-size: 14px; }
|
||
.card li { margin-bottom: 6px; }
|
||
.tid { font-size: 12px; font-weight: 700; color: var(--c);
|
||
font-family: Consolas, ui-monospace, monospace; }
|
||
.paths { list-style: none; padding: 0; margin: 0; font-size: 12.5px; }
|
||
.paths li { padding: 3px 0; border-bottom: 1px dotted var(--line);
|
||
font-family: Consolas, ui-monospace, monospace; color: var(--muted); word-break: break-all; }
|
||
.paths li:last-child { border-bottom: none; }
|
||
.weight { margin-top: auto; padding-top: 16px; font-size: 12.5px; color: var(--muted); }
|
||
.weight b { color: var(--ink); font-size: 15px; }
|
||
|
||
.scroll { overflow-x: auto; border: 1px solid var(--line); border-radius: 3px; }
|
||
table { border-collapse: collapse; width: 100%; font-size: 13.5px; background: var(--surface); }
|
||
th, td { text-align: left; padding: 11px 14px; border-bottom: 1px solid var(--line); vertical-align: top; }
|
||
thead th { background: var(--surface-2); font-size: 11px; letter-spacing: .1em;
|
||
text-transform: uppercase; color: var(--muted); font-weight: 700; white-space: nowrap; }
|
||
tbody tr:last-child td { border-bottom: none; }
|
||
td.day, th.day { white-space: nowrap; font-weight: 700; font-size: 13px; }
|
||
td.cl { border-left: 3px solid var(--lead); }
|
||
td.c1 { border-left: 3px solid var(--m1); }
|
||
td.c2 { border-left: 3px solid var(--m2); }
|
||
tr.mark td { background: var(--surface-2); font-weight: 600; }
|
||
td small { color: var(--muted); display: block; font-size: 12.5px; }
|
||
.pill { display: inline-block; font-size: 11px; font-weight: 700; padding: 2px 7px;
|
||
border-radius: 2px; letter-spacing: .04em; white-space: nowrap; }
|
||
.pill.cp { background: var(--m1-wash); color: var(--m1); }
|
||
.pill.gate { background: var(--m2-wash); color: var(--m2); }
|
||
.pill.ship { background: var(--lead-wash); color: var(--lead); }
|
||
|
||
.rules { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; }
|
||
@media (max-width: 760px) { .rules { grid-template-columns: 1fr; } }
|
||
.rule { background: var(--surface); border: 1px solid var(--line);
|
||
border-radius: 3px; padding: 18px 20px; border-left: 3px solid var(--c, var(--line)); }
|
||
.rule.hard { --c: var(--warn); }
|
||
.rule.soft { --c: var(--ok); }
|
||
.rule h3 { margin: 0 0 8px; font-size: 15px; }
|
||
.rule p { margin: 0; font-size: 14px; color: var(--muted); }
|
||
.rule code { color: var(--ink); }
|
||
|
||
|
||
/* tóm tắt: đọc 30 giây là nắm được, trước khi vào chi tiết */
|
||
.tldr {
|
||
display: grid; grid-template-columns: 1.35fr 1fr; gap: 0;
|
||
border: 1px solid var(--line); border-radius: 3px; overflow: hidden;
|
||
margin-bottom: 8px; background: var(--surface);
|
||
}
|
||
@media (max-width: 820px) { .tldr { grid-template-columns: 1fr; } }
|
||
.tldr > div { padding: 20px 24px; }
|
||
.tldr .right { background: var(--surface-2); border-left: 1px solid var(--line); }
|
||
@media (max-width: 820px) { .tldr .right { border-left: none; border-top: 1px solid var(--line); } }
|
||
.tldr .cap {
|
||
font-size: 11px; letter-spacing: .14em; text-transform: uppercase;
|
||
color: var(--muted); font-weight: 700; margin: 0 0 12px;
|
||
}
|
||
.flow { list-style: none; padding: 0; margin: 0; font-size: 14px; }
|
||
.flow li { padding: 7px 0; border-bottom: 1px dotted var(--line); display: flex; gap: 10px; }
|
||
.flow li:last-child { border-bottom: none; }
|
||
.flow .b {
|
||
flex: 0 0 auto; font-size: 11.5px; font-weight: 700; padding: 1px 7px; border-radius: 2px;
|
||
background: var(--w2); color: var(--c2); height: fit-content; margin-top: 2px;
|
||
font-family: Consolas, ui-monospace, monospace;
|
||
}
|
||
.flow li.f0 { --c2: var(--warn); --w2: var(--surface-2); }
|
||
.flow li.f1 { --c2: var(--lead); --w2: var(--lead-wash); }
|
||
.flow li.f2 { --c2: var(--m1); --w2: var(--m1-wash); }
|
||
.flow li.f3 { --c2: var(--m2); --w2: var(--m2-wash); }
|
||
.flow .t { flex: 1; }
|
||
.flow .t b { display: block; }
|
||
.flow .t small { color: var(--muted); font-size: 12.5px; }
|
||
.must { margin: 0; padding-left: 18px; font-size: 14px; }
|
||
.must li { margin-bottom: 8px; }
|
||
.must li:last-child { margin-bottom: 0; }
|
||
.must b { color: var(--ink); }
|
||
|
||
footer { margin-top: 64px; padding-top: 20px; border-top: 1px solid var(--line);
|
||
font-size: 13px; color: var(--muted); }
|
||
</style>
|
||
|
||
<div class="wrap">
|
||
|
||
<header class="top">
|
||
<p class="eyebrow">Team Gamma · Automation, Workflows & Governance</p>
|
||
<h1>Một mục chung, rồi ba nhánh tính năng</h1>
|
||
<p class="lede">
|
||
Toàn bộ phần việc refactor 10 ngày của Team Gamma. Nhóm trưởng làm thêm một mục chung —
|
||
khung kiến trúc, hợp đồng dữ liệu, cổng kiểm duyệt — nằm ngoài ba nhánh; xong mục đó thì
|
||
ba người vào ba nhánh tính năng ngang nhau, không ai phải sửa chung file với ai.
|
||
</p>
|
||
<p class="alias">
|
||
Ba tài liệu refactor gọi team này là <b>“Team Nam”</b> (theo tên lead). Cùng một team, cùng
|
||
phạm vi R02 · R08 · R09 · R07-T06. Tên nhánh giữ tiền tố <code>nam/</code> đã thống nhất với
|
||
Team Duy và Team Hoa — đổi sang <code>gamma/</code> sẽ lệch quy ước chung.
|
||
</p>
|
||
<div class="facts">
|
||
<div class="fact"><span class="k">Thời hạn</span><span class="v mono">21/08 → 31/08</span></div>
|
||
<div class="fact"><span class="k">Người</span><span class="v mono">3</span></div>
|
||
<div class="fact"><span class="k">Nhánh</span><span class="v mono">1 chung + 3 tính năng</span></div>
|
||
<div class="fact"><span class="k">Code phải bóc</span><span class="v mono">~6.500 dòng</span></div>
|
||
<div class="fact"><span class="k">Cổng phải qua</span><span class="v mono">CASAN Check 1</span></div>
|
||
</div>
|
||
</header>
|
||
|
||
|
||
<section class="tldr">
|
||
<div>
|
||
<p class="cap">Tóm tắt · thứ tự làm</p>
|
||
<ul class="flow">
|
||
<li class="f0">
|
||
<span class="b">CHUNG</span>
|
||
<span class="t"><b>Nhóm trưởng làm trước, nửa ngày</b>
|
||
<small>Dựng khung 5 thư mục (đang là 0 file) · interface + fake cho Config/Secrets ·
|
||
chốt <code>api_key</code> và báo Team Duy · script CASAN Check 1 · đưa 3 check vào CI ·
|
||
quyết số phận 24 checker UI. Merge xong mới chia nhánh.</small></span>
|
||
</li>
|
||
<li class="f1">
|
||
<span class="b">N1</span>
|
||
<span class="t"><b>Cấu hình · Bí mật · Vỏ ứng dụng — nhóm trưởng</b>
|
||
<small>R02 (6 task) · settings 4 widget · bootstrap + MainWindow · policy doc.
|
||
Giữ luôn <code>app.py</code>, <code>config.py</code>, <code>theme.py</code>,
|
||
<code>i18n.py</code>. ~2.700 dòng.</small></span>
|
||
</li>
|
||
<li class="f2">
|
||
<span class="b">N2</span>
|
||
<span class="t"><b>Giám sát — thành viên 1</b>
|
||
<small>7 tab Monitoring · CanonicalAuditLogger · MonitoringQueryService ·
|
||
2 vòng lặp import · ma trận Sandbox. ~2.650 dòng.</small></span>
|
||
</li>
|
||
<li class="f3">
|
||
<span class="b">N3</span>
|
||
<span class="t"><b>Co4E Studio — thành viên 2</b>
|
||
<small>Co4EWorkflowService · tách <code>co4e_tab.py</code> + <code>co4e_canvas.py</code>
|
||
thành 5 phần. ~2.880 dòng, file to nhất team.</small></span>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
<div class="right">
|
||
<p class="cap">Ba điều bắt buộc</p>
|
||
<ol class="must">
|
||
<li><b>Không chạm file dùng chung.</b> Cần thêm chuỗi hay màu thì nhắn nhóm trưởng, đừng tự sửa.</li>
|
||
<li><b>Nộp factory, không tự lắp vào <code>app.py</code>.</b> N1 lắp trong <code>bootstrap.py</code> ngày 28/08.</li>
|
||
<li><b>Bị chặn thì dùng fake, báo ngay trong ngày.</b> Không ngồi đợi ai.</li>
|
||
</ol>
|
||
<p class="cap" style="margin-top:20px">Nghiệm thu</p>
|
||
<p style="margin:0;font-size:14px;color:var(--muted)">
|
||
<code>git diff --name-only</code> giữa ba nhánh — giao của ba tập phải <b style="color:var(--ink)">rỗng</b>.
|
||
Còn giao nhau là quy ước 1 đang bị vi phạm.
|
||
</p>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="gatebox">
|
||
<h2>Mục chung — nhóm trưởng làm, xong mới chia nhánh</h2>
|
||
<p class="sub">
|
||
Sáu việc dưới đây không thuộc nhánh tính năng nào — chúng là thứ cả ba người cùng đụng
|
||
vào. Nhóm trưởng làm một lần trên nhánh <code>nam/workflow-governance-base</code>, merge
|
||
thẳng, rồi ba người mới tách nhánh riêng. Ước tính nửa ngày.
|
||
</p>
|
||
<ol class="steps">
|
||
<li>
|
||
<span class="est">~30 phút</span>
|
||
<b>Dựng khung thư mục</b>
|
||
<small>
|
||
<code>domain/</code> <code>application/</code> <code>infrastructure/</code>
|
||
<code>presentation/</code> <code>platform/</code> <code>tests/fakes/</code> —
|
||
hiện tại <b>chưa tồn tại, 0 file</b>. Mọi task của cả ba người đều ghi vào đây; để ba
|
||
người tự tạo là đụng nhau ở <code>__init__.py</code> ngay ngày đầu.
|
||
</small>
|
||
</li>
|
||
<li>
|
||
<span class="est">~45 phút</span>
|
||
<b>Viết interface + fake cho Config và Secrets</b>
|
||
<small>
|
||
<code>SecretStore</code>, <code>ConfigRepository</code>, kèm
|
||
<code>FakeSecretStore</code> và <code>FakeConfigRepository</code>. Chỉ chữ ký, chưa cần
|
||
thân hàm. Đây là thứ gỡ chốt cho cả hai người kia — <b>156 lời gọi
|
||
<code>ctx.config.*</code> trong 29 file</b> đang chờ nó.
|
||
</small>
|
||
</li>
|
||
<li>
|
||
<span class="est">~20 phút</span>
|
||
<b>Chốt số phận <code>api_key</code> và báo Team Duy</b>
|
||
<small>
|
||
<code>provider_conf()</code> còn trả <code>api_key</code> bên trong, hay tách hẳn sang
|
||
<code>SecretStore</code>? Có 5 nơi đọc trực tiếp, <b>3 trong số đó nằm trong
|
||
<code>providers/</code> của Team Duy</b>. Quyết một mình rồi im lặng là làm vỡ code
|
||
team bạn.
|
||
</small>
|
||
</li>
|
||
<li>
|
||
<span class="est">~30 phút</span>
|
||
<b>Viết <code>scripts/audit_security.py</code> (CASAN Check 1)</b>
|
||
<small>
|
||
Gamma chủ trì check này ngày 30/08. Viết ngay hôm nay thì lead tự kiểm được trong suốt
|
||
quá trình chuyển API key, thay vì tới ngày cổng mới chạy lần đầu và phát hiện vấn đề.
|
||
</small>
|
||
</li>
|
||
<li>
|
||
<span class="est">~20 phút</span>
|
||
<b>Thêm 3 check CASAN vào CI</b>
|
||
<small>
|
||
CI hiện chỉ chạy <code>pytest tests -q</code>. Ba check (secret · ≤400 dòng · import
|
||
guard) không nằm trong CI, nên tới 30/08 mới biết ai vi phạm. Đưa vào CI thì mỗi PR tự
|
||
báo.
|
||
</small>
|
||
</li>
|
||
<li>
|
||
<span class="est">~30 phút</span>
|
||
<b>Quyết số phận 24 checker UI, rồi thông báo</b>
|
||
<small>
|
||
Chúng bám vào <code>cowork_local.config</code> (34 chỗ) và <code>cowork_local.app</code>
|
||
(16 chỗ) — <b>sẽ chết ngay khi lead đụng <code>config.py</code></b>. Đây là lưới an toàn
|
||
duy nhất cho phần UI vừa làm xong. Xem mục quy ước bên dưới.
|
||
</small>
|
||
</li>
|
||
</ol>
|
||
</section>
|
||
|
||
<h2>Ba nhánh tính năng</h2>
|
||
<p class="sub">
|
||
Ba nhánh ngang nhau, mỗi nhánh khoảng 2.700 dòng phải bóc tách. Nhóm trưởng nhận nhánh N1
|
||
vì đó là nhánh chạm tới file dùng chung nhiều nhất. Cột “sở hữu” là danh sách file
|
||
<em>chỉ</em> người đó được sửa.
|
||
</p>
|
||
|
||
<div class="cards">
|
||
|
||
<div class="card lead">
|
||
<div class="tag">Nhánh N1 · Nhóm trưởng</div>
|
||
<h3>Cấu hình, Bí mật & Vỏ ứng dụng</h3>
|
||
<p class="who">Nhánh chạm nhiều file dùng chung nhất — để nhóm trưởng giữ</p>
|
||
<div class="branch">nam/workflow-governance-config</div>
|
||
|
||
<h4>Việc</h4>
|
||
<ul>
|
||
<li><span class="tid">R02-T01…T06</span> AtomicJsonFile · ConfigRepository · Typed Settings Facade · SecretStore + Keyring · chuyển API key · schema versioning</li>
|
||
<li><span class="tid">R08-T07</span> tách <code>settings_dialog.py</code> → 4 section widget</li>
|
||
<li><span class="tid">R08-T10</span> <code>bootstrap.py</code> + tách <code>MainWindow</code> → shell · tray · lifecycle <em>(cuối sprint, lắp factory của hai người kia)</em></li>
|
||
<li><span class="tid">R09-T01</span> tài liệu Security Policy Model</li>
|
||
<li>Chủ trì <b>CASAN Check 1</b> · giữ CI · duyệt PR của hai người</li>
|
||
</ul>
|
||
|
||
<h4>Sở hữu độc quyền</h4>
|
||
<ul class="paths">
|
||
<li>config.py</li>
|
||
<li>app.py → presentation/shell/</li>
|
||
<li>bootstrap.py</li>
|
||
<li>theme.py · i18n.py</li>
|
||
<li>infrastructure/config/ · secrets/ · persistence/</li>
|
||
<li>ui/settings_dialog.py → presentation/settings/</li>
|
||
<li>scripts/ · .gitea/workflows/</li>
|
||
</ul>
|
||
|
||
<p class="weight"><b>~2.700 dòng</b> · 727 settings + 1.352 app + 616 config<br>+ mục chung ở trên</p>
|
||
</div>
|
||
|
||
<div class="card one">
|
||
<div class="tag">Nhánh N2 · Thành viên 1</div>
|
||
<h3>Giám sát & Quan trắc</h3>
|
||
<p class="who">Hợp với người chịu được việc lặp, tách 7 tab có kỷ luật</p>
|
||
<div class="branch">nam/workflow-governance-monitoring</div>
|
||
|
||
<h4>Việc</h4>
|
||
<ul>
|
||
<li><span class="tid">R08-T08</span> tách <code>monitoring_tab.py</code> → 7 tab độc lập</li>
|
||
<li><span class="tid">R09-T04</span> <code>CanonicalAuditLogger</code></li>
|
||
<li><span class="tid">R09-T05</span> <code>MonitoringQueryService</code> read-only, phân trang</li>
|
||
<li><span class="tid">R09-T02</span> gỡ vòng lặp <code>model_pricing</code> ↔ <code>usage_tracker</code></li>
|
||
<li><span class="tid">R09-T03</span> gỡ vòng lặp <code>agent_security</code> ↔ <code>alert</code></li>
|
||
<li><span class="tid">R09-T06</span> ma trận Sandbox theo hệ điều hành</li>
|
||
</ul>
|
||
|
||
<h4>Sở hữu độc quyền</h4>
|
||
<ul class="paths">
|
||
<li>ui/monitoring_tab.py → presentation/monitoring/</li>
|
||
<li>application/monitoring/</li>
|
||
<li>infrastructure/telemetry/ · sandbox/</li>
|
||
<li>core/audit_log.py</li>
|
||
<li>core/model_pricing.py · usage_tracker.py</li>
|
||
<li>core/agent_security*.py</li>
|
||
</ul>
|
||
|
||
<p class="weight"><b>~2.650 dòng</b> · 1.545 monitoring + ~1.100 core</p>
|
||
</div>
|
||
|
||
<div class="card two">
|
||
<div class="tag">Nhánh N3 · Thành viên 2</div>
|
||
<h3>Co4E Studio</h3>
|
||
<p class="who">Hợp với người nắm canvas và luồng chạy workflow</p>
|
||
<div class="branch">nam/workflow-governance-co4e</div>
|
||
|
||
<h4>Việc</h4>
|
||
<ul>
|
||
<li><span class="tid">R07-T06</span> <code>Co4EWorkflowService</code> thuần Python</li>
|
||
<li><span class="tid">R08-T09</span> tách <code>co4e_tab.py</code> + <code>co4e_canvas.py</code> → canvas · node property · run control · chat view · agent list</li>
|
||
<li>Gọi tool qua <code>ToolPolicyGateway</code> của Team Hoa — dùng fake, không chờ</li>
|
||
</ul>
|
||
|
||
<h4>Sở hữu độc quyền</h4>
|
||
<ul class="paths">
|
||
<li>ui/co4e_tab.py → presentation/co4e/</li>
|
||
<li>ui/co4e_canvas.py</li>
|
||
<li>ui/co4e_config_panel.py</li>
|
||
<li>application/workflows/</li>
|
||
<li>domain/workflows/</li>
|
||
<li>core/co4e_run_manager.py</li>
|
||
</ul>
|
||
|
||
<p class="weight"><b>~2.880 dòng</b> · file to nhất của cả team</p>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<h2>Quy ước cho hai nhánh N2 và N3</h2>
|
||
<p class="sub">
|
||
Bảy điều dưới đây là luật của team, nhóm trưởng chốt và duyệt PR theo đó. Ba điều đầu là
|
||
bắt buộc — vi phạm thì PR bị trả về.
|
||
</p>
|
||
|
||
<div class="rules">
|
||
|
||
<div class="rule hard">
|
||
<h3>1 · Không chạm file dùng chung</h3>
|
||
<p>
|
||
<code>app.py</code>, <code>theme.py</code>, <code>i18n.py</code>, <code>config.py</code>,
|
||
<code>bootstrap.py</code> thuộc nhánh N1. Cần thêm chuỗi hay token màu thì
|
||
<b>nhắn, đừng sửa</b> — lead thêm trong ngày. Đây là ba file duy nhất có thể gây conflict
|
||
thật, và luật này xoá hẳn khả năng đó.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="rule hard">
|
||
<h3>2 · Nộp factory, không tự lắp vào app</h3>
|
||
<p>
|
||
Mỗi nhánh expose một hàm dựng widget với chữ ký chốt từ ngày đầu, ví dụ
|
||
<code>build_monitoring_tab(ctx, query_service) -> QWidget</code>. Nhánh N1 gọi nó
|
||
trong <code>bootstrap.py</code> ngày 28/08. Không ai tự sửa chỗ khởi tạo trong
|
||
<code>app.py</code>.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="rule hard">
|
||
<h3>3 · Bị chặn thì dùng fake, không ngồi đợi</h3>
|
||
<p>
|
||
Chưa có <code>ConfigRepository</code> bản thật thì dùng <code>FakeConfigRepository</code>.
|
||
Chưa có <code>ToolPolicyGateway</code> của Team Hoa thì fake. <b>Báo ngay trong ngày</b>
|
||
nếu thiếu fake nào — đó là việc của nhóm trưởng, không phải lý do dừng tay.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="rule soft">
|
||
<h3>4 · PR nhỏ, mỗi ngày một lần</h3>
|
||
<p>
|
||
Một PR cho một sub-widget hoặc một service, không dồn 7 tab vào một PR cuối tuần. Nhóm
|
||
trưởng duyệt trong ngày. PR càng to thì rủi ro càng dồn về ngày 28/08.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="rule soft">
|
||
<h3>5 · Mỗi PR kèm test, và không làm đỏ 90 test cũ</h3>
|
||
<p>
|
||
Baseline hiện tại: <b>90 test xanh trong 4,3 giây</b>. Chạy <code>pytest tests -q</code>
|
||
trước khi mở PR. Đây là lưới an toàn cho phần logic — giữ nó xanh suốt 10 ngày.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="rule soft">
|
||
<h3>6 · File mới ≤ 400 dòng, không import PySide6 vào lõi</h3>
|
||
<p>
|
||
Hai điều kiện của CASAN Check 2 và 3. Tự kiểm trước khi mở PR — CI sẽ báo, nhưng biết
|
||
sớm thì đỡ phải tách lại lần hai.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="rule soft">
|
||
<h3>7 · Checker UI thuộc phạm vi ai, người đó cập nhật</h3>
|
||
<p>
|
||
24 checker sẽ vỡ khi file bị dời. Ai dời file thì sửa checker tương ứng ngay trong PR đó
|
||
— tốn thêm khoảng 15% thời gian, đổi lại giữ được lưới an toàn cho phần UI vừa làm xong.
|
||
<b>Nhóm trưởng quyết định phương án này và chịu trách nhiệm nếu đổi ý.</b>
|
||
</p>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<h2>Lịch từng ngày</h2>
|
||
<p class="sub">Ba hàng chạy độc lập. Hàng tô nền là lúc cả ba phải gặp nhau.</p>
|
||
|
||
<div class="scroll">
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th class="day">Ngày</th>
|
||
<th>N1 · Cấu hình & Vỏ</th>
|
||
<th>N2 · Giám sát</th>
|
||
<th>N3 · Co4E</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td class="day">21/08<br><small>T6</small></td>
|
||
<td class="cl"><b>Mục chung</b> · dựng khung · interface + fake · chốt api_key · CASAN script<small>Merge trước khi hai người kia bắt đầu</small></td>
|
||
<td class="c1">Chốt schema log 9 trường<small>Giữ nguyên định dạng cũ để 24 chỗ gọi không phải sửa</small></td>
|
||
<td class="c2">Chốt chữ ký <code>Co4EWorkflowService</code><small>Nộp cho lead để lắp bootstrap sau</small></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="day">22–23/08<br><small>T7–CN</small></td>
|
||
<td class="cl">AtomicJsonFile · ConfigRepository · Typed Settings Facade</td>
|
||
<td class="c1">CanonicalAuditLogger · gỡ vòng lặp pricing ↔ usage</td>
|
||
<td class="c2">Co4EWorkflowService — CRUD & validate, test không cần Qt</td>
|
||
</tr>
|
||
<tr class="mark">
|
||
<td class="day">23/08<br><small>17:00</small></td>
|
||
<td colspan="3"><span class="pill cp">Checkpoint 1</span> 100% DTO và fake xong · <code>pytest</code> xanh · không ai bị chặn</td>
|
||
</tr>
|
||
<tr>
|
||
<td class="day">24/08<br><small>T2</small></td>
|
||
<td class="cl">Tách settings: provider + connector widget</td>
|
||
<td class="c1">3 tab đầu: overview · sandbox · security events</td>
|
||
<td class="c2">node_property_panel · agent_list_panel</td>
|
||
</tr>
|
||
<tr>
|
||
<td class="day">25/08<br><small>T3</small></td>
|
||
<td class="cl">Tách settings: routing + general widget</td>
|
||
<td class="c1">4 tab còn lại: MCP · action logs · agent status · security settings</td>
|
||
<td class="c2">co4e_canvas_widget — thao tác node</td>
|
||
</tr>
|
||
<tr>
|
||
<td class="day">26/08<br><small>T4</small></td>
|
||
<td class="cl">Chuyển API key sang SecretStore<small>Báo Team Duy trước khi đụng providers/</small></td>
|
||
<td class="c1">Lắp shell MonitoringTab · query service bản thật</td>
|
||
<td class="c2">Run control · chat view</td>
|
||
</tr>
|
||
<tr>
|
||
<td class="day">27/08<br><small>T5</small></td>
|
||
<td class="cl">Schema versioning · recovery policy</td>
|
||
<td class="c1">Ma trận Sandbox · gỡ vòng lặp agent_security</td>
|
||
<td class="c2">Lắp container Co4ETab · thay fake bằng service thật</td>
|
||
</tr>
|
||
<tr>
|
||
<td class="day">28/08<br><small>T6</small></td>
|
||
<td class="cl"><b>bootstrap.py + tách MainWindow</b><small>Nhận factory của N2 và N3 để lắp</small></td>
|
||
<td class="c1">Nộp factory · dọn file >400 dòng · cập nhật checker</td>
|
||
<td class="c2">Nộp factory · dọn file >400 dòng · cập nhật checker</td>
|
||
</tr>
|
||
<tr class="mark">
|
||
<td class="day">28/08<br><small>17:00</small></td>
|
||
<td colspan="3"><span class="pill cp">Checkpoint 2</span> Tách xong 100% god file · 0 circular import</td>
|
||
</tr>
|
||
<tr>
|
||
<td class="day">29/08<br><small>T7</small></td>
|
||
<td class="cl">Tài liệu Security Policy · integration test Settings</td>
|
||
<td class="c1">Integration test Monitoring</td>
|
||
<td class="c2">Integration test luồng Co4E đầu-cuối</td>
|
||
</tr>
|
||
<tr class="mark">
|
||
<td class="day">30/08<br><small>CN 17:00</small></td>
|
||
<td colspan="3"><span class="pill gate">CASAN Gate</span> <b>Nhóm trưởng chủ trì Check 1</b> — quét toàn bộ config/JSON, phải ra 0 secret plaintext. N2 và N3 sửa ngay phần của mình nếu script bắt được.</td>
|
||
</tr>
|
||
<tr class="mark">
|
||
<td class="day">31/08<br><small>T2 15:00</small></td>
|
||
<td colspan="3"><span class="pill ship">Bàn giao</span> Fix tồn đọng · cập nhật tài liệu kiến trúc · merge PR cuối · smoke test 5 luồng chính</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<h2>Nghiệm thu: làm sao biết đã thật sự song song</h2>
|
||
<p class="sub">Không phải “đã họp xong” mà là chạy được. Ba câu hỏi, trả lời bằng lệnh.</p>
|
||
|
||
<div class="scroll">
|
||
<table>
|
||
<thead>
|
||
<tr><th>Câu hỏi</th><th>Cách trả lời</th><th>Khi nào</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>N2 có chạy được khi chưa có config bản thật?</td>
|
||
<td>Dựng một tab Monitoring, chạy test của nó, <b>không import <code>cowork_local.config</code></b> dòng nào — chỉ dùng <code>FakeConfigRepository</code></td>
|
||
<td class="mono">21/08</td>
|
||
</tr>
|
||
<tr>
|
||
<td>N3 có chạy được khi Team Hoa chưa xong gateway?</td>
|
||
<td>Test <code>Co4EWorkflowService</code> xanh với <code>FakeToolPolicyGateway</code></td>
|
||
<td class="mono">23/08</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Ba nhánh có đụng file nhau không?</td>
|
||
<td><code>git diff --name-only</code> giữa ba nhánh — giao của ba tập phải <b>rỗng</b></td>
|
||
<td class="mono">mỗi ngày</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<footer>
|
||
Nguồn: <code>docs/refactor/plan.md</code>, <code>Refactoring_Checklist.md</code>,
|
||
<code>Feature_Architecture_Proposal.md</code>. Số dòng code, số lời gọi và baseline test đo
|
||
trực tiếp trên nhánh <code>main</code> ngày 21/08.
|
||
Mục chung, cách chia ba nhánh, bảy quy ước và mục nghiệm thu là đề xuất — không có trong
|
||
tài liệu gốc.
|
||
</footer>
|
||
|
||
</div>
|
||
|
||
</body>
|
||
</html>
|