用 Markdown+Git+CI 将项目管理手册变成可校验工程资产

用 Markdown+Git+CI 将项目管理手册变成可校验工程资产 简介这份《项目管理手册》是企业级项目管理制度文档面向项目经理、PMO 及质量、财务等职能部门负责人用于统一从前期立项到结项后评估的全流程操作规范。包内为 1 个 doc 文件体积约 1.01MB可直接编辑套用也可作为企业内部制度模板修订。手册按十三章展开总则给出管理思路与近期、长期目标职责分工划定项目质量部、销售部、物资部、财务部等九个角色的边界正文依次覆盖项目整体、范围、进度、成本、质量、人力资源、沟通、风险、采购与变更等模块。其中立项与 ERP 创建要求、双周报与月度经营数据分析、分类型项目的进度管理方式、预算编制与挣值分析、自风险识别到跟踪的闭环流程、六类变更的分类处置及进度与成本绩效指标均可直接对照落地。目前已有 703 人学习下载适合需要搭建或对标项目管理体系的项目经理与制度编写人员参考。1. 手册发了三年新项目经理还是靠问人有个很常见的场景一家做政企交付的信息技术公司项目管理手册.doc躺在共享盘里最近一次修改时间是三年前。新来的项目经理问「客户临时加一个报表要不要走变更单」老同事的答案是「你去翻手册第 4 章有」然后两个人一起翻了十分钟翻到的是上一版组织架构下的审批人名字。问题不在手册写得不好而在这份手册是个死物没有负责人、没有版本号、没有生效日期、没有更新机制。它是一份 Word 附件不是一套工程资产。只要它还是二进制文件任何一次修订都要「谁有最终版」的扯皮任何一条流程都不可能被自动校验项目档案合不合规只能靠人眼看。把手册当代码管是唯一能长期活下来的做法Markdown 写源文件Git 存版本pandoc 编译成交付用的 .docCI 定时扫过期条款存档目录用脚本查合规。适合 10 到 200 人规模、同时跑几个交付项目的技术团队PMO、技术负责人、研发经理都能直接抄。2. 用 Markdown 加 Git 重建项目管理手册的源文件结构2.1 把一个大 .doc 拆成六类文件一个几百页的单文件手册检索成本极高改一处要重排目录两个人同时改必然冲突。拆成按主题分文件的仓库结构每份文件有独立负责人和复核时间改动粒度才对得上。目录内容典型 Owner复核周期00_总则/适用范围、角色职责、术语表PMO12 个月10_流程/立项、需求、设计、提测、上线、验收六个门禁交付总监6 个月20_模板/WBS、风险登记册、变更单、验收单PMO12 个月30_度量/工时口径、缺陷密度、里程碑偏差定义质量负责人6 个月40_工具/缺陷系统字段约定、分支与发布规则研发负责人6 个月50_附录/历史修订记录、废止条款清单PMO随改随记拆分的原则是「谁签字谁负责」一个文件的 owner 只能有一个复核时间只允许提前不允许推后。术语表必须单独成文件因为「迭代」「版本」「发布」这三个词在不同团队里含义完全不同口径不统一后面所有度量都是白算。2.2 用 pandoc 把 Markdown 编译成交付版 .doc客户和评审专家要的还是 .doc签字的也是 .doc所以源文件用 Markdown、交付物仍然编译成 Word。常见做法是用 pandoc 加一个公司样式模板# 仓库根目录执行build/ 与 templates/ 的约定见上一节 pandoc \ manual/*.md manual/**/*.md \ -o build/信息技术有限公司项目管理手册-$(git describe --tags --always).docx \ --reference-doctemplates/reference.docx \ --toc --toc-depth3 \ --number-sections \ --metadata title信息技术有限公司项目管理手册 \ --metadata version$(git describe --tags --always) \ --resource-pathmanual:assets参数逐个说明--reference-doc指定样式模板字体、页眉、标题级别全部继承公司模板避免每次编译出来样式漂移--toc-depth3只生成到三级标题不然目录能占三页--number-sections让章节号参与交叉引用正文里写「见 10.2 节」不会因为插入新章节而失效--metadata version把 Git 标签打进封面这是追溯的唯一抓手评审专家问「你看的是哪版」时能立刻答上来--resource-path解决图片相对路径找不到的问题。注意编译产物写进build/并加入.gitignore仓库里只留源文件和模板。谁提交编译产物谁就是在制造「哪个 docx 才是最新的」这个老问题。2.3 用 Git 钩子拦住没有负责人的条款手册失效的根因是没有 owner。与其靠人自觉不如在提交阶段直接拒绝#!/usr/bin/env bash # .githooks/pre-commit 启用git config core.hooksPath .githooks set -euo pipefail fail0 # 只看暂存区里新增或修改的 manual 下 Markdown 文件 for f in $(git diff --cached --name-only --diff-filterACM | grep ^manual/.*\.md$ || true); do head -n 10 $f | grep -q ^owner: || { echo [缺少 owner] $f; fail1; } head -n 10 $f | grep -q ^review_by: || { echo [缺少 review_by] $f; fail1; } head -n 10 $f | grep -q ^version: || { echo [缺少 version] $f; fail1; } done exit $fail逻辑很直白每个条款文件的前十行必须出现三个前置字段。head -n 10限制了元数据区长度防止有人把字段塞到正文里糊弄检查。grep -q只判断存在不输出|| true保证没有匹配文件时管道不报错。退出码非零时 Git 直接拒绝提交作者会看到具体缺哪个字段、哪份文件。启用后本地效果是即时的但钩子可以被--no-verify绕过所以同一条检查要在 CI 里再跑一遍命令完全一样只是把git diff --cached换成对比目标分支。2.4 定时任务扫出过期条款给每个文件写好review_by: 2025-06-30之后还要有人催。用一段不依赖任何语言的 Shell 就够# scripts/check_expired.sh 挂在 CI 的每日定时任务里 today$(date %F) grep -rn ^review_by: manual/ | while IFS: read -r file line value; do due$(echo $value | tr -d ) # 去掉冒号后的空格 if [[ $due $today ]]; then echo ::warning file$file,line$line::条款已过期需复核$due fi doneYYYY-MM-DD格式的字符串比较在字典序上等价于日期比较不需要引入date -d这类平台差异大的用法。::warning file...是 CI 平台的注解格式过期条款会直接标在对应的代码行上而不是淹没在日志里。跑三个月之后这份过期清单本身就是「手册修订计划」的输入PMO 不用再拍脑袋决定今年改哪几章。3. 项目管理手册里必须写死的门禁与参数3.1 六个门禁与放行条件流程章最容易写虚写成「立项阶段需充分评估」就等于没写。可执行的门禁必须回答四件事触发条件、必须产出、谁签字、不通过怎么办。门禁触发条件必须产出放行人未通过的处理G1 立项商机转交付意向立项申请、资源与成本评估交付总监退回售前补范围G2 需求冻结需求评审完成需求规格、需求基线产品加技术负责人打回不进迭代G3 设计定稿技术方案评审概要设计、接口清单、数据模型架构师高风险项列备选方案G4 提测开发自测通过提测单、单测覆盖率报告测试负责人打回开发记录提测失败次数G5 上线验收环境验证通过上线清单、回滚方案、发布窗口运维加项目经理顺延下一个发布窗口G6 验收结项稳定运行 14 天验收单、复盘报告、度量数据客户加交付总监转运维期整改挂账「不通过怎么办」这一列比前三列都重要。很多手册只写通过标准结果卡住的时候没人知道该退回还是该特批最后都变成特批。3.2 变更走不走单用工作量占比定阈值变更流程是手册里被绕过最多的一章因为阈值写成了「重大变更」这种形容词。改成量化规则写进 YAML 配置再补一段判定代码# change_gate.py 变更分级按人天占比 影响面判定 RULES [ # (名称, 占比上限, 影响已承诺里程碑, 触碰对外接口, 分级) (微变更, 0.05, False, False, C), (小变更, 0.15, False, False, B), (中变更, 0.30, True, False, A), (大变更, 1.00, True, True, S), ] def grade(effort_days: float, sprint_days: float, hits_milestone: bool, touches_api: bool) - str: ratio effort_days / sprint_days # 变更工作量占迭代可用容量 for name, cap, m, a, level in RULES: if ratio cap and hits_milestone m and touches_api a: return f{level}-{name} return S-大变更 print(grade(6, 40, True, True)) # 输出 S-大变更参数说明effort_days是评估后的总人天必须按开发加测试加联调合并计算只算开发人天是最常见的低估来源sprint_days是迭代内可投入的有效人天需要先扣掉请假、例行会议和线上值班三个占比阈值 5%/15%/30% 是二十人以下团队的经验值团队越大阈值应当越小因为跨项目协调成本上升。C 级口头确认、B 级项目经理审批、A 级变更评审会、S 级加合同补充协议这条对应关系也要写进手册别只写阈值不写动作。3.3 度量口径要用 SQL 写死口径不写死每月复盘必然吵架。手册附录里直接放可执行的查询报表照着跑-- 手册附录 A项目度量口径字段名按团队实际库表调整 SELECT p.project_code, -- 交付工时只统计研发、测试、设计管理成本不计入 SUM(CASE WHEN w.work_type IN (dev,test,design) THEN w.hours ELSE 0 END) AS dev_hours, -- 缺陷密度每千行变更代码对应的上线后 30 天内缺陷数 ROUND(1000.0 * COUNT(DISTINCT CASE WHEN d.found_stage prod AND d.created_at p.release_at INTERVAL 30 days THEN d.id END) / NULLIF(SUM(c.added_lines), 0), 2) AS defect_per_kloc, -- 里程碑偏差正数代表延期天数 MAX(m.actual_date - m.plan_date) AS milestone_slip_days FROM project p JOIN worklog w ON w.project_id p.id JOIN commit c ON c.project_id p.id LEFT JOIN defect d ON d.project_id p.id LEFT JOIN milestone m ON m.project_id p.id GROUP BY p.project_code;三处口径要点缺陷只算prod阶段发现的测试阶段发现的不计入否则测试做得越认真数字越难看30 天窗口是权衡值太短会把慢暴露的问题漏掉太长会让当期报表迟迟不能定稿NULLIF防止没有代码变更的项目除零报错。milestone_slip_days用正数表示延期、负数表示提前符号约定必须写进手册不然同一张报表有人读反。4. 手册配套模板与自动校验4.1 流程章配模板模板配必填字段手册写的是「要做什么」模板承接「做完留下什么」。每个门禁至少对应一份模板模板里每个字段都要能追溯到一条流程要求否则字段迟早被填成「无」。手册章节模板文件必填字段校验方式10.1 立项立项申请.md预算、人力、交付日期、验收标准字段存在性10.2 需求需求规格.md需求编号、优先级、验收口径编号连续性10.4 风险风险登记册.md概率、影响、应对人、关闭日期开放项超期告警10.6 验收验收单.md验收项、结论、签字、遗留清单遗留项必须有负责人4.2 用 Python 校验项目归档是否合规结项前跑一次扫描把「靠人翻」变成「靠脚本查」# audit_project.py 检查项目归档目录是否符合手册第 10 章 from pathlib import Path REQUIRED { 01_立项: [立项申请.md, 资源评估.xlsx], 02_需求: [需求规格.md, 需求评审记录.md], 03_计划: [WBS.md, 风险登记册.md], 05_验收: [验收单.md, 上线清单.md], } FIELDS [owner, review_by, version] # 与 pre-commit 保持一致 def audit(root: str) - list[str]: issues, base [], Path(root) for folder, files in REQUIRED.items(): for name in files: p base / folder / name if not p.exists(): issues.append(f缺失 {folder}/{name}) continue if p.suffix .md: head p.read_text(encodingutf-8).splitlines()[:12] for f in FIELDS: if not any(line.startswith(f :) for line in head): issues.append(f{p} 缺少字段 {f}) return issues for i in audit(projects/P-2024-013): print(i)REQUIRED用字典而不是列表是为了出问题时报出目录路径而不是干巴巴的文件名FIELDS和前面钩子里的字段保持一致一套元数据只管一件事只读前 12 行做字段检查避免大文件全量读取。这段脚本最容易踩的坑是编码Windows 上生成的 Markdown 常带 BOMencodingutf-8解析后首行会多出不可见字符导致owner:匹配失败改成utf-8-sig更稳。4.3 评审节奏与修订留痕季度小修只动参数和责任人年度大修才动流程结构这两件事必须分开否则每季度都掀一次桌子团队会彻底不认这份手册。每次修订在50_附录/修订记录.md里留一行格式固定为「版本 / 生效日期 / 改动章节 / 改动原因 / 批准人」禁止写「优化若干措辞」这类无信息量的记录。废止条款不要直接删移到废止清单里标注失效日期因为历史项目复盘时还要引用当时的规则。5. 进阶用手册字段自动生成项目健康度周报到了这一步项目档案里的元数据已经齐了周报就没必要手工拼。思路是让每个项目目录下维护一份status.yaml字段名和手册附录里的度量口径一一对应再用一段脚本渲染成 Markdown 表格贴进例会文档。# weekly_report.py 从各项目 status.yaml 汇总健康度 import yaml, datetime from pathlib import Path today datetime.date.today().isoformat() rows [] for f in Path(projects).glob(*/status.yaml): s yaml.safe_load(f.read_text(encodingutf-8-sig)) # 偏差率 (已消耗人天 - 计划人天) / 计划人天正数代表超支 burn_gap (s[spent_days] - s[plan_days]) / s[plan_days] slip (datetime.date.fromisoformat(s[forecast_date]) - datetime.date.fromisoformat(s[commit_date])).days # 三级灯任一指标越界即降级不做加权平均 light 红 if (burn_gap 0.15 or slip 5) else (黄 if (burn_gap 0.05 or slip 0) else 绿) rows.append((s[code], f{burn_gap:.1%}, slip, len(s.get(open_risks, [])), light)) print(f| 项目 | 工时偏差 | 预计延期(天) | 未关闭风险 | 灯 |) print(| --- | --- | --- | --- | --- |) for r in sorted(rows, keylambda x: x[4]): print(f| {r[0]} | {r[1]} | {r[2]} | {r[3]} | {r[4]} |) print(f\n生成时间{today})关键设计是灯色用「任一指标越界即降级」不做加权平均。加权平均会把一个延期十天的项目和三个轻微超支的项目混成同一个分数例会上照样吵。工时偏差 15%、延期 5 天这两条红线直接抄手册里的门禁参数改手册时只改一个地方。open_risks从风险登记册里同步未关闭风险数超过三条的项目自动进黄灯逼着项目经理在例会上给出关闭计划。跑顺之后可以再往前一步把输出直接落到 CI 的定时任务里每周一早上生成并推送到例会文档仓库status.yaml的owner、review_by字段沿用第 2 章那套前置元数据过期未更新的项目在报表里加一行提示。这样手册里的每一条流程要求最终都会变成报表上的一列写不写、填不填一眼就能看出来。本文还有配套的精品资源点击获取