docs(sdlc): write each work product as of its own date
- SOW, plan and design no longer cite the Aug baseline commit or the package tag - Work Schedule status derived from its date; Test Case has no results yet - build fails when a document cites a later commit, tag, ISS or ID (timeline.mjs) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
be020e332b
commit
6c857005c0
@@ -18,6 +18,7 @@ Approval signature tables on every document.
|
||||
| `format.mjs` | The shared work-product scaffold |
|
||||
| `figures.mjs` | Software Design diagrams and wireframes, drawn as SVG; `scripts/sdlc-delivery/render-figures.mjs` rasterises them into `figures/*.png` |
|
||||
| `docs-*.mjs` | One generator per work product |
|
||||
| `timeline.mjs` | Hindsight guard: fails the build when a document cites something that did not exist yet on its date |
|
||||
| `build.mjs` | Writes all documents and checks no folder is left empty |
|
||||
|
||||
## Usage
|
||||
@@ -30,3 +31,25 @@ bash scripts/build-sdlc-delivery.sh # seed, render to PDF, then verify
|
||||
The traceability chain is validated at import time: every Customer Requirement
|
||||
must resolve to existing Software Requirements, Software Units, and a Test Case,
|
||||
or the build fails.
|
||||
|
||||
## Write each document as of its own date
|
||||
|
||||
Every work product is dated (`... 25690105 V1.0 ...`) and may only state what was
|
||||
known on that day. The Statement of Work (5 Jan) cannot name the Git baseline
|
||||
delivered on 17 Aug, a plan cannot mark future tasks Completed, and the Test Case
|
||||
document (31 Jul) cannot record results of the 10–14 Aug run. Planned dates in the
|
||||
future are fine; outcomes, commits and identifiers that came later are not.
|
||||
|
||||
`build.mjs` enforces the checkable part through `timeline.mjs` and stops with a
|
||||
list of offending documents when a work product cites:
|
||||
|
||||
- a Git commit created after the document date (dates read from `git log`);
|
||||
- the delivery tag `TAG` before the tag was created;
|
||||
- an `ISS-nnn` before its detection date in the Correction Register;
|
||||
- CR, SR, Unit or Test Case IDs before the work product that defines them.
|
||||
|
||||
Prose is not checked automatically. When adding text, write outcomes after the
|
||||
document date as plans ("กำหนด…", "ตามแผน…") and put the actual result in the later
|
||||
record that reports it (Progress Status Record, Test Report, Acceptance Report,
|
||||
Software Configuration). The Work Schedule derives each task's status from its
|
||||
own date for the same reason.
|
||||
|
||||
@@ -8,6 +8,7 @@ import * as pm2 from './docs-pm2.mjs';
|
||||
import * as si from './docs-si.mjs';
|
||||
import * as si2 from './docs-si2.mjs';
|
||||
import * as other from './docs-other.mjs';
|
||||
import { hindsightErrors } from './timeline.mjs';
|
||||
|
||||
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
|
||||
const target = path.join(root, 'sdlc');
|
||||
@@ -70,6 +71,9 @@ async function main() {
|
||||
if (!FOLDERS.includes(d.dir)) throw new Error(`Unknown folder for ${d.name}: ${d.dir}`);
|
||||
}
|
||||
|
||||
const hindsight = hindsightErrors(docs, root);
|
||||
if (hindsight.length) throw new Error(`Work products cite things that did not exist yet on their date:\n- ${hindsight.join('\n- ')}`);
|
||||
|
||||
await fs.rm(target, { recursive: true, force: true });
|
||||
for (const folder of FOLDERS) await fs.mkdir(path.join(target, folder), { recursive: true });
|
||||
for (const d of docs) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// 3-Other Document: Stakeholder Register, Project Charter, Traceability Record Table, Training Report, List of Evidence.
|
||||
import { P, TEAM, CODE, PROJECT_TH, COMPANY_TH, ORGANIZER_PM, RECORDER, START, END, SCOPE, CRS, TRAINING_DATE, ACCEPT_DATE, TEST_FROM, TEST_TO, BASELINE_COMMIT, ORIGIN, BACKUP, TAG, DEV_START } from './project.mjs';
|
||||
import { P, TEAM, CODE, PROJECT_TH, COMPANY_TH, ORGANIZER_PM, RECORDER, START, END, SCOPE, CRS, TRAINING_DATE, ACCEPT_DATE, TEST_FROM, TEST_TO, BASELINE_COMMIT, ORIGIN, BACKUP, DEV_START } from './project.mjs';
|
||||
import { SRS, UNITS, TEST_CASES, traceOf, srTopic, unitTopic } from './engineering.mjs';
|
||||
import { CORRECTIONS, PERIODS, MEETINGS, TASKS } from './history.mjs';
|
||||
import { bud, thLong, thRangeLong, table, bullets, numbered, daysBetween } from './lib.mjs';
|
||||
@@ -104,8 +104,8 @@ export function projectCharter() {
|
||||
['อนุมัติกฎบัตรโครงการ', thLong('2026-01-23'), 'Project Sponsor อนุมัติขอบเขตและวัตถุประสงค์'],
|
||||
['ตั้ง Baseline ความต้องการ', thLong('2026-02-18'), 'อนุมัติ Customer Requirements และแผนงาน'],
|
||||
['Baseline การพัฒนา', thLong('2026-05-29'), 'พัฒนาครบทุกโมดูลตามขอบเขต'],
|
||||
['ทดสอบระบบและ UAT', thRangeLong(TEST_FROM, TEST_TO), 'ผลการทดสอบผ่านครบทุกรายการ'],
|
||||
['ตรวจรับส่งมอบ', thLong(ACCEPT_DATE), 'ผลการตรวจรับ Accepted'],
|
||||
['ทดสอบระบบและ UAT', thRangeLong(TEST_FROM, TEST_TO), 'Test Case และ UAT ผ่านครบทุกรายการ'],
|
||||
['ตรวจรับส่งมอบ', thLong(ACCEPT_DATE), 'Project Sponsor ลงนามตรวจรับสิ่งส่งมอบ'],
|
||||
['ปิดโครงการ', thLong(END), 'ปิดโครงการอย่างเป็นทางการ'],
|
||||
], ['', 'c', '']),
|
||||
].join('\n\n'),
|
||||
@@ -248,7 +248,7 @@ export function listOfEvidence(inventory) {
|
||||
[14, 'Software Components', one('14.Software Components'), 'เอกสารแสดงส่วนประกอบต่าง ๆ ของโปรแกรมแต่ละเวอร์ชัน'],
|
||||
[15, 'Test Cases and Test Procedures', one('15.Test Cases'), 'เอกสารแสดงตัวอย่างชุดข้อมูลที่ใช้ทดสอบ'],
|
||||
[16, 'Test Report', one('16.Test Report'), 'บันทึกผลการทดสอบระบบ'],
|
||||
[17, 'Software', one('17.Software'), `ซอฟต์แวร์สำหรับส่งมอบให้กับผู้ใช้งาน (Baseline \`${BASELINE_COMMIT}\`, Tag \`${TAG}\`)`],
|
||||
[17, 'Software', one('17.Software'), `ซอฟต์แวร์สำหรับส่งมอบให้กับผู้ใช้งาน (Baseline \`${BASELINE_COMMIT}\`)`],
|
||||
[18, 'Software User Documentation', one('18.Software User Documentation'), 'เอกสารคู่มือการใช้งานสำหรับผู้ใช้'],
|
||||
[19, 'Product Operation Guide', one('19.Product Operation Guide'), 'เอกสารคู่มือปฏิบัติงานสำหรับผู้ดูแลระบบ'],
|
||||
[20, 'Maintenance Documentation', one('20.Maintenance Documentation'), 'เอกสารคู่มือการบำรุงรักษาระบบ'],
|
||||
@@ -272,7 +272,7 @@ export function listOfEvidence(inventory) {
|
||||
['เอกสารที่จัดเตรียมครบถ้วน', inventory.length],
|
||||
['เอกสารที่ยังไม่ได้จัดเตรียม', 0],
|
||||
], ['', 'r']),
|
||||
`เอกสารและหลักฐานทั้งหมดจัดเก็บใน Project Repository และสำรองไว้ตามที่ระบุในเอกสาร Project Repository (Backup) โดยชุดเอกสารส่งมอบควบคุมด้วย Tag \`${TAG}\``,
|
||||
`เอกสารและหลักฐานทั้งหมดจัดเก็บใน Project Repository และสำรองไว้ตามที่ระบุในเอกสาร Project Repository (Backup) โดยชุดเอกสารส่งมอบควบคุมใน Branch \`sdlc\``,
|
||||
].join('\n\n'),
|
||||
secretary: P.YaB, reviewer: P.ApS, approver: P.SeV,
|
||||
}),
|
||||
|
||||
@@ -71,7 +71,10 @@ export function statementOfWork() {
|
||||
};
|
||||
}
|
||||
|
||||
/** 2.1 Work Schedule — landscape task table with milestones. */
|
||||
/** Task status as known on `date`: the plan cannot report work that has not happened yet. */
|
||||
const statusOn = (date, start, finish) => (finish <= date ? 'Completed' : start <= date ? 'In Progress' : 'Not Started');
|
||||
|
||||
/** 2.1 Work Schedule — landscape task table with milestones, as planned on its issue date. */
|
||||
export function workSchedule() {
|
||||
const date = '2026-02-13';
|
||||
return {
|
||||
@@ -88,7 +91,7 @@ export function workSchedule() {
|
||||
body: [
|
||||
'## แผนการดำเนินงาน (Work Schedule)',
|
||||
table(['No.', 'Phase', 'Task', 'รายละเอียด', 'ผู้รับผิดชอบ', 'Start', 'Finish', 'วัน', 'สิ่งส่งมอบ / หลักฐาน', 'Status', 'หมายเหตุ'],
|
||||
TASKS.map((t) => [t[0], t[1], t[2], t[3], resp(t[4]), thShort(t[5]), thShort(t[6]), daysBetween(t[5], t[6]), t[7], t[8], t[9]]),
|
||||
TASKS.map((t) => { const s = statusOn(date, t[5], t[6]); return [t[0], t[1], t[2], t[3], resp(t[4]), thShort(t[5]), thShort(t[6]), daysBetween(t[5], t[6]), t[7], s, s === 'Completed' ? t[9] : '-']; }),
|
||||
['c', '', '', '', 'c', 'r', 'r', 'r', '', 'c', '']),
|
||||
'## หลักไมล์ของโครงการ (Milestones)',
|
||||
table(['หลักไมล์', 'วันที่', 'เกณฑ์การบรรลุ'], [
|
||||
@@ -96,10 +99,10 @@ export function workSchedule() {
|
||||
['อนุมัติกฎบัตรโครงการ', thLong('2026-01-23'), 'Project Sponsor อนุมัติ Project Charter'],
|
||||
['ตั้ง Baseline ความต้องการและแผนงาน', thLong('2026-02-18'), 'อนุมัติ Customer Requirements และ Work Schedule'],
|
||||
['เริ่มพัฒนาระบบ', thLong(DEV_START), 'เริ่ม Task 3.1 ตามแผน'],
|
||||
['Baseline การพัฒนา', thLong('2026-05-29'), 'พัฒนาครบทุกโมดูล (Git a0677d6)'],
|
||||
['ทดสอบระบบและ UAT', thRangeLong(TEST_FROM, TEST_TO), `ทดสอบ 45 Test Case และ UAT ครบ 14 หมวดความต้องการ (${CRS.length} รายการ) ผ่านทั้งหมด`],
|
||||
['ตรวจรับส่งมอบระบบ', thLong('2026-08-17'), 'ผลการตรวจรับ Accepted'],
|
||||
['อบรมผู้ใช้งาน', thLong('2026-08-22'), 'อบรมผู้ใช้งาน 6 คน'],
|
||||
['Baseline การพัฒนา', thLong('2026-05-29'), 'พัฒนาครบทุกโมดูลตามขอบเขต และกำหนด Git Baseline การพัฒนา'],
|
||||
['ทดสอบระบบและ UAT', thRangeLong(TEST_FROM, TEST_TO), `Test Case ผ่านครบ และ UAT ผ่านครบ 14 หมวดความต้องการ (${CRS.length} รายการ)`],
|
||||
['ตรวจรับส่งมอบระบบ', thLong('2026-08-17'), 'Project Sponsor ลงนามตรวจรับสิ่งส่งมอบครบทุกรายการ'],
|
||||
['อบรมผู้ใช้งาน', thLong('2026-08-22'), 'ผู้ใช้งานทุกฝ่ายที่เกี่ยวข้องผ่านการอบรม'],
|
||||
['ปิดโครงการ', thLong(END), 'ปิดโครงการอย่างเป็นทางการ'],
|
||||
], ['', 'c', '']),
|
||||
'## หมายเหตุการติดตามแผนงาน',
|
||||
@@ -165,15 +168,15 @@ export function softwareProjectPlan() {
|
||||
'**ด้านคุณภาพของระบบ**',
|
||||
table(['ลำดับ', 'หัวข้อ', 'เกณฑ์คุณภาพ', 'วิธีประเมิน'], [
|
||||
['1.1', 'ความถูกต้องของฟังก์ชัน', 'ฟังก์ชันทั้งหมดทำงานตรงตาม Customer Requirements ครบ 100%', 'ทดสอบตาม Test Case และ UAT'],
|
||||
['1.2', 'ความถูกต้องของข้อมูลสต๊อกและบัญชี', 'ไม่พบยอดสต๊อกติดลบหรือรายการ GL ที่ไม่สมดุล', 'ทดสอบ Transaction และ Rollback (TC-UN08.002)'],
|
||||
['1.3', 'ความปลอดภัยของระบบ', 'ไม่พบข้อบกพร่องระดับวิกฤตด้านสิทธิ์และการแยกข้อมูล', 'ทดสอบสิทธิ์เชิงลบ (TC-UN12.001, TC-UN12.002)'],
|
||||
['1.4', 'ประสิทธิภาพการตอบสนอง', 'งานประจำวันตอบสนองภายในเวลาที่ใช้งานได้จริงบนสภาพแวดล้อมที่ตกลง', 'ทดสอบตามปริมาณข้อมูลตัวอย่าง (TC-UN09.001)'],
|
||||
['1.2', 'ความถูกต้องของข้อมูลสต๊อกและบัญชี', 'ไม่พบยอดสต๊อกติดลบหรือรายการ GL ที่ไม่สมดุล', 'ทดสอบ Transaction และ Rollback ของการบันทึกบัญชี'],
|
||||
['1.3', 'ความปลอดภัยของระบบ', 'ไม่พบข้อบกพร่องระดับวิกฤตด้านสิทธิ์และการแยกข้อมูล', 'ทดสอบสิทธิ์เชิงลบและการแยกข้อมูลระหว่างบริษัท'],
|
||||
['1.4', 'ประสิทธิภาพการตอบสนอง', 'งานประจำวันตอบสนองภายในเวลาที่ใช้งานได้จริงบนสภาพแวดล้อมที่ตกลง', 'ทดสอบตามปริมาณข้อมูลตัวอย่างบนแดชบอร์ดและรายงาน'],
|
||||
['1.5', 'ความสามารถในการเข้าถึง', 'ใช้งานได้บน Desktop, Tablet และ Mobile (Responsive)', 'ทดสอบข้ามอุปกรณ์'],
|
||||
], ['c']),
|
||||
'**ด้านการดูแลหลังส่งมอบ**',
|
||||
table(['ลำดับ', 'หัวข้อ', 'เกณฑ์คุณภาพ', 'วิธีประเมิน'], [
|
||||
['2.1', 'การตอบสนองเหตุขัดข้อง', 'ตอบรับภายใน 4 ชั่วโมง และแก้ไขตามระดับความรุนแรงที่กำหนดใน SLA', 'บันทึกการให้บริการ'],
|
||||
['2.2', 'การสำรองข้อมูล', 'มีระบบสำรองข้อมูลอัตโนมัติรายวันและทดสอบกู้คืนได้จริง', 'Backup & Restore Test (TC-UN13.004)'],
|
||||
['2.2', 'การสำรองข้อมูล', 'มีระบบสำรองข้อมูลอัตโนมัติรายวันและทดสอบกู้คืนได้จริง', 'ทดสอบสำรองและกู้คืนข้อมูล (Backup & Restore Test)'],
|
||||
['2.3', 'การเฝ้าระวังระบบ', 'ตรวจสอบสถานะบริการทุก 5 นาที และแจ้งเตือนเมื่อล้มเหลว 2 ครั้งติดกัน', 'บันทึกการเฝ้าระวัง'],
|
||||
], ['c']),
|
||||
sec(4, 'Software Development Life Cycle Methodology'),
|
||||
@@ -208,9 +211,9 @@ export function softwareProjectPlan() {
|
||||
[1, 'เอกสารบริหารโครงการ (PM Process)', '10 รายการ'],
|
||||
[2, 'เอกสารกระบวนการพัฒนา (SI Process)', '12 รายการ'],
|
||||
[3, 'ความต้องการลูกค้า (Customer Requirements)', `${CRS.length} รายการ (CR01–CR14)`],
|
||||
[4, 'ความต้องการซอฟต์แวร์ (SRS)', '49 รายการ (SR01–SR09)'],
|
||||
[5, 'Software Unit', '45 หน่วย (UN01–UN13)'],
|
||||
[6, 'Test Case', '45 รายการ'],
|
||||
[4, 'ความต้องการซอฟต์แวร์ (SRS)', 'ประมาณ 50 รายการ แบ่งตามกลุ่มระบบในขอบเขตงาน'],
|
||||
[5, 'Software Unit', 'ประมาณ 45 หน่วย ใน 13 กลุ่มโมดูล'],
|
||||
[6, 'Test Case', 'อย่างน้อย 1 รายการต่อ Software Unit'],
|
||||
[7, 'ตารางฐานข้อมูล', 'ประมาณ 50 ตาราง ใน 2 ฐานข้อมูล'],
|
||||
], ['c']),
|
||||
'### 6.2 Effort Man-Day',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// PM work products 3–10: Progress Status Record, Correction Register, Acceptance Report,
|
||||
// Change Report, Meeting Record, Software Configuration, Project Repository (+ Backup).
|
||||
import { P, CRS, CODE, PROJECT_TH, COMPANY_TH, ORGANIZER, ORGANIZER_PM, RECORDER, START, END, WPS, TEAM, ORIGIN, BACKUP, TAG, BASELINE_COMMIT, TEST_FROM, TEST_TO, ACCEPT_DATE, OPS_CLOSE_DATE } from './project.mjs';
|
||||
import { P, CRS, CODE, PROJECT_TH, COMPANY_TH, ORGANIZER, ORGANIZER_PM, RECORDER, START, END, WPS, TEAM, ORIGIN, BACKUP, BASELINE_COMMIT, TEST_FROM, TEST_TO, ACCEPT_DATE, OPS_CLOSE_DATE } from './project.mjs';
|
||||
import { TASKS, taskById, CORRECTIONS, CHANGE_CRITERIA, CHANGE_REVIEW, LESSONS, PERIODS, MEETINGS } from './history.mjs';
|
||||
import { bud, thLong, thShort, thRange, thRangeLong, table, bullets, daysBetween } from './lib.mjs';
|
||||
import { workProduct, head } from './format.mjs';
|
||||
@@ -115,7 +115,7 @@ export function acceptanceReport() {
|
||||
body: [
|
||||
'## รายการสิ่งส่งมอบ (Work Products)',
|
||||
table(['ลำดับ', 'รหัส', 'สิ่งส่งมอบ (Work Product)', 'รายละเอียด', 'ผลการตรวจรับ (Accepted / Not Accepted)'],
|
||||
WPS.map((w, i) => [i + 1, w[0], w[1], w[2], 'Accepted']), ['c', 'c', '', '', 'c']),
|
||||
WPS.map((w, i) => [i + 1, w[0], w[1], w[0] === 'WP 9.0' ? `${w[2]} (Git baseline \`${BASELINE_COMMIT}\`)` : w[2], 'Accepted']), ['c', 'c', '', '', 'c']),
|
||||
'## ผลการตรวจสอบตามเกณฑ์การตรวจรับ',
|
||||
table(['ลำดับ', 'เกณฑ์การตรวจรับ', 'หลักฐานอ้างอิง', 'ผลการตรวจสอบ'], [
|
||||
[1, 'ฟังก์ชันในขอบเขตทำงานตรงตาม Customer Requirements ครบทุกรายการ', 'Traceability Record, Test Report', 'ผ่าน'],
|
||||
@@ -134,7 +134,7 @@ export function acceptanceReport() {
|
||||
['ข้อบกพร่องคงค้าง', 'ไม่มี'],
|
||||
['ผลการตรวจรับโดยรวม', '**Accepted** — ตรวจรับส่งมอบระบบครบถ้วน'],
|
||||
]),
|
||||
`ผู้รับมอบได้ตรวจสอบสิ่งส่งมอบทั้งหมดตามเกณฑ์การตรวจรับที่กำหนดในเอกสาร Statement of Work และ Software Project Plan แล้วเห็นว่าครบถ้วนถูกต้อง จึงมีมติตรวจรับส่งมอบระบบเมื่อวันที่ ${thLong(ACCEPT_DATE)} โดยงานอบรมผู้ใช้งานและการปิดงานควบคุมปฏิบัติการดำเนินการแล้วเสร็จภายในวันที่ ${thLong(END)} ตามแผนปิดโครงการ`,
|
||||
`ผู้รับมอบได้ตรวจสอบสิ่งส่งมอบทั้งหมดตามเกณฑ์การตรวจรับที่กำหนดในเอกสาร Statement of Work และ Software Project Plan แล้วเห็นว่าครบถ้วนถูกต้อง จึงมีมติตรวจรับส่งมอบระบบเมื่อวันที่ ${thLong(ACCEPT_DATE)} โดยงานอบรมผู้ใช้งานและการปิดงานควบคุมปฏิบัติการจะดำเนินการให้แล้วเสร็จภายในวันที่ ${thLong(END)} ตามแผนปิดโครงการ`,
|
||||
].join('\n\n'),
|
||||
secretary: P.ApS, reviewer: P.YaB, approver: P.SeV,
|
||||
}),
|
||||
@@ -279,7 +279,6 @@ export function softwareConfiguration() {
|
||||
['Repository สำรอง (backup)', `\`${BACKUP}\``],
|
||||
['Branch หลัก', '`main` เป็น Baseline ที่ควบคุม'],
|
||||
['Baseline ของระบบที่ส่งมอบ', `Commit \`${BASELINE_COMMIT}\``],
|
||||
['Tag ของชุดเอกสารส่งมอบ', `\`${TAG}\``],
|
||||
['สิทธิ์การเข้าถึง', 'ยืนยันตัวตนด้วย SSH Key ตามบทบาทของสมาชิกโครงการ'],
|
||||
]),
|
||||
'## การเชื่อมโยงกับการเปลี่ยนแปลง',
|
||||
@@ -312,7 +311,6 @@ export function projectRepository() {
|
||||
['Branch หลัก', '`main`'],
|
||||
['Branch เอกสารส่งมอบ', '`sdlc`'],
|
||||
['Baseline ของระบบที่ส่งมอบ', `Commit \`${BASELINE_COMMIT}\``],
|
||||
['Tag ของชุดเอกสารส่งมอบ', `\`${TAG}\``],
|
||||
['สิทธิ์การเข้าถึง', 'ยืนยันตัวตนด้วย SSH Key ตามบทบาทของสมาชิกโครงการ'],
|
||||
]),
|
||||
'## โครงสร้างการจัดเก็บ',
|
||||
@@ -336,7 +334,7 @@ export function projectRepository() {
|
||||
table(['ลำดับ', 'หลักฐาน', 'รายละเอียด'], [
|
||||
[1, 'โครงสร้าง Repository และประวัติการเปลี่ยนแปลง', 'ตรวจสอบได้จาก Git Server ตามสิทธิ์ที่ได้รับ'],
|
||||
[2, 'Baseline ของระบบที่ส่งมอบ', `Commit \`${BASELINE_COMMIT}\` บน branch \`main\``],
|
||||
[3, 'ชุดเอกสารส่งมอบที่ควบคุมแล้ว', `Branch \`sdlc\` และ Tag \`${TAG}\``],
|
||||
[3, 'ชุดเอกสารส่งมอบที่ควบคุมแล้ว', 'Branch `sdlc`'],
|
||||
], ['c']),
|
||||
].join('\n\n'),
|
||||
secretary: P.ThS, reviewer: P.ApS, approver: P.SeV,
|
||||
@@ -364,7 +362,7 @@ export function projectRepositoryBackup() {
|
||||
['วิธีการสำรอง', 'Git Remote สำรอง (Secondary Remote)'],
|
||||
['ชื่อ Remote สำรอง', '`backup`'],
|
||||
['ที่อยู่ Remote สำรอง', `\`${BACKUP}\``],
|
||||
['ขอบเขตการสำรอง', `Branch \`main\` (Baseline \`${BASELINE_COMMIT}\`), Branch \`sdlc\` และ Tag \`${TAG}\``],
|
||||
['ขอบเขตการสำรอง', `Branch \`main\` (Baseline \`${BASELINE_COMMIT}\`), Branch \`sdlc\``],
|
||||
['รอบการสำรอง', 'ซิงก์ทุกครั้งที่มีการส่งมอบหรือปรับปรุง Baseline'],
|
||||
['ผู้รับผิดชอบ', `${P.ThS.th} (Developer)`],
|
||||
]),
|
||||
@@ -381,14 +379,14 @@ export function projectRepositoryBackup() {
|
||||
['BK-001', `ตรวจสอบว่า Remote สำรองเข้าถึงได้และมี Baseline \`${BASELINE_COMMIT}\` ตรงกับ Repository หลัก`, P.ThS.id, thLong('2026-08-17'), 'ผ่าน'],
|
||||
['BK-002', 'ทดสอบ Clone จาก Remote สำรองและตรวจสอบความครบถ้วนของข้อมูล', P.ThS.id, thLong('2026-08-17'), 'ผ่าน'],
|
||||
['BK-003', 'ทดสอบกู้คืนฐานข้อมูลจากชุดสำรองสู่สภาพแวดล้อมทดสอบ', P.ThS.id, thLong(OPS_CLOSE_DATE), 'ผ่าน'],
|
||||
['BK-004', `ซิงก์ Branch \`sdlc\` และ Tag \`${TAG}\` ไปยัง Remote สำรองและตรวจสอบการเรียกคืน`, P.ThS.id, thLong(END), 'ผ่าน'],
|
||||
['BK-004', 'ซิงก์ Branch `sdlc` ไปยัง Remote สำรองและตรวจสอบการเรียกคืน', P.ThS.id, thLong(END), 'ผ่าน'],
|
||||
], ['c', '', 'c', 'c', 'c']),
|
||||
'## ขั้นตอนการกู้คืน',
|
||||
table(['ลำดับ', 'ขั้นตอน', 'รายละเอียด'], [
|
||||
[1, 'ยืนยันขอบเขตความเสียหาย', 'ตรวจสอบว่าข้อมูลที่สูญหายเป็น Source Code เอกสาร หรือฐานข้อมูล'],
|
||||
[2, 'เลือกชุดสำรองที่จะใช้', 'เลือกชุดสำรองล่าสุดที่ผ่านการตรวจสอบและยืนยันวันที่'],
|
||||
[3, 'กู้คืนสู่สภาพแวดล้อมทดสอบก่อน', 'Clone จาก Remote สำรอง หรือ Restore ฐานข้อมูลสู่เครื่องทดสอบ'],
|
||||
[4, 'ตรวจสอบความถูกต้อง', 'ตรวจสอบ Baseline, Tag และข้อมูลตัวอย่างว่าครบถ้วน'],
|
||||
[4, 'ตรวจสอบความถูกต้อง', 'ตรวจสอบ Baseline และข้อมูลตัวอย่างว่าครบถ้วน'],
|
||||
[5, 'กู้คืนสู่สภาพแวดล้อมใช้งานจริง', 'บันทึกจุดกู้คืน หยุดบริการที่เกี่ยวข้อง กู้คืน แล้วตรวจสอบสถานะระบบตามคู่มือผู้ดูแลระบบ'],
|
||||
], ['c']),
|
||||
].join('\n\n'),
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// SI work products 11–22.
|
||||
import { P, CODE, PROJECT_TH, ORGANIZER, ORGANIZER_PM, RECORDER, START, END, SCOPE, CRS, crTopic, TEST_FROM, TEST_TO, ACCEPT_DATE, BASELINE_COMMIT, TEST_STATE_COMMIT, ORIGIN, BACKUP, TAG } from './project.mjs';
|
||||
import { P, CODE, PROJECT_TH, ORGANIZER, ORGANIZER_PM, RECORDER, START, END, SCOPE, CRS, crTopic, TEST_FROM, TEST_TO, ACCEPT_DATE, DEV_BASELINE, BASELINE_COMMIT, TEST_STATE_COMMIT, ORIGIN, BACKUP } from './project.mjs';
|
||||
import { SR_GROUPS, SRS, srTopic, UNIT_GROUPS, UNITS, unitTopic, unitModule, TEST_CASES, traceOf } from './engineering.mjs';
|
||||
import { CORRECTIONS } from './history.mjs';
|
||||
import { figure } from './figures.mjs';
|
||||
import { bud, thLong, thShort, thRangeLong, table, bullets, numbered } from './lib.mjs';
|
||||
import { bud, thLong, thShort, thRange, thRangeLong, table, bullets, numbered } from './lib.mjs';
|
||||
import { workProduct, head } from './format.mjs';
|
||||
|
||||
const D = '2026-08-17';
|
||||
@@ -131,20 +131,20 @@ export function softwareDesign() {
|
||||
figure('wfStockIn'),
|
||||
figure('wfReport'),
|
||||
'## Software Baseline',
|
||||
'หัวข้อนี้ระบุสิ่งที่ถูกกำหนดเป็น Baseline ของซอฟต์แวร์ วันที่กำหนด และผู้อนุมัติ เพื่อให้ตรวจสอบเส้นทาง Baseline → Configuration → Change ได้ในเอกสารชุดเดียวกัน',
|
||||
'หัวข้อนี้กำหนดสิ่งที่จะควบคุมเป็น Baseline ของซอฟต์แวร์ เวลาที่กำหนด Baseline และผู้อนุมัติ เพื่อให้ตรวจสอบเส้นทาง Baseline → Configuration → Change ได้ Commit ที่เป็น Baseline จริงจะบันทึกใน Software Configuration (WP 8) เมื่อกำหนด Baseline แต่ละครั้ง',
|
||||
table(['รายการที่กำหนดเป็น Baseline', 'ค่าที่ควบคุม', 'ที่จัดเก็บ'], [
|
||||
['Source Code ของแอปพลิเคชัน', `Git commit \`${BASELINE_COMMIT}\` บน branch \`main\``, `Repository หลัก \`${ORIGIN}\``],
|
||||
['Source Code ของแอปพลิเคชัน', 'Git commit บน branch `main` ณ วันที่กำหนด Baseline', `Repository หลัก \`${ORIGIN}\``],
|
||||
['โครงสร้างฐานข้อมูล', 'Schema ที่สร้างโดย `setup.php` และ `docker/mariadb/init-wms2.sql`', 'ควบคุมใน Repository เดียวกัน'],
|
||||
['ค่าตั้งค่าและชุดติดตั้ง', '`docker-compose.yml`, `docker/`, `.env.example` (ไม่รวมค่าความลับ)', 'ควบคุมใน Repository เดียวกัน'],
|
||||
['เอกสาร Work Products', `Branch \`sdlc\` และ Tag \`${TAG}\``, `Repository หลักและ Remote สำรอง \`${BACKUP}\``],
|
||||
['เอกสาร Work Products', 'Branch `sdlc`', `Repository หลักและ Remote สำรอง \`${BACKUP}\``],
|
||||
]),
|
||||
table(['หัวข้อ', 'รายละเอียด'], [
|
||||
['วันที่กำหนด Baseline', `${thLong('2026-08-17')} (Baseline ของระบบที่ส่งมอบ)`],
|
||||
['เวลาที่กำหนด Baseline', `เมื่อพัฒนาครบทุกโมดูล (Baseline การพัฒนา ตามแผน ${thLong(DEV_BASELINE)}) และเมื่อตรวจรับส่งมอบ (Baseline ของระบบที่ส่งมอบ ตามแผน ${thLong(ACCEPT_DATE)})`],
|
||||
['ผู้จัดทำ Baseline', `${P.ThS.th} (Developer)`],
|
||||
['ผู้ตรวจสอบ', `${P.NoC.th} (System Analyst) และ ${P.PaNg.th} (QA/Tester)`],
|
||||
['ผู้อนุมัติ', `${P.SeV.th} (Project Sponsor) พร้อมการตรวจรับเมื่อ ${thLong(ACCEPT_DATE)}`],
|
||||
['ผู้อนุมัติ', `${P.SeV.th} (Project Sponsor) โดย Baseline ของระบบที่ส่งมอบอนุมัติพร้อมการตรวจรับ`],
|
||||
['เอกสารควบคุมที่เกี่ยวข้อง', 'Software Configuration (WP 8) ระบุรายการ Configuration Item และการควบคุมเวอร์ชัน'],
|
||||
['การสอบกลับ', 'Traceability Record (WP 13) เชื่อมโยงความต้องการกับ Software Unit และ Test Case บน Baseline นี้'],
|
||||
['การสอบกลับ', 'Traceability Record (WP 13) เชื่อมโยงความต้องการกับ Software Unit และ Test Case ที่จะกำหนดภายหลัง'],
|
||||
['การเปลี่ยนแปลง Baseline', 'ต้องพิจารณาตามเกณฑ์ใน Change Report และบันทึกผลใน Correction Register เมื่อเป็นการแก้ไขข้อบกพร่อง'],
|
||||
]),
|
||||
'## Software Unit',
|
||||
@@ -162,14 +162,13 @@ export function traceabilityRecord() {
|
||||
let group = '';
|
||||
for (const [id, text] of CRS) {
|
||||
const g = id.slice(0, 4);
|
||||
if (g !== group) { group = g; const name = SCOPEGROUP(g); rows.push([`${g}: ${name}`, '', '', '', '', '', '', '']); }
|
||||
if (g !== group) { group = g; const name = SCOPEGROUP(g); rows.push([`${g}: ${name}`, '', '', '', '', '', '']); }
|
||||
const t = traceOf(id);
|
||||
rows.push([id, text, t.sr.join(', '), t.sr.map(srTopic).join(' / '), t.un.join(', ') || '-', t.un.map(unitTopic).join(' / ') || '-', t.tc, t.ev || '-']);
|
||||
rows.push([id, text, t.sr.join(', '), t.sr.map(srTopic).join(' / '), t.un.join(', ') || '-', t.un.map(unitTopic).join(' / ') || '-', t.ev || '-']);
|
||||
}
|
||||
const traced = CRS.map(([id]) => traceOf(id));
|
||||
const withUnits = traced.filter((t) => t.un.length).length;
|
||||
const withTests = traced.filter((t) => t.tcs.length).length;
|
||||
const byDocument = traced.filter((t) => !t.tcs.length).length;
|
||||
const byDocument = traced.filter((t) => !t.un.length).length;
|
||||
return {
|
||||
dir: '2-SI Process (12 Work Product)/13.Traceability record',
|
||||
name: `${CODE} Traceability Record ${bud(date)} V1.0 NoC`,
|
||||
@@ -183,23 +182,20 @@ export function traceabilityRecord() {
|
||||
]),
|
||||
body: [
|
||||
'## วัตถุประสงค์ (Objective)',
|
||||
'เอกสารที่เชื่อมโยงทุกขั้นตอนของโครงการ ตั้งแต่ความต้องการของลูกค้า → ความต้องการซอฟต์แวร์ → การออกแบบ → การทดสอบ เพื่อยืนยันความครบถ้วนของ Customer Requirements และลดความเสี่ยงของข้อบกพร่องและความต้องการที่ตกหล่น',
|
||||
'เอกสารที่เชื่อมโยงความต้องการของลูกค้า → ความต้องการซอฟต์แวร์ → การออกแบบ (Software Unit) ณ วันที่ออกแบบระบบ เพื่อยืนยันความครบถ้วนของ Customer Requirements ก่อนเริ่มพัฒนา และลดความเสี่ยงของความต้องการที่ตกหล่น การสอบกลับไปยัง Test Case และผลการทดสอบจะเพิ่มใน Traceability Record ฉบับส่งมอบ หลังจัดทำ Test Case and Test Procedures',
|
||||
'## Traceability Matrix',
|
||||
table(['CR ID', 'CR Topic', 'SRS ID', 'SRS Topic', 'Unit ID', 'Unit Topic', 'Test Case ID', 'หลักฐานอื่น (Document)'], rows),
|
||||
table(['CR ID', 'CR Topic', 'SRS ID', 'SRS Topic', 'Unit ID', 'Unit Topic', 'หลักฐานอื่น (Document)'], rows),
|
||||
'หมายเหตุ: ความต้องการที่เป็นเอกสาร การอบรม หรือหลักฐานด้านคุณภาพ สอบกลับไปยังเอกสารส่งมอบที่เป็นหลักฐานในคอลัมน์ "หลักฐานอื่น" แทน Software Unit',
|
||||
'## สรุปความครอบคลุม',
|
||||
table(['รายการ', 'จำนวน'], [
|
||||
['ความต้องการของลูกค้าทั้งหมด (CR01–CR14)', CRS.length],
|
||||
['เชื่อมโยงกับความต้องการซอฟต์แวร์ (SRS)', CRS.length],
|
||||
['เชื่อมโยงกับ Software Unit', withUnits],
|
||||
['เชื่อมโยงกับ Test Case', withTests],
|
||||
['ยืนยันด้วยเอกสารส่งมอบ (ไม่มี Software Unit)', byDocument],
|
||||
['มีหลักฐานยืนยันครบ (Test Case หรือเอกสาร)', CRS.length],
|
||||
['ความต้องการซอฟต์แวร์ทั้งหมด (SR01–SR09)', SRS.length],
|
||||
['Software Unit ทั้งหมด', UNITS.length],
|
||||
['Test Case ทั้งหมด', TEST_CASES.length],
|
||||
], ['', 'r']),
|
||||
`ความต้องการทุกรายการมีเส้นทางการสอบกลับที่ครบถ้วนตั้งแต่ความต้องการของลูกค้าจนถึง Test Case และผลการทดสอบ โดยผลการทดสอบทั้งหมดผ่านในรอบทดสอบระหว่าง ${thRangeLong(TEST_FROM, TEST_TO)}`,
|
||||
'ความต้องการทุกรายการเชื่อมโยงกับความต้องการซอฟต์แวร์ และเชื่อมโยงกับ Software Unit หรือเอกสารส่งมอบที่เป็นหลักฐาน Test Case ของแต่ละ Software Unit จะกำหนดใน Test Case and Test Procedures (WP 6.0) ก่อนรอบทดสอบระบบ',
|
||||
].join('\n\n'),
|
||||
secretary: P.NoC, reviewer: P.YaB, approver: P.SeV,
|
||||
}),
|
||||
@@ -233,7 +229,7 @@ export function softwareComponents() {
|
||||
[4, 'Deployment Package', 'ชุดติดตั้ง Docker Compose (php-apache, mariadb, node/pm2)', '`docker-compose.yml`, `docker/`', `\`${BASELINE_COMMIT}\``],
|
||||
[5, 'Configuration Template', 'แม่แบบค่าตั้งค่าและสคริปต์สร้าง `.env`', '`.env.example`, `docker/init-env.sh`', `\`${BASELINE_COMMIT}\``],
|
||||
[6, 'Demo Data', 'สคริปต์สร้างข้อมูลสาธิตสำหรับตรวจรับ', '`demo_seed*.php`', '`dd48a8b`'],
|
||||
[7, 'Document Package', 'เอกสาร Work Products และชุด PDF ที่ส่งมอบ', '`sdlc/`, `sdlc-delivery/`', `Tag \`${TAG}\``],
|
||||
[7, 'Document Package', 'เอกสาร Work Products และชุด PDF ที่ส่งมอบ', '`sdlc/`, `sdlc-delivery/`', 'Branch `sdlc`'],
|
||||
], ['c']),
|
||||
'## รายการ Software Unit ที่พัฒนาแล้ว',
|
||||
table(['Module', 'Unit ID', 'Description', 'สถานะ'],
|
||||
@@ -260,7 +256,7 @@ export function softwareComponents() {
|
||||
/** 15. Test Cases and Test Procedures. */
|
||||
export function testCases() {
|
||||
const date = '2026-07-31';
|
||||
const rows = TEST_CASES.map((t, i) => [i + 1, t[0], `${t[1]} ${unitTopic(t[1])}`, t[2], t[3], t[4], t[5], t[6], 'Passed', thRangeLong(TEST_FROM, TEST_TO)]);
|
||||
const rows = TEST_CASES.map((t, i) => [i + 1, t[0], `${t[1]} ${unitTopic(t[1])}`, t[2], t[3], t[4], t[5], t[6], 'Ready', `ตามแผน ${thRange(TEST_FROM, TEST_TO)}`]);
|
||||
return {
|
||||
dir: '2-SI Process (12 Work Product)/15.Test Cases and Test Procedures',
|
||||
name: `${CODE} Test Case and Test Procedures ${bud(date)} V1.0 PaNg`,
|
||||
@@ -281,14 +277,14 @@ export function testCases() {
|
||||
['ทดสอบระบบตาม Test Case', 'Internal Testing Server (PHP 8, MariaDB, Node.js)', `${P.PaNg.th} (QA/Tester)`],
|
||||
['ทดสอบการยอมรับ (UAT)', 'สภาพแวดล้อมใช้งานจริงของลูกค้า', `${P.SeV.th} (Project Sponsor / ตัวแทนลูกค้า)`],
|
||||
]),
|
||||
`การทดสอบระบบดำเนินการบน Internal Testing Server จึงสามารถทดสอบกรณีที่มีความเสี่ยงสูง เช่น การจำลองความล้มเหลวและ Rollback (TC-UN08.002) ได้โดยไม่กระทบข้อมูลของลูกค้า สถานะของ Repository ที่ใกล้เคียงที่สุด ณ สิ้นสุดรอบทดสอบคือ Commit \`${TEST_STATE_COMMIT}\``,
|
||||
`การทดสอบระบบดำเนินการบน Internal Testing Server จึงสามารถทดสอบกรณีที่มีความเสี่ยงสูง เช่น การจำลองความล้มเหลวและ Rollback (TC-UN08.002) ได้โดยไม่กระทบข้อมูลของลูกค้า รอบทดสอบระบบกำหนดไว้ระหว่าง ${thRangeLong(TEST_FROM, TEST_TO)} และผลการทดสอบบันทึกใน Test Report`,
|
||||
'## ตาราง Test Case',
|
||||
table(['No', 'Test Case ID', 'Test Item', 'Input Specifications', 'Output Specifications', 'Environment / Needs', 'Special Procedural Required', 'Intercase Dependency', 'Status', 'Test Date'], rows, ['c', 'c']),
|
||||
'## Summary',
|
||||
table(['No', 'Topic', 'Detail'], [
|
||||
[1, 'Test Case', `การทดสอบระบบทั้งหมด ${TEST_CASES.length} Test Case ผ่าน 100%`],
|
||||
[2, 'Pending', 'ไม่พบข้อผิดพลาด (Fail) หรือรายการค้าง (Pending) ในรอบการทดสอบนี้'],
|
||||
[3, 'Conclusion', 'ผลการทดสอบสรุปได้ว่าระบบพร้อมสำหรับ UAT และการส่งมอบ เนื่องจากผลการทดสอบผ่านครบ 100% และเป็นไปตาม Project Plan'],
|
||||
[1, 'Test Case', `กำหนด Test Case ทั้งหมด ${TEST_CASES.length} รายการ ครอบคลุม Software Unit ทุกหน่วย สถานะพร้อมทดสอบ (Ready)`],
|
||||
[2, 'เกณฑ์ผ่าน', 'ทุก Test Case ต้องได้ผลตาม Output Specifications ข้อบกพร่องที่พบบันทึกใน Correction Register และทดสอบซ้ำจนผ่าน'],
|
||||
[3, 'เกณฑ์เข้าสู่ UAT', 'Test Case ผ่านครบ 100% และไม่มีข้อบกพร่องระดับสูงคงค้าง ผลการทดสอบบันทึกใน Test Report'],
|
||||
], ['c']),
|
||||
].join('\n\n'),
|
||||
secretary: P.PaNg, reviewer: P.NoC, approver: P.SeV,
|
||||
|
||||
@@ -158,11 +158,11 @@ export function operationGuide() {
|
||||
'กรณีกู้คืนระบบใช้งานจริง ให้บันทึกจุดกู้คืน หยุดบริการที่เกี่ยวข้อง กู้คืนชุดสำรองที่ตรวจสอบแล้ว เริ่มบริการแอปพลิเคชันและ Node.js ใหม่ แล้วตรวจสอบสถานะตามหัวข้อ 5',
|
||||
'กรณีการสำรองข้อมูลล้มเหลวหรือไม่ทำงานตามกำหนด ผู้ดูแลระบบต้องตรวจสอบสาเหตุและสั่งทำงานซ้ำ',
|
||||
]),
|
||||
`การทดสอบกู้คืนดำเนินการเมื่อ ${thLong(OPS_CLOSE_DATE)} โดยกู้คืนชุดสำรองลงในสภาพแวดล้อมที่ไม่ใช่ระบบใช้งานจริงสำเร็จ ตรวจสอบการเข้าถึงฐานข้อมูลและข้อมูลตัวอย่างครบถ้วน และผู้จัดการโครงการได้ทบทวนผลการทดสอบในขั้นตอนปิดโครงการ`,
|
||||
`กำหนดทดสอบกู้คืนภายใน ${thLong(OPS_CLOSE_DATE)} โดยกู้คืนชุดสำรองลงในสภาพแวดล้อมที่ไม่ใช่ระบบใช้งานจริง ตรวจสอบการเข้าถึงฐานข้อมูลและข้อมูลตัวอย่างให้ครบถ้วน และให้ผู้จัดการโครงการทบทวนผลการทดสอบในขั้นตอนปิดโครงการ`,
|
||||
'## 7 การปิดรายการควบคุมด้านปฏิบัติการ',
|
||||
table(['ลำดับ', 'รายการควบคุม', 'ผู้รับผิดชอบ', 'สถานะ', 'หลักฐานการปิดรายการ'], [
|
||||
['OP-001', 'การสำรองและกู้คืนฐานข้อมูล `wms` และ `wms2` แบบอัตโนมัติ', 'System Administrator', `ปิดรายการ ${thLong(OPS_CLOSE_DATE)}`, 'หัวข้อ 6 ระบุรอบการสำรอง ที่จัดเก็บ การเก็บรักษา ขั้นตอนกู้คืน และผลการทดสอบกู้คืน'],
|
||||
['OP-002', 'การเฝ้าระวังระบบและการแจ้งเตือนเมื่อเกิดเหตุขัดข้อง', 'System Administrator', `ปิดรายการ ${thLong(OPS_CLOSE_DATE)}`, 'หัวข้อ 5 ระบุรอบการตรวจสอบ เงื่อนไขการแจ้งเตือน ผู้รับแจ้ง และขั้นตอนการยกระดับ'],
|
||||
['OP-001', 'การสำรองและกู้คืนฐานข้อมูล `wms` และ `wms2` แบบอัตโนมัติ', 'System Administrator', `กำหนดปิดรายการ ${thLong(OPS_CLOSE_DATE)}`, 'หัวข้อ 6 ระบุรอบการสำรอง ที่จัดเก็บ การเก็บรักษา ขั้นตอนกู้คืน และแผนการทดสอบกู้คืน'],
|
||||
['OP-002', 'การเฝ้าระวังระบบและการแจ้งเตือนเมื่อเกิดเหตุขัดข้อง', 'System Administrator', `กำหนดปิดรายการ ${thLong(OPS_CLOSE_DATE)}`, 'หัวข้อ 5 ระบุรอบการตรวจสอบ เงื่อนไขการแจ้งเตือน ผู้รับแจ้ง และขั้นตอนการยกระดับ'],
|
||||
], ['c']),
|
||||
'## 8 การจัดการผู้ใช้งานและสิทธิ์',
|
||||
bullets([
|
||||
@@ -453,7 +453,7 @@ export function validationResults() {
|
||||
['ไม่ผ่าน (Failed)', 0],
|
||||
['ค้างการทดสอบ', 0],
|
||||
], ['', 'r']),
|
||||
`ตัวแทนลูกค้าได้ทดสอบการยอมรับครบทุกความต้องการใน Customer Requirements บนสภาพแวดล้อมใช้งานจริง ผลการทดสอบผ่านทั้งหมด ไม่พบประเด็นที่ขัดขวางการเปิดใช้งาน จึงยืนยันความพร้อมของระบบสำหรับการตรวจรับส่งมอบ ซึ่งบันทึกไว้ใน Acceptance Report เมื่อ ${thLong(ACCEPT_DATE)}`,
|
||||
`ตัวแทนลูกค้าได้ทดสอบการยอมรับครบทุกความต้องการใน Customer Requirements บนสภาพแวดล้อมใช้งานจริง ผลการทดสอบผ่านทั้งหมด ไม่พบประเด็นที่ขัดขวางการเปิดใช้งาน จึงยืนยันความพร้อมของระบบและเสนอเข้าสู่การตรวจรับส่งมอบตามแผนในวันที่ ${thLong(ACCEPT_DATE)}`,
|
||||
].join('\n\n'),
|
||||
secretary: P.PaNg, reviewer: P.NoC, approver: P.SeV,
|
||||
}),
|
||||
|
||||
@@ -8,10 +8,10 @@ export const TASKS = [
|
||||
['1.2', 'Project Initiation', 'Identify Stakeholders', 'ระบุผู้มีส่วนได้ส่วนเสียและบทบาทความรับผิดชอบ', ['ApS', 'YaB'], '2026-01-12', '2026-01-16', 'Stakeholder Register', 'Completed', 'จัดทำ Stakeholder Register'],
|
||||
['1.3', 'Project Initiation', 'Define Vision & Objectives', 'กำหนดวัตถุประสงค์ ขอบเขตระบบ และเกณฑ์ความสำเร็จ', ['ApS', 'NoC'], '2026-01-19', '2026-01-21', 'Project Charter Report', 'Completed', 'อนุมัติวัตถุประสงค์โครงการ'],
|
||||
['1.4', 'Project Initiation', 'Approve Project Charter', 'ผู้บริหารอนุมัติกฎบัตรโครงการและขอบเขตงาน', ['SeV', 'ApS'], '2026-01-22', '2026-01-23', 'Approved Project Charter', 'Completed', 'อนุมัติโดย Project Sponsor'],
|
||||
['2.1', 'Project Planning', 'Collect Customer Requirements', 'เก็บและวิเคราะห์ความต้องการลูกค้า จัดหมวด CR01–CR14', ['NoC', 'ApS'], '2026-01-12', '2026-02-06', 'Customer Requirements', 'Completed', `ความต้องการ ${CRS.length} รายการ ผลสรุป A ทั้งหมด`],
|
||||
['2.1', 'Project Planning', 'Collect Customer Requirements', 'เก็บและวิเคราะห์ความต้องการลูกค้า และจัดหมวดความต้องการ', ['NoC', 'ApS'], '2026-01-12', '2026-02-06', 'Customer Requirements', 'Completed', `ความต้องการ ${CRS.length} รายการ ผลสรุป A ทั้งหมด`],
|
||||
['2.2', 'Project Planning', 'Develop Software Project Plan', 'จัดทำแผนโครงการ ทรัพยากร ความเสี่ยง และการควบคุม', ['ApS', 'YaB'], '2026-01-26', '2026-02-13', 'Software Project Plan', 'Completed', 'รวม Work Schedule'],
|
||||
['2.3', 'Project Planning', 'Baseline Requirements & Schedule', 'ทบทวนและตั้ง Baseline ความต้องการและแผนงาน', ['ApS', 'NoC'], '2026-02-16', '2026-02-18', 'Baselined Plan', 'Completed', 'เริ่มพัฒนา 19 ก.พ. 69'],
|
||||
['3.1', 'Project Execution', 'Software Requirements & Design', 'จัดทำ SRS (SR01–SR09) และเอกสารออกแบบระบบ', ['NoC', 'ThS'], '2026-02-19', '2026-02-25', 'SRS, Software Design', 'Completed', 'Git 9a50080–1843308'],
|
||||
['3.1', 'Project Execution', 'Software Requirements & Design', 'จัดทำ SRS และเอกสารออกแบบระบบ', ['NoC', 'ThS'], '2026-02-19', '2026-02-25', 'SRS, Software Design', 'Completed', 'Git 9a50080–1843308'],
|
||||
['3.2', 'Project Execution', 'Security & Database Foundation', 'ป้องกันไฟล์ตั้งค่า แก้ผลตรวจความปลอดภัย และออกแบบฐานข้อมูลสต๊อก', ['ThS'], '2026-03-09', '2026-03-17', 'Database schema, ICS modules', 'Completed', 'Git 93d903c–a4f474b'],
|
||||
['3.3', 'Project Execution', 'Inventory & Warehouse Modules', 'พัฒนาคลังสินค้า ความจุ สินค้า Lot/Serial/Expiry และรายงาน', ['ThS', 'NoC'], '2026-04-09', '2026-04-29', 'Inventory & report modules', 'Completed', 'Git 3b8f94f–db5c47b'],
|
||||
['3.4', 'Project Execution', 'Authentication & Onboarding', 'พัฒนาเข้าสู่ระบบ ลงทะเบียน Onboarding และสิทธิ์ตามบทบาท', ['ThS'], '2026-04-28', '2026-05-12', 'Authentication modules', 'Completed', 'Git d8fd30c–79e66bd'],
|
||||
@@ -135,7 +135,7 @@ export const LESSONS = [
|
||||
*/
|
||||
export const PERIODS = [
|
||||
['2026-01-09', '2026-01-05', '2026-01-09', 1, [['1.1', 'Completed', 100, 'เปิดโครงการอย่างเป็นทางการ ชี้แจงขอบเขตและทีมงานครบทุกบทบาท'], ['1.2', 'In Progress', 40, 'อยู่ระหว่างรวบรวมรายชื่อผู้มีส่วนได้ส่วนเสียและระดับการมีส่วนร่วม']], [], [], { date: '2026-01-16', topic: 'ทบทวนรายชื่อผู้มีส่วนได้ส่วนเสียและเริ่มกำหนดวัตถุประสงค์โครงการ' }],
|
||||
['2026-01-23', '2026-01-12', '2026-01-23', 2, [['1.2', 'Completed', 100, 'จัดทำ Stakeholder Register ครบ 6 บทบาทหลักและผู้ใช้งานที่เกี่ยวข้อง'], ['1.3', 'Completed', 100, 'กำหนดวัตถุประสงค์ ขอบเขตระบบ 11 ระบบงาน และเกณฑ์ความสำเร็จ'], ['1.4', 'Completed', 100, 'Project Charter ได้รับอนุมัติจาก Project Sponsor'], ['2.1', 'In Progress', 30, 'เริ่มเก็บความต้องการลูกค้าจากฝ่ายคลังสินค้า ขาย และบัญชี']], [], [], { date: '2026-02-06', topic: 'สรุปความต้องการลูกค้า CR01–CR14 และทบทวนร่างแผนโครงการ' }],
|
||||
['2026-01-23', '2026-01-12', '2026-01-23', 2, [['1.2', 'Completed', 100, 'จัดทำ Stakeholder Register ครบ 6 บทบาทหลักและผู้ใช้งานที่เกี่ยวข้อง'], ['1.3', 'Completed', 100, 'กำหนดวัตถุประสงค์ ขอบเขตระบบ 11 ระบบงาน และเกณฑ์ความสำเร็จ'], ['1.4', 'Completed', 100, 'Project Charter ได้รับอนุมัติจาก Project Sponsor'], ['2.1', 'In Progress', 30, 'เริ่มเก็บความต้องการลูกค้าจากฝ่ายคลังสินค้า ขาย และบัญชี']], [], [], { date: '2026-02-06', topic: 'สรุปความต้องการลูกค้าและทบทวนร่างแผนโครงการ' }],
|
||||
['2026-02-06', '2026-01-26', '2026-02-06', 3, [['2.1', 'Completed', 100, `สรุปความต้องการลูกค้า ${CRS.length} รายการ จัดหมวด CR01–CR14 และได้ผลสรุป A ทุกรายการ`], ['2.2', 'In Progress', 60, 'อยู่ระหว่างจัดทำ Software Project Plan และ Work Schedule']], [], [], { date: '2026-02-13', topic: 'ทบทวน Software Project Plan, Work Schedule และแผนความเสี่ยง' }],
|
||||
['2026-02-18', '2026-02-09', '2026-02-18', 4, [['2.2', 'Completed', 100, 'จัดทำแผนโครงการ ทรัพยากร ความเสี่ยง R1–R8 และแผนสำรองงานที่ไม่แล้วเสร็จ'], ['2.3', 'Completed', 100, 'ตั้ง Baseline ความต้องการและแผนงาน อนุมัติเริ่มพัฒนา 19 ก.พ. 69']], [], [], { date: '2026-02-25', topic: 'ติดตามการจัดทำ SRS, เอกสารออกแบบระบบ และการเริ่มพัฒนา' }],
|
||||
['2026-02-25', '2026-02-19', '2026-02-25', 5, [['3.1', 'Completed', 100, 'จัดทำ SRS (SR01–SR09) และเอกสารออกแบบระบบ เริ่มพัฒนาโครงสร้างแอปพลิเคชัน'], ['4.2', 'In Progress', 20, 'เริ่มควบคุม Source Code บน Git และกำหนด main เป็น Baseline']], [], [], { date: '2026-03-17', topic: 'ติดตามผลการแก้ไขด้านความปลอดภัยและการออกแบบฐานข้อมูลสต๊อก' }],
|
||||
@@ -148,7 +148,7 @@ export const PERIODS = [
|
||||
['2026-08-03', '2026-06-01', '2026-08-03', 12, [['4.3', 'In Progress', 70, 'ตรวจสอบเอกสารความต้องการ ออกแบบ และการสอบกลับ รอบที่ 1–3'], ['4.5', 'In Progress', 80, 'จัดทำคู่มือผู้ใช้ คู่มือผู้ดูแลระบบ และคู่มือบำรุงรักษา'], ['4.6', 'Completed', 100, 'แก้ไขการเข้าสู่ระบบและค่าตั้งค่าสภาพแวดล้อมหลัง Baseline']], ['ISS-028'], [], { date: '2026-08-14', topic: 'ติดตามผลการทดสอบระบบ UAT และข้อมูลสาธิต' }],
|
||||
['2026-08-14', '2026-08-04', '2026-08-14', 13, [['4.4', 'Completed', 100, `ทดสอบระบบ 45 Test Case และ UAT ครบ 14 หมวดความต้องการ (${CRS.length} รายการ) ผ่านทั้งหมดระหว่าง 10–14 ส.ค. 69`], ['4.7', 'In Progress', 60, 'จัดทำข้อมูลสาธิตเสร็จ อยู่ระหว่างปรับแบรนด์และชุดติดตั้ง Docker Compose'], ['5.1', 'In Progress', 70, 'ตรวจสอบความครบถ้วนของ Work Products']], [], [], { date: '2026-08-17', topic: 'สรุปผลการตรวจสอบรอบสุดท้าย และตรวจรับส่งมอบระบบ' }],
|
||||
['2026-08-17', '2026-08-15', '2026-08-17', 14, [['4.3', 'Completed', 100, 'ตรวจสอบ Work Products รอบที่ 4 ครบทุกรายการ ผลผ่านทั้งหมด'], ['4.5', 'Completed', 100, 'คู่มือผู้ใช้ คู่มือผู้ดูแลระบบ และคู่มือบำรุงรักษาเสร็จสมบูรณ์'], ['4.7', 'Completed', 100, 'ปรับแบรนด์และชุดติดตั้ง Docker Compose เสร็จ Baseline 6c39700'], ['5.1', 'Completed', 100, 'จัดทำ List of Evidence ครบทุก Work Product'], ['5.3', 'In Progress', 80, 'ลูกค้าตรวจรับระบบเมื่อ 17 ส.ค. 69 เหลือการอบรมและปิดโครงการ']], [], [], { date: '2026-08-24', topic: 'สรุปผลการอบรม การปิดงานควบคุมปฏิบัติการ และปิดโครงการ' }],
|
||||
['2026-08-24', '2026-08-18', '2026-08-24', 15, [['5.2', 'Completed', 100, 'อบรมผู้ใช้งาน 6 คน เมื่อ 22 ส.ค. 69 ผ่านการทดลองใช้งานครบทุกขั้นตอน'], ['5.3', 'Completed', 100, 'ปิดงานควบคุมปฏิบัติการ 23 ส.ค. 69 และปิดโครงการอย่างเป็นทางการ 24 ส.ค. 69'], ['4.1', 'Completed', 100, 'บันทึกความก้าวหน้าและรายงานการประชุมครบตลอดโครงการ'], ['4.2', 'Completed', 100, 'ควบคุมเวอร์ชันและสำรองข้อมูลครบ พร้อม Tag sdlc-v1.0-final']], [], [], null],
|
||||
['2026-08-24', '2026-08-18', '2026-08-24', 15, [['5.2', 'Completed', 100, 'อบรมผู้ใช้งาน 6 คน เมื่อ 22 ส.ค. 69 ผ่านการทดลองใช้งานครบทุกขั้นตอน'], ['5.3', 'Completed', 100, 'ปิดงานควบคุมปฏิบัติการ 23 ส.ค. 69 และปิดโครงการอย่างเป็นทางการ 24 ส.ค. 69'], ['4.1', 'Completed', 100, 'บันทึกความก้าวหน้าและรายงานการประชุมครบตลอดโครงการ'], ['4.2', 'Completed', 100, 'ควบคุมเวอร์ชันและสำรองข้อมูลครบ']], [], [], null],
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -171,7 +171,7 @@ export const MEETINGS = [
|
||||
progress: [['Phase 1: Project Initiation', 'เสร็จสมบูรณ์', 100, 'อนุมัติ Project Charter และขอบเขตงานครบถ้วน'], ['Phase 2: Project Planning', 'กำลังดำเนินการ', 30, 'อยู่ระหว่างเก็บความต้องการลูกค้า'], ['รวมความคืบหน้าทั้งโครงการ', 'ตามแผน', 12, 'เข้าสู่ขั้นตอนวางแผนโครงการตามกำหนด']],
|
||||
agenda: [['พิจารณาอนุมัติกฎบัตรโครงการ (Project Charter)', 'ยืนยันวัตถุประสงค์ ขอบเขต และงบประมาณ'], ['ทบทวน Stakeholder Register', 'ยืนยันบทบาทและระดับการมีส่วนร่วม'], ['แผนการเก็บความต้องการลูกค้า', 'กำหนดผู้ให้ข้อมูลและกรอบเวลา']],
|
||||
discussion: [['อนุมัติกฎบัตรโครงการ', 'Project Sponsor อนุมัติวัตถุประสงค์ ขอบเขต 11 ระบบงาน และเกณฑ์ความสำเร็จ', ['SeV'], 'อนุมัติ'], ['ผู้มีส่วนได้ส่วนเสีย', 'ยืนยันผู้รับผิดชอบตาม RACI ครบทุกบทบาท', ['ApS', 'YaB'], 'ยืนยันแล้ว'], ['การเก็บความต้องการ', 'มอบหมาย System Analyst สัมภาษณ์ฝ่ายคลังสินค้า ขาย จัดซื้อ และบัญชี', ['NoC'], 'ดำเนินการ']],
|
||||
actions: [['เก็บและสรุปความต้องการลูกค้า CR01–CR14 (Task 2.1)', ['NoC', 'ApS'], '2026-02-06', 'Scheduled'], ['จัดทำร่าง Software Project Plan และ Work Schedule (Task 2.2)', ['ApS'], '2026-02-13', 'Scheduled']],
|
||||
actions: [['เก็บและสรุปความต้องการลูกค้า (Task 2.1)', ['NoC', 'ApS'], '2026-02-06', 'Scheduled'], ['จัดทำร่าง Software Project Plan และ Work Schedule (Task 2.2)', ['ApS'], '2026-02-13', 'Scheduled']],
|
||||
next: { date: '2026-02-06', topic: 'สรุปความต้องการลูกค้าและทบทวนร่างแผนโครงการ' },
|
||||
},
|
||||
{
|
||||
@@ -260,7 +260,7 @@ export const MEETINGS = [
|
||||
attachment: 'Training Report, Product Operation Guide, Project Repository (Backup)',
|
||||
progress: [['Phase 1: Project Initiation', 'เสร็จสมบูรณ์', 100, 'เปิดโครงการและอนุมัติกฎบัตรเรียบร้อย'], ['Phase 2: Project Planning', 'เสร็จสมบูรณ์', 100, 'อนุมัติแผนโครงการและ Baseline ความต้องการ'], ['Phase 3: Project Execution', 'เสร็จสมบูรณ์', 100, 'พัฒนาระบบครบตามขอบเขตและเปิดใช้งานจริง'], ['Phase 4: Verification & Validation', 'เสร็จสมบูรณ์', 100, 'ตรวจสอบ ทดสอบ และ UAT ผ่านครบทุกรายการ'], ['Phase 5: Project Close', 'เสร็จสมบูรณ์', 100, 'อบรม ปิดงานควบคุมปฏิบัติการ และปิดโครงการเรียบร้อย'], ['รวมความคืบหน้าทั้งโครงการ', 'ตามแผน', 100, 'โครงการเสร็จสมบูรณ์ ปิดโครงการอย่างเป็นทางการ']],
|
||||
agenda: [['สรุปผลการอบรมผู้ใช้งาน', 'ยืนยันความพร้อมของผู้ใช้ก่อนเปิดใช้งานจริง'], ['สรุปการปิดงานควบคุมการสำรองข้อมูลและการเฝ้าระวัง', 'ยืนยันการปิดรายการ OP-001 และ OP-002'], ['สรุปการสำรอง Repository และเอกสารโครงการ', 'ยืนยันการสำรองและกู้คืนได้จริง'], ['ปิดโครงการอย่างเป็นทางการ', 'ลงมติปิดโครงการและส่งมอบเอกสาร']],
|
||||
discussion: [['ผลการอบรม', 'อบรมผู้ใช้งาน 6 คน เมื่อ 22 ส.ค. 69 ผู้เข้าอบรมทดลองใช้งานครบทุกขั้นตอนได้สำเร็จ', ['ThS', 'PaNg'], 'เสร็จสิ้น'], ['การควบคุมปฏิบัติการ', 'ปิดรายการสำรองฐานข้อมูลอัตโนมัติ (OP-001) และการเฝ้าระวังพร้อมแจ้งเตือน (OP-002) เมื่อ 23 ส.ค. 69', ['ThS'], 'เสร็จสิ้น'], ['การสำรองข้อมูลโครงการ', 'ซิงก์ Branch และ Tag sdlc-v1.0-final ไปยัง Backup Remote และทดสอบเรียกคืนสำเร็จ', ['ThS', 'YaB'], 'เสร็จสิ้น'], ['การปิดโครงการ', 'ที่ประชุมมีมติปิดโครงการอย่างเป็นทางการเมื่อ 24 ส.ค. 69', ['SeV', 'ApS'], 'ปิดโครงการ']],
|
||||
discussion: [['ผลการอบรม', 'อบรมผู้ใช้งาน 6 คน เมื่อ 22 ส.ค. 69 ผู้เข้าอบรมทดลองใช้งานครบทุกขั้นตอนได้สำเร็จ', ['ThS', 'PaNg'], 'เสร็จสิ้น'], ['การควบคุมปฏิบัติการ', 'ปิดรายการสำรองฐานข้อมูลอัตโนมัติ (OP-001) และการเฝ้าระวังพร้อมแจ้งเตือน (OP-002) เมื่อ 23 ส.ค. 69', ['ThS'], 'เสร็จสิ้น'], ['การสำรองข้อมูลโครงการ', 'ซิงก์ Branch ไปยัง Backup Remote และทดสอบเรียกคืนสำเร็จ', ['ThS', 'YaB'], 'เสร็จสิ้น'], ['การปิดโครงการ', 'ที่ประชุมมีมติปิดโครงการอย่างเป็นทางการเมื่อ 24 ส.ค. 69', ['SeV', 'ApS'], 'ปิดโครงการ']],
|
||||
actions: [['จัดเก็บเอกสารโครงการทั้งหมดเข้าระบบควบคุมเอกสาร', ['YaB'], '2026-08-24', 'เสร็จสมบูรณ์'], ['ติดตามการตรวจนับสต๊อกสิ้นเดือนแรกหลังเปิดใช้งานจริง', ['ApS'], '2026-09-30', 'Scheduled']],
|
||||
next: null,
|
||||
},
|
||||
|
||||
@@ -42,7 +42,7 @@ export const WPS = [
|
||||
['WP 6.0', `เอกสาร ${CODE} Test Case and Test Procedures`, 'ส่งเอกสารจำนวน 1 ชุด'],
|
||||
['WP 7.0', `เอกสาร ${CODE} Validation Results`, 'ส่งเอกสารจำนวน 1 ชุด'],
|
||||
['WP 8.0', `เอกสาร ${CODE} Software User Document`, 'ส่งเอกสารจำนวน 1 ชุด'],
|
||||
['WP 9.0', 'ระบบ BRN WMS ที่ผ่านการทดสอบพร้อมนำไปใช้งานจริง', `ส่ง Source Code (Git baseline ${BASELINE_COMMIT}) และติดตั้ง`],
|
||||
['WP 9.0', 'ระบบ BRN WMS ที่ผ่านการทดสอบพร้อมนำไปใช้งานจริง', 'ส่ง Source Code ผ่าน Git Repository และติดตั้ง'],
|
||||
['WP 10.0', `เอกสาร ${CODE} Product Operation Guide`, 'ส่งเอกสารจำนวน 1 ชุด'],
|
||||
['WP 11.0', `เอกสาร ${CODE} Maintenance Document`, 'ส่งเอกสารจำนวน 1 ชุด'],
|
||||
];
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
// Hindsight guard: a work product may only cite what existed on its own date.
|
||||
// A plan dated 13 Feb cannot quote the commit that was delivered on 17 Aug, a test case
|
||||
// dated 31 Jul cannot record results from the 10–14 Aug test run, and so on.
|
||||
// Planned dates in the future are fine; facts that only came into being later are not.
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { TAG } from './project.mjs';
|
||||
import { CORRECTIONS } from './history.mjs';
|
||||
|
||||
/** Document date from its name: "... 25690817 V1.0 ..." -> 2026-08-17 */
|
||||
export function docDate(name) {
|
||||
const m = name.match(/ (25\d\d)(\d\d)(\d\d) V/);
|
||||
if (!m) throw new Error(`No date in document name: ${name}`);
|
||||
return `${+m[1] - 543}-${m[2]}-${m[3]}`;
|
||||
}
|
||||
|
||||
function git(root, args) {
|
||||
try { return execFileSync('git', ['-C', root, ...args], { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim(); } catch { return null; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifier families and the work product that first defines them. An ID may be cited only
|
||||
* on or after the date of that work product.
|
||||
*/
|
||||
const FAMILIES = [
|
||||
{ label: 'Customer Requirement', re: /\bCR\d\d(?::\d{3})?\b/g, definedBy: 'Customer Requirements' },
|
||||
{ label: 'Software Requirement', re: /\bSR\d\d(?::\d{3})?\b/g, definedBy: 'Software Requirements' },
|
||||
{ label: 'Software Unit', re: /(?<!TC-)\bUN\d\d(?:\.\d{3})?\b/g, definedBy: 'Software Design' },
|
||||
{ label: 'Test Case', re: /\bTC-UN\d\d\.\d{3}\b/g, definedBy: 'Test Case and Test Procedures' },
|
||||
];
|
||||
|
||||
/** Returns a list of "document: problem" strings; empty when every document is consistent with its date. */
|
||||
export function hindsightErrors(docs, root) {
|
||||
const errors = [];
|
||||
const hasGit = git(root, ['rev-parse', '--git-dir']) !== null;
|
||||
const commitDates = new Map();
|
||||
const commitDate = (h) => {
|
||||
if (!commitDates.has(h)) commitDates.set(h, git(root, ['log', '-1', '--format=%ad', '--date=short', `${h}^{commit}`]) || null);
|
||||
return commitDates.get(h);
|
||||
};
|
||||
const tagDate = hasGit ? git(root, ['for-each-ref', `refs/tags/${TAG}`, '--format=%(creatordate:short)']) || null : null;
|
||||
const detected = new Map(CORRECTIONS.map((c) => [c[0], c[1]]));
|
||||
const firstDefined = new Map();
|
||||
for (const f of FAMILIES) {
|
||||
const dates = docs.filter((d) => d.name.includes(` ${f.definedBy} `)).map((d) => docDate(d.name)).sort();
|
||||
if (!dates.length) throw new Error(`No work product defines ${f.label} IDs (${f.definedBy})`);
|
||||
firstDefined.set(f, dates[0]);
|
||||
}
|
||||
|
||||
for (const d of docs) {
|
||||
const D = docDate(d.name);
|
||||
const found = new Set();
|
||||
const flag = (what) => found.add(what);
|
||||
if (hasGit) {
|
||||
for (const [, h] of d.md.matchAll(/(?<![\w/.-])([0-9a-f]{7,40})(?![\w/.-])/g)) {
|
||||
const c = commitDate(h);
|
||||
if (c && c > D) flag(`commit ${h} (created ${c})`);
|
||||
}
|
||||
}
|
||||
if (d.md.includes(TAG) && !(tagDate && tagDate <= D)) flag(`tag ${TAG} (${tagDate ? `created ${tagDate}` : 'not created yet'})`);
|
||||
for (const [id] of d.md.matchAll(/\bISS-\d{3}\b/g)) {
|
||||
const at = detected.get(id);
|
||||
if (!at) flag(`${id} is not in the Correction Register`);
|
||||
else if (at > D) flag(`${id} (detected ${at})`);
|
||||
}
|
||||
for (const f of FAMILIES) {
|
||||
const at = firstDefined.get(f);
|
||||
if (at <= D) continue;
|
||||
const ids = [...new Set([...d.md.matchAll(f.re)].map((m) => m[0]))];
|
||||
if (ids.length) flag(`${f.label} IDs ${ids.slice(0, 3).join(', ')}${ids.length > 3 ? ', …' : ''} (defined ${at})`);
|
||||
}
|
||||
for (const what of found) errors.push(`${d.name}: ${what}`);
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
Reference in New Issue
Block a user