docs: book P15-A as delivered (wrapper 0.3.8, ROADMAP rows + third-wave table)
P15-A merged to crearte-server master as e70e99b (0.19.0, merge-base fe810dd, eight commits on the branch). Both review rounds came back with notes and both caught the controller's own verification method rather than the refactor: the gofmt gate in the shared four-gate recipe was exit-code blind, and identifier counting cannot prove behaviour is unchanged (whole files rather than function bodies, a hand-picked list read as a universal claim, and no visibility into control flow at all). The branch review's most valuable contribution was an evidence layer all four earlier runs had silently skipped: the five real-database guards the spec names were SKIPPED everywhere because nobody set TEST_DATABASE_URL, and the ~11s internal/api timing reported as "including real-database integration" was the mock path. Running postgres against both trees gave base 194 PASS versus HEAD 199 PASS with identical state multisets - the first runtime proof of unchanged behaviour on the production database path. Five of its notes concerned controller artefacts, not code. Four were spec staleness introduced during re-pin, every one from writing executable details from memory instead of grepping them out of the code; the fifth was the fake-rigour form of the vacuous-assertion defect this project keeps finding - a verification script printing nineteen [OK] lines with no checking logic behind them while citing an output file that was never archived. Both are now fixed in37a0a8dand9061c39, and the lesson is a spec discipline: executable details in a spec must be grepped from the code entity and pasted, never hand-written. ROADMAP also gains a correction of my own omission: P15-A and P15-B were registered in the document index but never added to the third-wave status table, which still ended at P14. P15-B is deliberately excluded from this entry. Its branch review returned "needs fixing before merge" - the second time a branch review has blocked a batch after P11 - on a critical gap in the guard layer: maskSourceComments() compares a two-character slice against the four-character '<!--', so its HTML comment branch is dead code. A second fix-forward is in flight; P15-B books as 0.3.9 when it merges.
This commit is contained in:
@@ -6,6 +6,31 @@ All notable changes to this repository should be documented in this file.
|
||||
The format loosely follows Keep a Changelog and can be adapted to the team's habits.
|
||||
本文档参考了 Keep a Changelog 的思路,也可以根据团队习惯调整。
|
||||
|
||||
## [0.3.8] - 2026-10-04
|
||||
|
||||
### Done / 完成
|
||||
|
||||
- P15-A splits `Approve` in crearte-server (0.19.0, merge `e70e99b`, merge-base `fe810dd`, 8 commits on the branch): a 169-line function body becomes a 24-line orchestrator plus eight named helpers, and the batch's only acceptance criterion — zero behaviour change — is evidenced at statement level (of 162 statement lines, 149 verbatim, 13 changed with a registered reason each, 0 missing, 0 order violations) rather than by counting identifiers. Two new guard files land: a function-length guard whose threshold of 100 rests on a survey of all 78 functions in the package (`Approve` at 169 was the only one above it, the next largest being `deleteAccountLocked` at 83), and a phase-independence guard pinning that the optimistic precheck and the pessimistic in-transaction recheck stay separate — the property the length guard structurally cannot see.
|
||||
- P15-A 在 crearte-server 拆分 `Approve`(0.19.0,merge `e70e99b`,merge-base `fe810dd`,分支 8 个 commit):169 行的函数体变成 24 行编排体加八个具名 helper,而本批唯一验收判据——零行为变更——用语句级证据支撑(162 条语句行中 149 逐字保留、13 条变更且每条有登记理由、0 缺失、0 乱序),不是靠数标识符。落地两个新守卫文件:函数长度守卫(阈值 100 的依据是全包 78 个函数的普查——169 行的 `Approve` 是唯一超标者,次大的 `deleteAccountLocked` 是 83 行),以及钉住「乐观预检与事务内悲观重检保持分离」这个阶段独立性守卫,而那正是长度守卫在结构上看不见的性质。
|
||||
|
||||
- **The review ladder again produced the batch's main result rather than the refactor: both rounds caught the controller's own verification method.** Task-level review (PASS with notes, 12 findings from 18 self-designed mutations) proved two things. First, the gofmt gate in the shared four-gate recipe was exit-code blind — `gofmt -l .` *lists* unformatted files and still exits 0 — so the reported "gofmt clean" evidenced nothing beyond the absence of parse errors; controller and implementer used the same recipe, which is why one blind spot was computed twice and caught by neither. The recipe is now `test -z "$(gofmt -l .)"` with the raw output retained. Second, "ten identifiers count the same" cannot prove behaviour is unchanged: it counted whole files rather than function bodies, its hand-picked list carried no completeness argument yet read as a universal claim, and counting cannot see control flow at all — the reviewer's counterexample moves `GetByIDForUpdate` from phase six into phase four, leaving every count and every error string identical while destroying the TOCTOU layering, and all three shipped guards stayed green on it.
|
||||
- **审查阶梯再次把本批的主要产出放在了重构之外:两轮都抓到了控制者自己的核实方法。** 任务级审查(PASS with notes,18 轮自设 mutation 挖出 12 条 findings)证明了两件事。第一,共用四门配方里的 gofmt 门是退出码盲的——`gofmt -l .` **列出**未格式化文件而仍然 exit 0——所以报出的「gofmt 干净」除了「没有解析错误」之外什么也证明不了;控制者与实现者用的是同一个配方,这正是同一盲点被复算两次而谁都没发现的原因。配方现在改为 `test -z "$(gofmt -l .)"` 并保留原始输出。第二,「十个标识符计数相同」证明不了行为未变:它数的是整文件而非函数体,手挑的清单没有完备性论证却被读成全称声明,而计数根本看不见控制流——审查者的反例把 `GetByIDForUpdate` 从阶段六搬进阶段四,每个计数与每条错误文案都不变,而 TOCTOU 分层被摧毁,三条既有守卫在它面前全绿。
|
||||
|
||||
- Branch-level review returned **APPROVE with notes**: the merged branch code has zero defects, and all eleven of its findings were adjudicated before merge (five fixed, three ledgered with measured reasons, two fixed as spec/evidence corrections, one accepted with reason). Its most valuable contribution was an evidence layer all four earlier runs had silently skipped — the five real-database guards the spec names, two of them concurrency/TOCTOU tests, were SKIPPED everywhere because nobody set `TEST_DATABASE_URL`, and the ~11s `internal/api` timing that had been reported as "including real-database integration" was the mock path. Running `postgres:16-alpine` against both trees gave base 194 PASS / 0 FAIL versus HEAD 199 PASS / 0 FAIL (+5 being exactly this batch's new guards) with identical state multisets: the first runtime proof of unchanged behaviour on the production database path, where everything before it had been static.
|
||||
- 全分支终审返回 **APPRO with notes**:被合并的分支代码零缺陷,11 条 findings 在合并前逐条裁定(5 条已修、3 条带实测理由挂账、2 条作为 spec/证据更正已修、1 条 accepted with reason)。它最有价值的贡献是补上了此前四轮跑批都静默跳过的一层证据——spec 点名的 5 个真库守卫(其中两个是并发/TOCTOU 测试)在每一轮里都 SKIP,因为没人设 `TEST_DATABASE_URL`;而那个被报成「含真库集成」的 `internal/api` ~11s 其实是 mock 路径耗时。用 `postgres:16-alpine` 对两棵树各跑全量得 base 194 PASS / 0 FAIL vs HEAD 199 PASS / 0 FAIL(+5 恰为本批新增守卫),状态多重集完全一致:这是「行为未变」在生产数据库路径上的第一份运行时证明,此前所有证据都是静态的。
|
||||
|
||||
- Five of its notes were about the controller's own artefacts rather than the code. Four were spec staleness introduced during re-pin, all from writing executable details from memory instead of grepping them out of the code: `applyApprovalInTx`'s parameter order in three places, two migration ranges the re-pin claimed to have fixed but had not, a difference-table header still carrying pre-split line numbers, and a needle line number that was pre-fix plus an arithmetic error (126–200 is 75 lines, not 77 — 77 belongs to the 125–201 range and was carried over). The fifth was the fake-rigour form of the vacuous-assertion defect this project keeps finding: the controller's verification script printed nineteen `[OK]` lines with **no checking logic behind them** while citing an output file that was never archived, so its "0 FAIL" was true but not self-supporting. It was rebuilt as 106 real checks read from code and spec entities, with the cited output archived beside it. The lesson is now a spec discipline: executable details in a spec — regexes, signatures, literals, line ranges — must be grepped from the code entity and pasted, never hand-written.
|
||||
- 它的 5 条 note 针对的是控制者自己的工件而非代码。4 条是 re-pin 期间引入的 spec staleness,根因都是凭记忆写可执行细节而不是从代码里 grep 出来:`applyApprovalInTx` 的形参顺序 3 处、两处 re-pin 声称已修而实际未修的搬迁区间、一处仍带着拆分前行号的差异表表头、以及一个 pre-fix 的 needle 行号加一处算术错(126–200 是 75 行不是 77 行——77 属于 125–201 那个区间,被顺手带了过来)。第 5 条是本项目反复发现的「空断言」缺陷的假严谨形态:控制者的复验脚本打印了十九条 `[OK]` 而**背后没有任何校验逻辑**,同时引用了一个从未归档的输出文件,所以那句「0 FAIL」结论虽真却不自足。它已重建为 106 条从代码与 spec 实体读出的真校验,被引用的输出也归档在旁边。教训现在写成 spec 纪律:spec 里的可执行细节——正则、签名、字面量、行号区间——必须从代码实体 grep 出来粘贴,不得手写。
|
||||
|
||||
- One finding is ledgered rather than fixed: P14's assertion five, which scans for method declarations on `ContentService`, is bypassed by a **type-alias receiver** (`type X = ContentService` plus a method on `*X`). The shadowing genuinely takes effect through the composition root while a regex identical to the shipped one reports zero hits, with all ten guards plus vet and build green. It is pre-existing, fixing it means editing a file this batch is forbidden to touch, and HEAD has zero such aliases so the hole is not currently exploited. It also overturns that file's claim to be the "only mechanical means" of detecting shadowed promoted methods — the third universal claim overturned by measurement in three batches, after P14's "only means" and the `architecture_test.go` completeness claim.
|
||||
- 一条 finding 挂账而非在此修:P14 的断言五(扫描 `ContentService` 上的方法声明)被**类型别名接收者**绕过(`type X = ContentService` 加上 `*X` 上的方法)。遮蔽确实经组合根生效,而与既有断言逐字相同的正则报零命中,十条守卫加 vet 与 build 全绿。它是既有问题,修它要动本批禁触的文件,且 HEAD 上此类别名为 0 处故该洞当前未被利用。它还推翻了那个文件自称是侦测遮蔽提升方法的「唯一机械手段」——这是三批之内第三条被实测推翻的全称声明,前两条是 P14 的「唯一手段」与 `architecture_test.go` 的完备性声称。
|
||||
|
||||
- Spec work landed in three commits: `02188eb` (24 corrections across both specs and plans after task-level adjudication), `37a0a8d` (the four staleness notes), `9061c39` (the two advisories — the four-gate line now states that its timings are the mock path and lists all 11 `TEST_DATABASE_URL`-gated tests by name, and the archived differ script carries a note explaining that its 6 ORDER VIOLATIONS are a greedy `pop(0)` artefact while the prose conclusion of 0 is correct). Ledger entries added: six Go functions still over 60 lines; the threshold-100 blind spot restated as "any merge form at or under 100 lines" (merging phases four and six measures 91 lines and does not redden); the mirror blind spot where inlining one phase leaves every guard green; escape via any non-`func` form of arbitrary length (a 125-line package-level closure passes all four gates and all three length guards); three guard constants lacking self-attestation; the gofmt gate's exit-code blindness, which affects every batch's recipe; the type-alias bypass; a DB-enabled comparison added to the pre-merge checklist; and three guard-comment clarifications deferred with the minors above.
|
||||
- spec 工作落在三个 commit:`02188eb`(任务级裁定后对两份 spec 与 plan 共 24 处更正)、`37a0a8d`(4 条 staleness note)、`9061c39`(2 条 advisory——四门那一行现在写明其耗时是 mock 路径并按名列出全部 11 个受 `TEST_DATABASE_URL` 门控的测试;归档的 differ 脚本加注说明它输出的 6 个 ORDER VIOLATION 是贪心 `pop(0)` 的假象、而散文结论 0 是对的)。挂账新增:6 个仍超过 60 行的 Go 函数;阈值 100 的盲区重述为「≤100 行的任何合并形态」(合并阶段四与阶段六实测 91 行、不红);内联一个阶段时所有守卫全绿的镜像盲区;任意长度的非 `func` 形态逃逸(125 行的包级闭包通过全部四门与三条长度守卫);3 个守卫常量缺自证钉;gofmt 门的退出码盲——它影响**每个**批次的配方;类型别名绕过;合并前清单新增 DB-enabled 对照;以及随上述 minor 一并延后的 3 处守卫注释澄清。
|
||||
|
||||
- **P15-B is not part of this entry.** Its branch review returned 🔴 **needs fixing before merge** (the second time a branch review has blocked a batch, after P11): the product code is correct and all four gates are green, but the guard layer has a critical gap — `maskSourceComments()` compares a 2-character slice against the 4-character `'<!--'`, so its HTML-comment branch is dead code and never executes. Five measured consequences follow (two false greens where a fake palette block hides in an HTML comment, three false reds where merely rewording a comment reddens up to four legs while the e2e suite stays 9/9 green), and it overturns two claims already committed to the spec and the adjudication: that the guard "does not depend on the implementer's wording discipline" and that the fix for the earlier critical finding was safe because comment masking had neutralised the trap comment — true for that file, but only because the trap comment happened to use `//`. A second fix-forward is in flight; P15-B will be booked as 0.3.9 when it merges.
|
||||
- **P15-B 不在本条目内。** 它的全分支终审返回 🔴 **需修复后合并**(这是继 P11 之后第二次由终审拦下一批):产品代码正确、四门全绿,但守卫层有一个 critical 缺口——`maskSourceComments()` 用 2 字符切片去和 4 字符的 `'<!--'` 比较,于是它的 HTML 注释分支是死代码、永不执行。由此有五处实测后果(两处假绿:假调色板块藏在 HTML 注释里即可劫持;三处假红:仅仅改一句注释措辞就会红至四条腿,而 e2e 套件 9/9 全绿),并推翻了两处已入库的声称:spec 与裁定里写的「本守卫不依赖实现侧的措辞自律」,以及上一轮 critical finding 的修法之所以安全是「因为注释屏蔽已中和陷阱注释」——这句对那个文件是真的,但成立原因只是陷阱注释恰好用的是 `//`。第二轮 fix-forward 正在进行;P15-B 合并时记为 0.3.9。
|
||||
|
||||
## [0.3.7] - 2026-10-03
|
||||
|
||||
### Done / 完成
|
||||
|
||||
Reference in New Issue
Block a user