Final close session 5 — bao gồm: ==== Tests Phase 3 mini (NEW) ==== tests/SolutionErp.Infrastructure.Tests/Application/PeWorkflowAdminTests.cs - 6 test CreatePeWorkflowDefinitionCommandHandler: - First version → IsActive=true, Version=1, ActivatedAt set - Second version same Code → auto-increment v2 + deactivate v1 (atomic) - Different EvaluationType (A vs B) → independent active state - Persists steps ordered by Order field - Persists approvers per step - Third version → v1 + v2 deactivate, v3 active Total tests: 71 → 77 pass / ~2s (54 Domain + 23 Infra). Skip Phase 3 full (UpsertOpinion + Budget link validation) — cần Identity UserManager DI helper, defer session sau. ==== 3 gotcha CI mới (#39 #40 #41) ==== - #39 act_runner github.com TCP timeout 21s → manual checkout fix (run #108/#109 fail, #110 pass) - #40 npm junction cache `tsc not found` after Move-Item — rolled back, hypothesis nested junctions trong node_modules disrupt .bin/ paths. TODO debug session sau với robocopy hoặc act_runner cache.host - #41 Gitea Actions paths-ignore behavior — workflow file change vẫn trigger (correct), commit MD-only skip 100% (verify512880c→ no run #113) + Checklist debug bug mới items 18-20 referencing 3 gotcha trên. ==== Doc updates (8 file) ==== - STATUS.md: header Phase 8 update + 3 row Recently Done CI fixes + cumulative test 71→77 - HANDOFF.md: TL;DR + CI optimize section + Phase status + gotcha count 38→41 - migration-todos.md: Phase 8 §E updated với Phase 3 mini done + CI fixes - rules.md §7 Testing: rewrite full — stack + test pyramid + quy tắc bổ sung mỗi feature + workflow user end-of-task (`dotnet test` local trước push) + CI gate behavior - architecture.md §11: update test pyramid + phased priority + CI optimization sub-section (3 fix manual checkout / path filter / npm cache rollback) + tốc độ deploy table - gotchas.md: + #39 #40 #41 đầy đủ (triệu chứng + nguyên nhân + fix + reference) - ef-core-migration SKILL: Phase 8 update note thêm CI fixes + 77 test - CLAUDE.md root: test count 71→77 + folder structure + CI/CD pipeline 3 fix section - memory project_solution_erp.md: session 5 summary + workflow user mới - session log 2026-04-29-2300-chot-final-ci-tests-gotchas.md (NEW — 9 section detail) ==== Skill audit cron ==== `solution-erp-skill-audit-monthly` next fire 2026-05-01 (2 ngày sau). Cron survives across sessions (setup commitb904a25). Khi fire sẽ: - Cross-check 6 skill với STATUS/gotchas/migration-todos - Auto-refresh stale + đề xuất add/archive cho human approve - Log vào docs/changelog/skill-audit-2026-05.md - ABORT nếu repo dirty ==== Verify ==== - dotnet test SolutionErp.slnx → 77 pass / ~2s (54 Domain + 23 Infra) - git status clean sau commit này - CI: commit này chứa code (test + workflow) → trigger CI test gate Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
24 KiB
Gotchas — SOLUTION_ERP
Bẫy/pitfall đã gặp + cách xử lý. Đọc trước khi debug tương tự để không mất thời gian. Cập nhật liên tục khi gặp bug mới.
Tech stack constraints (.NET 10 + TS 6 + Vite 8)
1. MediatR 14.x không tương thích → pin 12.4.1
Triệu chứng: Unable to resolve service for type 'MediatR.IMediator' — AddMediatR vẫn chạy nhưng không register IMediator.
Fix: Pin MediatR 12.4.1. Khi đó RequestHandlerDelegate<TResponse> là delegate không tham số (v14 có thêm CancellationToken).
2. Swashbuckle 10.x + Microsoft.OpenApi 2.x breaking change
Triệu chứng: Build fail The type or namespace 'Models' does not exist in 'Microsoft.OpenApi'. Swagger 404.
Fix:
- Remove
Microsoft.AspNetCore.OpenApikhỏi Api - Downgrade Swashbuckle về
6.9.0
3. TypeScript 6 erasableSyntaxOnly cấm enum
Fix: Dùng const + as const + typeof[keyof] pattern:
export const SupplierType = { NhaCungCap: 1 } as const
export type SupplierType = typeof SupplierType[keyof typeof SupplierType]
4. TypeScript 6 deprecate baseUrl
Fix: Bỏ baseUrl trong tsconfig, chỉ giữ paths. Paths resolve relative tsconfig location.
5. Node 22 local vs CI pin 20
Bài học NamGroup: CI build fail trên Node latest.
Fix:
package.jsonengines:">=20"(min, không upper).nvmrc=20cho CI- GitHub/Gitea Actions:
actions/setup-node@v4vớinode-version: '20.x'
EF Core 10
6. Expression tree không support switch expression
Triệu chứng: CS8514: An expression tree may not contain a switch expression.
Fix: Tách switch ra ngoài LINQ:
var hasPermission = action switch
{
"Read" => await query.AnyAsync(p => p.CanRead),
"Create" => await query.AnyAsync(p => p.CanCreate),
_ => false,
};
7. Design-time DbContext resolve fail
Triệu chứng: dotnet ef migrations add → Unable to resolve service for type 'DbContextOptions<T>'.
Fix: Tạo IDesignTimeDbContextFactory<ApplicationDbContext> trong Infrastructure.
8. AddDefaultTokenProviders() không có trong AddIdentityCore
Fix: Bỏ call nếu chưa cần password reset. Khi cần, chuyển AddIdentity hoặc add package Microsoft.AspNetCore.Identity.UI.
OpenXml / ClosedXML
9. SpaceProcessingModeValues namespace
Fix: Full path + wrap EnumValue<>:
textElement.Space = new DocumentFormat.OpenXml.EnumValue<
DocumentFormat.OpenXml.SpaceProcessingModeValues>(
DocumentFormat.OpenXml.SpaceProcessingModeValues.Preserve);
10. Placeholder {{field}} bị split runs
Vấn đề: Word hay split text thành nhiều <w:t> — placeholder miss khi regex replace.
Fix: Iterate Paragraph, gom text tất cả <w:t> → replace → gán lại text đầu + clear rest. Đã implement trong DocxRenderer.
11. Word COM SaveAs PowerShell type conversion
Fix: Dùng SaveAs2:
$doc.SaveAs2($outPath, 16) # 16 = wdFormatDocumentDefault
12. Word COM stuck
Fix:
$word.DisplayAlerts = 0- Nếu stuck →
Get-Process WINWORD | Stop-Process -Force - Fallback: LibreOffice headless
soffice --headless --convert-to docx
System.Text.Json
13. Record deserialization fail với Unicode qua CLI
Triệu chứng: POST JSON tiếng Việt từ Windows bash/curl → 400 "JSON value could not be converted".
Fix: Dùng curl --data-binary @file.json (file UTF-8). API handle đúng qua axios/Swagger.
File operations
14. Dropbox sync có thể revert file đang edit
Triệu chứng: Write thành công, build pass, runtime chạy code cũ.
Fix: Sau Write quan trọng → Read lại verify. Nếu revert → Write lại.
15. .gitignore wwwroot rules
wwwroot/uploads/→ ignore (user files)wwwroot/templates/→ commit (source of truth)wwwroot/exports/→ ignore (temp)
Dev workflow
16. Port conflict khi restart dev server
Fix: TaskStop task cũ, hoặc netstat -ano | findstr :8082 → taskkill /F /PID <pid>.
17. EF migration 3-file rule
Mỗi migration tạo: {name}.cs + {name}.Designer.cs + ApplicationDbContextModelSnapshot.cs. Commit đủ 3.
Claude Code harness quirks
18. Edit tool "File not read" sau system-reminder
Triệu chứng: Edit file vừa Read, lỗi "File has not been read yet".
Nguyên nhân: System reminder interrupt reset read-cache.
Fix: Read lại file rồi Write/Edit. Hoặc dùng Write (ghi đè full) thay Edit.
19. Build pass nhưng DI thiếu registration
Triệu chứng: dotnet build → 0 errors nhưng runtime throw Unable to resolve service.
Nguyên nhân: C# compiler chỉ check type, không check DI graph.
Fix: Sau thêm interface mới + impl → luôn add services.AddScoped<IX, X>() trong DependencyInjection.cs. Test API start up là OK check.
Contract workflow
20. Mã HĐ gen 2 lần sau reject → approve lại
Fix: Check if (contract.MaHopDong is null) trước khi gen. Đã implement trong ContractWorkflowService.TransitionAsync.
21. BE adjacency vs FE NEXT_PHASES sync (RESOLVED)
Đã xử lý: FE không còn hardcode NEXT_PHASES nữa. BE expose contract.workflow.nextPhases trong ContractDetailDto từ WorkflowPolicyRegistry.ForContract(contract). FE render dynamic từ đó — single source of truth.
Nếu đổi policy BE: chỉ cần update WorkflowPolicies.Standard hoặc WorkflowPolicies.SkipCcm trong Domain/Contracts/WorkflowPolicy.cs. FE tự reflect.
22. Race condition gen mã HĐ khi 2 user cùng transition tới DangDongDau
Fix: IsolationLevel.Serializable transaction trong ContractCodeGenerator. Không skip.
Permission matrix
23. Permission update không real-time
Triệu chứng: Admin tick permission cho role X → user X vẫn thấy menu cũ.
Nguyên nhân: FE cache menu trong localStorage, không auto refetch.
Fix: User phải logout/login. Phase 3 iteration 2 có thể thêm SignalR push "permission-changed" → FE tự refetch /menus/me.
24. MenuKey typo — không check type
Fix: Luôn dùng MenuKeys.Contracts const (BE) + MenuKeys.Contracts (FE menuKeys.ts). Không hardcode string.
IIS / Windows Server
25. Install-WindowsFeature Web-WebSockets khóa section <webSocket> ở applicationHost
Triệu chứng: Sau khi install WebSocket feature → TẤT CẢ IIS site có <webSocket enabled="true" /> trong web.config trả về HTTP 500.19 với error code 0x80070021 "configuration section cannot be used at this path" — kể cả site khác project không liên quan.
Nguyên nhân: Feature install thêm <webSocket> section vào applicationHost.config với overrideModeDefault="Deny". Site web.config override section đó → fail.
Fix: Unlock section ở server level:
& "$env:SystemRoot\system32\inetsrv\appcmd.exe" unlock config -section:system.webServer/webSocket
Tương tự khi dùng URL Rewrite <serverVariables> cần unlock system.webServer/rewrite/allowedServerVariables.
Cảnh báo co-existence: Trên VPS shared với project khác, enable feature mới qua Install-WindowsFeature có thể làm sập site project khác. Luôn test all site sau mỗi enable.
SignalR / Realtime
26. SignalR WebSocket không cho custom Authorization header
Triệu chứng: new HubConnectionBuilder().withUrl('/hubs/x', { headers: { Authorization: ... } }) — WebSocket transport vẫn 401.
Nguyên nhân: Browser WebSocket API không cho set custom headers cho handshake. Chỉ 2 transport khác (SSE / LongPolling) mới dùng headers.
Fix:
- FE: dùng
accessTokenFactory: () => token— SignalR client tự append?access_token=query cho WebSocket - BE: Wire JWT bearer
OnMessageReceivedđể đọc token từ query khi path matches/hubs/*:
options.Events = new JwtBearerEvents {
OnMessageReceived = ctx => {
var accessToken = ctx.Request.Query["access_token"];
var path = ctx.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/hubs"))
ctx.Token = accessToken;
return Task.CompletedTask;
}
};
27. SignalR SaveChangesInterceptor — capture Added ở SavingChanges, push ở SavedChanges
Lý do: SavedChanges chỉ có entries sau commit thành công. Nhưng ở SavedChanges thì EntityEntry.State đã về Unchanged → không thể filter Added.
Fix: 2-phase pattern:
public override ValueTask<InterceptionResult<int>> SavingChangesAsync(...) {
_pending = eventData.Context.ChangeTracker.Entries<Notification>()
.Where(e => e.State == EntityState.Added)
.Select(e => e.Entity).ToList();
return base.SavingChangesAsync(...);
}
public override async ValueTask<int> SavedChangesAsync(..., int result, ...) {
foreach (var n in _pending) await _realtimeNotifier.PushAsync(n);
_pending.Clear();
return result;
}
DevOps / CI/CD
28. LibreOffice download URL 404 khi pin wrong version
Triệu chứng: Invoke-WebRequest https://download.documentfoundation.org/libreoffice/stable/25.2.7/... → 404.
Nguyên nhân: LibreOffice mirror chỉ giữ vài version mới nhất. 25.2.7, 24.8.7 không có. Chỉ 25.8.6 tồn tại tại thời điểm cài.
Fix: Check mirror URL trước khi pin. Dùng Invoke-WebRequest -Method Head verify trước download thật.
29. PowerShell 5.1 >> $GITHUB_PATH ghi UTF-16 → NUL byte crash Gitea Actions
Triệu chứng: Gitea Actions job fail với "NUL byte in PATH". echo "C:\\dotnet" >> $env:GITHUB_PATH.
Nguyên nhân: PS 5.1 default encoding UTF-16 LE BOM khi redirect >>. Gitea reads PATH as UTF-8 → NUL byte xuất hiện sau mỗi ASCII char.
Fix: Dùng Out-File -Encoding utf8 -Append:
"C:\dotnet" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
Hoặc drop step GITHUB_PATH hoàn toàn nếu NSSM PATH đã có sẵn dotnet+node.
30. PS 5.1 scripts với Vietnamese diacritics → parser error
Triệu chứng: Cannot parse script: Unexpected character khi chạy PS script có text tiếng Việt inline.
Nguyên nhân: PS 5.1 đọc file script với ANSI codepage (Windows-1258 hoặc default 1252), không phải UTF-8.
Fix (1): Save script với BOM UTF-8 (Write-Host có dấu vẫn work):
[System.IO.File]::WriteAllText($path, $content, [System.Text.Encoding]::UTF8)
Fix (2, safer): Rewrite script ASCII-only. Text tiếng Việt nằm trong log messages thay dùng code:
Write-Host "Setup IIS sites done" # thay vi "Hoan tat"
TypeScript / FE
31. Dialog size="xl" TS2322 nếu variant không khai báo
Triệu chứng: <Dialog size="xl"> → Type '"xl"' is not assignable to type '"sm" | "md" | "lg"'.
Fix: Sửa usage về "lg", hoặc add "xl" vào DialogSize type union trong components/ui/Dialog.tsx. Đừng lazy as any.
FE architecture
32. NavLink end prop cho query-param URL variants
Triệu chứng: /contracts?type=1 highlight cả /contracts lẫn /contracts?type=2 cùng lúc.
Nguyên nhân: Default NavLink startsWith match. Query string không parse distinct paths.
Fix: end={path.includes('?')} trong resolvePath để query-variants match exact:
<NavLink to={path} end={path.includes('?')}>
IIS / Windows Server (continued)
33. IPv4/IPv6 port hijack trên VPS shared (G-084)
Triệu chứng: git.baocaogiaoduc.vn trả về homepage Next.js của VietReport
thay vì Gitea UI. Headers lộ x-nextjs-cache: HIT + X-Powered-By: ARR/3.0
(request đã qua IIS ARR proxy rồi mới hit Next.js).
Root cause: Next.js app (NSSM service) được deploy lên VPS shared với
Gitea, ignore env PORT=3001 HOSTNAME=127.0.0.1 và bind 0.0.0.0:3000.
Gitea bind 0.0.0.0:3000 trước đó bị Windows fallback xuống IPv6-only
[::]:3000 (default IPV6_V6ONLY=1). IIS ARR rewrite http://localhost:3000
→ Windows DNS resolve IPv4 first → hit Next.js → leak homepage cho TẤT CẢ
subdomain có ARR proxy về :3000.
Fix (VietReport applied):
- Next.js NSSM env
PORT=3001 HOSTNAME=127.0.0.1— bind loopback IPv4 - Gitea
HTTP_ADDR=127.0.0.1— bind loopback IPv4 explicit - IIS
web.configrewrite URL dùng127.0.0.1thaylocalhost - NSSM
DependOnService=gitea— boot order tránh race
3 rules rút ra — áp dụng mọi service trên VPS shared:
- Reverse-proxy luôn IP literal
127.0.0.1, KHÔNG dùnglocalhost - Backend services bind loopback IPv4 explicit, KHÔNG
0.0.0.0 - Service dependency cho boot order khi nhiều service cùng port family
SOLUTION_ERP relevance:
- API host trong IIS app pool out-of-process (ANCM tự quản lý port Kestrel ephemeral) → risk THẤP
- FE gọi trực tiếp
https://api.solutions.com.vn(không ARR proxy) → risk THẤP - NHƯNG nếu tương lai thêm ARR reverse proxy (fe-admin/user
/apiproxy) hoặc deploy Kestrel standalone qua NSSM → PHẢI apply 3 rules trên - Scripts + skill doc đã update
localhost→127.0.0.1để đồng bộ
FE routing + state (Phase 6)
34. React Router NavLink isActive chỉ match pathname, không query string
Triệu chứng: 2 NavLink cùng pathname (/purchase-evaluations?type=2 vs
/purchase-evaluations?type=2&pendingMe=1) cùng highlight khi URL là một
trong 2. User thấy menu "Danh sách" + "Duyệt" active đồng thời.
Nguyên nhân: React Router v6 NavLink's built-in isActive chỉ so
pathname. end prop chỉ thêm exact-match cho pathname segment, không check
query string.
Fix: Custom isActive với queryMatches helper (URLSearchParams set
equality). Xem Layout.tsx cả 2 FE:
function queryMatches(current: string, target: string): boolean {
const a = new URLSearchParams(current)
const b = new URLSearchParams(target)
const aKeys = [...a.keys()].sort()
const bKeys = [...b.keys()].sort()
if (aKeys.length !== bKeys.length) return false
return aKeys.every((k, i) => bKeys[i] === k && a.get(k) === b.get(k))
}
function MenuLeaf({ node }: { node: MenuNode }) {
const location = useLocation()
const path = resolvePath(node.key)
const [targetPath, targetQuery = ''] = path.split('?')
const isActive = location.pathname === targetPath
&& queryMatches(location.search.replace(/^\?/, ''), targetQuery)
return <NavLink to={path} className={isActive ? 'active' : ''}>...</NavLink>
}
35. Menu tree inheritance phải extend khi thêm root mới
Triệu chứng: Admin/role đã grant PurchaseEvaluations.Read (inherit parent)
nhưng menu children Pe_DuyetNcc_List / Pe_DuyetNcc_Create không hiển thị.
Chỉ thấy root PurchaseEvaluations ở Layout sidebar.
Nguyên nhân: GetMyMenuTreeQuery hardcode 2 inherit root: Contracts và
Workflows. Descendant Ct_/Wf_ auto-inherit CRUD flags từ parent qua switch
statement. Khi thêm root mới (PurchaseEvaluations, PeWorkflows) — không có
trong switch → children mặc định (false,false,false,false) → filter
HasAccess hide children.
Fix: Extend switch + nextInherit propagation:
var contractsFlags = GetFlags(MenuKeys.Contracts);
var workflowsFlags = GetFlags(MenuKeys.Workflows);
var peFlags = GetFlags(MenuKeys.PurchaseEvaluations); // NEW
var peWorkflowsFlags = GetFlags(MenuKeys.PeWorkflows); // NEW
// Trong BuildChildren:
if (inheritFromKey is not null && !resolved.ContainsKey(m.Key))
{
flags = inheritFromKey switch
{
var k when k == MenuKeys.Contracts => contractsFlags,
var k when k == MenuKeys.Workflows => workflowsFlags,
var k when k == MenuKeys.PurchaseEvaluations => peFlags, // NEW
var k when k == MenuKeys.PeWorkflows => peWorkflowsFlags, // NEW
_ => flags,
};
}
var nextInherit = inheritFromKey
?? (m.Key == MenuKeys.Contracts ? MenuKeys.Contracts
: m.Key == MenuKeys.Workflows ? MenuKeys.Workflows
: m.Key == MenuKeys.PurchaseEvaluations ? MenuKeys.PurchaseEvaluations
: m.Key == MenuKeys.PeWorkflows ? MenuKeys.PeWorkflows
: null);
Rule: Khi thêm 1 root mới có child leaves (vd PeWorkflows → PeWf_*) —
PHẢI update cả 3 chỗ: (1) MenuKeys.All, (2) GetMyMenuTreeQuery GetFlags + switch,
(3) nextInherit propagation.
36. Vite env var embed compile-time — đổi .env.production phải rebuild FE
Triệu chứng: Đổi VITE_API_BASE_URL=... trong .env.production nhưng FE
vẫn gọi URL cũ. Hot reload không giúp.
Nguyên nhân: Vite inline import.meta.env.VITE_* tại build time vào JS
bundle (minified). File .env* chỉ đọc khi vite build — không runtime.
Fix: Sau đổi env:
- Rebuild:
cd fe-admin ; npm run build - Deploy dist mới lên IIS
- Clear CDN/browser cache (Ctrl+Shift+R)
Verify bundle có URL mới: curl dist/assets/index-*.js | grep -oE 'https://[^"]+api'.
Deploy / Production (continued)
37. PowerShell 5.1 diacritics trong script — gotcha #30 tái phát
Triệu chứng (bis): migrate-domains.ps1 viết với "Phương Án", "→",
em-dash → PS 5.1 parser fail Missing closing '', Unexpected character.
Fix: Luôn ASCII-only cho .ps1 — rule lặp lại gotcha #30. Cách phát hiện: grep file cho UTF-8 multi-byte chars trước khi deploy:
grep -P '[\x80-\xff]' scripts/*.ps1
# Nếu có match → rewrite ASCII-only
Email / Users
38. Email rename Identity user — 4 field cần update đồng thời
Triệu chứng: Đổi user.Email xong login với email mới vẫn 401. Hoặc
UserManager.FindByEmail trả null.
Nguyên nhân: Identity lookup qua NormalizedEmail (uppercase), không
Email. Username cũng dùng email. 4 field phải sync:
u.Email = newEmail;
u.NormalizedEmail = newEmail.ToUpperInvariant();
u.UserName = newEmail;
u.NormalizedUserName = newEmail.ToUpperInvariant();
await userManager.UpdateAsync(u);
Bonus: Check conflict trước khi rename (user khác đã có email mới) → skip để tránh duplicate.
39. act_runner v0.2.13 fetch actions/checkout từ github.com timeout 21s
Triệu chứng: Run #108/#109 fail trong 22s với:
Get "https://github.com/actions/checkout/info/refs?service=git-upload-pack":
dial tcp 20.205.243.166:443: connectex: A connection attempt failed
because the connected party did not properly respond...
Test gate (Domain + Infra) chưa kịp chạy. Build/deploy không tới.
Nguyên nhân: act_runner mỗi run đều git fetch action source code từ
github.com (kiểm tra update actions/checkout@v4). Khi VPS → github.com
TCP có vấn đề (intermittent firewall/network), 21s timeout → toàn job fail
TRƯỚC step nào của workflow chạy.
Fix: Thay uses: actions/checkout@v4 bằng manual git checkout từ Gitea
internal — bypass github.com hoàn toàn.
- name: Checkout (manual git, bypass github.com)
shell: powershell
run: |
git config --global --add safe.directory '*'
git init -q
git remote add origin "https://gitea-actions:${{ github.token }}@git.baocaogiaoduc.vn/${{ github.repository }}.git"
$ref = "${{ github.ref }}"
if ($ref -like "refs/heads/*") { $ref = $ref.Substring(11) }
git fetch --depth=30 origin $ref
git checkout --quiet "${{ github.sha }}"
Tương tự với actions/upload-artifact@v4 — bỏ vì cũng phụ thuộc github.com.
TRX file vẫn save local trong test-results/ cho debug.
Long-term option: config github_mirror trong gitea-runner config.yaml
mirror github.com → Gitea internal repo. Hoặc pre-cache .cache/act/<hash>/
manually 1 lần.
Reference: Run #108 commit 52999f3 fail, run #110 commit 14b7d18 fix pass.
40. npm junction cache tsc not found sau Move-Item — chưa xác định root cause
Triệu chứng: Implement npm cache strategy bằng junction (mklink /J) +
Move-Item node_modules → cache dir → fail 'tsc' is not recognized ở step
npm run build. Log NO Write-Host "cache MISS" output, NO npm install
output. Timing 1.6s từ end-of-BE-build → start-of-fe-admin npm run build
(impossible cho npm install 49s).
Hypothesis:
- (A) Move-Item của
node_moduleschứa nested junctions/symlinks → .bin/ relative paths broken sau move - (B) act_runner PowerShell stream capture có quirk với cache MISS branch → output bị silenced
- (C)
Test-Pathtrả về stale TRUE từ một state khác
Workaround tạm: Rollback về fresh npm install mỗi run (49s + 33s = 82s).
Path filter docs-only skip CI là alternative win lớn hơn.
TODO khi debug session sau:
- Thử
robocopy /MIRthayMove-Item(handle symlinks tốt hơn) - Hoặc Copy-Item với
-Force -Recurse(slower nhưng safer) - Hoặc dùng act_runner built-in
cache.hostserver (có sẵn trong config.yaml)
Reference: Run #111 commit 29eb5d9 fail, rollback ở a21790d.
41. Gitea Actions paths-ignore — workflow file change vẫn trigger
Triệu chứng: Setup paths-ignore: ['docs/**', '**/*.md'] để skip CI
khi commit MD-only. Tự nhiên commit .gitea/workflows/deploy.yml (chính
workflow file) cũng bị skip → không thể test workflow change.
Nguyên nhân: paths-ignore evaluate set của file thay đổi. Nếu TẤT CẢ
file thay đổi match patterns → skip. Workflow file .gitea/workflows/**
không trong list ignore → trigger normal. OK behavior.
Edge case ngược: commit thay đổi cả docs/STATUS.md + src/Backend/...cs
→ NOT skip vì có file ngoài ignore patterns. Cũng OK.
Verify: Commit chỉ touch docs/STATUS.md → check Gitea Actions UI →
phải KHÔNG có run mới trigger. Test với curl /api/v1/.../runs/<id>
trả Not found cho run-id tiếp theo.
Pattern hiện áp dụng:
on:
push:
branches: [main]
paths-ignore:
- 'docs/**'
- '**/*.md'
- '.claude/skills/**'
- '.gitignore'
- 'scripts/**.md'
KHÔNG ignore: .gitea/workflows/**, *.cs, *.tsx, *.ts, *.csproj,
*.json, *.slnx, tests/**.
Saving: ~196s/commit cho ~30% commit thuộc loại docs-only (chốt MD, session log, etc).
Reference: Commit 29eb5d9 add filter, verify ở commit 512880c
(docs-only) → Gitea NO trigger run #113.
Checklist debug bug mới
- Build pass không? → fail → check using + package version compat
- DI register đủ? → runtime error "Unable to resolve" → add
AddScoped/Singleton - API log startup có error ẩn? →
tailoutput file - File đã persist đúng chưa? →
head -5verify sau Write - Nếu package exotic → thử downgrade về stable trước
- Nếu TS error → check
erasableSyntaxOnly,verbatimModuleSyntax - Nếu EF expression tree → tách logic ra ngoài query
- Nếu Unicode CLI → dùng file payload
- Nếu workflow 403 → check FE
workflow.nextPhasessync từ BE pinned policy - Nếu SignalR 401 → dùng
accessTokenFactory+ BE OnMessageReceived hook (#26) - Nếu PS 5.1 script fail → check encoding UTF-8 / BOM / ASCII-only (#30)
- Nếu subdomain trả sai content / bị hijack → check IPv4/IPv6 port collision trên VPS shared (#33)
- Nếu 2 NavLink cùng active / không đúng highlight → custom isActive match query string (#34)
- Nếu menu item có quyền nhưng không hiện → check GetMyMenuTreeQuery inheritance extend (#35)
- Nếu FE gọi API sai URL sau đổi env → rebuild + clear bundle cache (#36)
- Nếu .ps1 fail parser trên PS 5.1 → ASCII-only, grep multi-byte chars (#30, #37)
- Nếu rename email Identity vẫn 401 → update 4 field NormalizedEmail/UserName (#38)
- Nếu CI fail TCP timeout 21s ở "Set up job" → bypass github.com, manual checkout từ Gitea (#39)
- Nếu npm install caching fail
tsc not found→ KHÔNG dùng junction Move-Item, thử robocopy/Copy-Item (#40) - Nếu CI vẫn trigger khi commit MD-only → paths-ignore trong on:push không match patterns đúng (#41)