From b4c8482f874dd710f97a91f854283fe670734ecb Mon Sep 17 00:00:00 2001 From: unanmed <1319491857@qq.com> Date: Wed, 23 Sep 2026 11:12:03 +0800 Subject: [PATCH] fix(04): revise 04-08 plan based on checker feedback --- .../phases/04-render-adaptation/04-08-PLAN.md | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.planning/phases/04-render-adaptation/04-08-PLAN.md b/.planning/phases/04-render-adaptation/04-08-PLAN.md index eb892bf..63d243d 100644 --- a/.planning/phases/04-render-adaptation/04-08-PLAN.md +++ b/.planning/phases/04-render-adaptation/04-08-PLAN.md @@ -172,7 +172,7 @@ Phase 4 第七个增量(用户裁定 2026-09-22 · D-38 / D-39 / D-40 / D-41 / Task 0: 预执行汇报关卡 —— 汇报 TextureManager 新接口影响清点(D-38/D-40 第一步)的范围、基准、A–F 六类分列规则与唯一交付物、D-39/D-41/D-42 三条硬边界与只读证明方式,等待用户回复「可以执行」 - 用户对 TextureManager 新接口适应的裁定 D-38 / D-39 / D-40 / D-41 / D-42 已写入 `.planning/phases/04-render-adaptation/04-CONTEXT.md`(以文件内实际存在 D-38..D-42 为准);`d36ea69 refactor: 贴图存储方式` 已在当日 HEAD(`git rev-parse HEAD` 以 `d36ea69` 开头) + 用户对 TextureManager 新接口适应的裁定 D-38 / D-39 / D-40 / D-41 / D-42 已写入 `.planning/phases/04-render-adaptation/04-CONTEXT.md`(以文件内实际存在 D-38..D-42 为准);`d36ea69 refactor: 贴图存储方式` 是当日 HEAD 或其**祖先**(`git merge-base --is-ancestor d36ea69 HEAD` 退出码 0),且自 `d36ea69` 至当日 HEAD 对生产路径无提交差异(`git diff --stat d36ea69 HEAD -- packages packages-user src` 无输出)—— 即 `packages-user` / `packages` / `src` 的**生产代码基线 = `d36ea69`**(工作树内可能存在的并发未提交改动不属本前置条件,由 Task 1 步骤 (0a) 当日实测记录为只读起始基线) (只读汇报,不修改任何文件) - AGENTS.md(「任何计划的执行,其第一个 block 点必须是向我汇报计划内容并等待我确认」;只有明确回复「可以执行」四个字之后才允许开始执行;「如果发现任何问题,永远不要擅自将其视为问题并记录,必须经由我确认」;「任何公共、受保护、私有方法、字段等内容的命名必须先向我反馈」) @@ -233,7 +233,7 @@ Phase 4 第七个增量(用户裁定 2026-09-22 · D-38 / D-39 / D-40 / D-41 / 需用户先指定文件名;phase 目录内既有 `04-01`..`04-07` 产物不得改写 / 覆盖 - 回复「可以执行」四个字(或给出修订意见) + 回复「可以执行」四个字(修订意见请按 action ⑪ 处理) 用户在对话中明确回复「可以执行」四个字(人工核对用户回复原文;本关卡为人工关卡,无自动命令,失败语义写在 Task 0 的 action ⑩ 与 acceptance_criteria 中) @@ -353,7 +353,7 @@ Phase 4 第七个增量(用户裁定 2026-09-22 · D-38 / D-39 / D-40 / D-41 / - node -e "const fs=require('fs');const t=fs.readFileSync('.planning/phases/04-render-adaptation/04-TEXTURE-INTERFACE-IMPACT.md','utf8');const seg=h=>{const i=t.indexOf(h);if(i<0)return '';const rest=t.slice(i+h.length);const j=rest.search(/\r?\n## /);return j<0?rest:rest.slice(0,j)};const A=seg('## A \u7b26\u53f7\u6539\u540d'),B=seg('## B \u6210\u5458\u4e0e\u7b7e\u540d'),C=seg('## C \u5730\u56fe\u6e32\u67d3\u6d88\u8d39\u8005'),D=seg('## D \u88ab\u5220\u4e14\u65e0\u66ff\u4ee3'),E=seg('## E \u52a0\u8f7d\u76f8\u5173'),F=seg('## F \u672a\u80fd\u4ece\u9605\u8bfb\u786e\u5b9a');const cnt=(s,re)=>(s.match(re)||[]).length;const rows=s=>cnt(s,/^\|\s*#04-08-[ABCDE]-\d+\s*\|/gm);const anch=l=>cnt(l,/[A-Za-z0-9_\-.\/]+\.tsx?:\d+/g);const p=[];if(!A||!B||!C||!D||!E||!F)p.push('MISSING SECTION');if(rows(A)<5)p.push('A ROWS '+rows(A));if(rows(B)<4)p.push('B ROWS '+rows(B));if(rows(C)<8)p.push('C ROWS '+rows(C));if(rows(D)<6)p.push('D ROWS '+rows(D));if(rows(E)<4)p.push('E ROWS '+rows(E));if(cnt(F,/^- \*\*#04-08-F-\d+/gm)<8)p.push('F ITEMS '+cnt(F,/^- \*\*#04-08-F-\d+/gm));for(const pr of [['A',A],['B',B],['C',C],['D',D],['E',E]]){const bad=pr[1].split(/\r?\n/).filter(l=>/^\|\s*#04-08-[ABCDE]-\d+\s*\|/.test(l)).filter(l=>anch(l)<2);if(bad.length>0)p.push(pr[0]+' ROWS WITHOUT TWO-SIDED ANCHOR '+bad.length)}const needA=['TextureManager','MaterialManager','renderAnimatedWith','BlockCls','tileType','AutotileProcessor','IClientBase','getIdentifierByAlias','getAliasByIdentifier','createMaterial'];const mA=needA.filter(k=>A.indexOf(k)<0);if(mA.length>0)p.push('A MISSING '+mA.join('|'));const needB=['tiles','autotile','tilesetReserve','tilesetUnit','getFrameCount','addGrid','addRowAnimate','addAutotile','addTileset','AutotileType','IClientCoreConfig'];const mB=needB.filter(k=>B.indexOf(k)<0);if(mB.length>0)p.push('B MISSING '+mB.join('|'));const needC=['renderer.ts','vertex.ts','hero.ts','moving.ts','client.ts','types.ts','core.ts','#04-08-'];const mC=needC.filter(k=>C.indexOf(k)<0);if(mC.length>0)p.push('C MISSING '+mC.join('|'));const needD=['renderWithoutCheck','BlockCls','manager','renderWith','getIdentifierByAlias','getAliasByIdentifier','utils.ts'];const mD=needD.filter(k=>D.indexOf(k)<0);if(mD.length>0)p.push('D MISSING '+mD.join('|'));if(cnt(D,/\u8bf7\u7528\u6237\u53cd\u9988/g)<6)p.push('D FEEDBACK PHRASE '+cnt(D,/\u8bf7\u7528\u6237\u53cd\u9988/g));const needE=['fallback/load.ts','loader.ts','packages','src/types/declaration'];const mE=needE.filter(k=>E.indexOf(k)<0);if(mE.length>0)p.push('E MISSING '+mE.join('|'));if(p.length>0){console.error('CATEGORY GATE FAILED :: '+p.join(' ;; '));process.exit(1)}console.log('OK categories A'+rows(A)+' B'+rows(B)+' C'+rows(C)+' D'+rows(D)+' E'+rows(E)+' F'+cnt(F,/^- \*\*#04-08-F-\d+/gm))" + node -e "const fs=require('fs');const t=fs.readFileSync('.planning/phases/04-render-adaptation/04-TEXTURE-INTERFACE-IMPACT.md','utf8');const seg=h=>{const i=t.indexOf(h);if(i<0)return '';const rest=t.slice(i+h.length);const j=rest.search(/\r?\n## /);return j<0?rest:rest.slice(0,j)};const A=seg('## A \u7b26\u53f7\u6539\u540d'),B=seg('## B \u6210\u5458\u4e0e\u7b7e\u540d'),C=seg('## C \u5730\u56fe\u6e32\u67d3\u6d88\u8d39\u8005'),D=seg('## D \u88ab\u5220\u4e14\u65e0\u66ff\u4ee3'),E=seg('## E \u52a0\u8f7d\u76f8\u5173'),F=seg('## F \u672a\u80fd\u4ece\u9605\u8bfb\u786e\u5b9a');const cnt=(s,re)=>(s.match(re)||[]).length;const rows=s=>cnt(s,/^\|\s*#04-08-[ABCDE]-\d+\s*\|/gm);const anch=l=>cnt(l,/[A-Za-z0-9_\-.\/]+\.tsx?:\d+/g);const p=[];if(!A||!B||!C||!D||!E||!F)p.push('MISSING SECTION');if(rows(A)<5)p.push('A ROWS '+rows(A));if(rows(B)<4)p.push('B ROWS '+rows(B));if(rows(C)<8)p.push('C ROWS '+rows(C));if(rows(D)<6)p.push('D ROWS '+rows(D));if(rows(E)<4)p.push('E ROWS '+rows(E));if(cnt(F,/^- \*\*#04-08-F-\d+/gm)<8)p.push('F ITEMS '+cnt(F,/^- \*\*#04-08-F-\d+/gm));for(const pr of [['A',A],['B',B],['C',C],['D',D],['E',E]]){const bad=pr[1].split(/\r?\n/).filter(l=>/^\|\s*#04-08-[ABCDE]-\d+\s*\|/.test(l)).filter(l=>anch(l)<2);if(bad.length>0)p.push(pr[0]+' ROWS WITHOUT TWO-SIDED ANCHOR '+bad.length)}const needA=['TextureManager','MaterialManager','renderAnimatedWith','BlockCls','tileType','AutotileProcessor','IClientBase','getIdentifierByAlias','getAliasByIdentifier','createMaterial'];const mA=needA.filter(k=>A.indexOf(k)<0);if(mA.length>0)p.push('A MISSING '+mA.join('|'));const needB=['tiles','autotile','tilesetReserve','tilesetUnit','getFrameCount','addGrid','addRowAnimate','addAutotile','addTileset','AutotileType','IClientCoreConfig'];const mB=needB.filter(k=>B.indexOf(k)<0);if(mB.length>0)p.push('B MISSING '+mB.join('|'));const needC=['renderer.ts','vertex.ts','hero.ts','moving.ts','client.ts','types.ts','core.ts'];const mC=needC.filter(k=>C.indexOf(k)<0);if(mC.length>0)p.push('C MISSING '+mC.join('|'));const cbad=C.split(/\r?\n/).filter(l=>/^\|\s*#04-08-C-\d+\s*\|/.test(l)).filter(l=>!/#04-08-[ABD]-\d+/.test(l));if(cbad.length>0)p.push('C ROW MISSING CROSS-REF '+cbad.length);const needD=['renderWithoutCheck','BlockCls','manager','renderWith','getIdentifierByAlias','getAliasByIdentifier','utils.ts'];const mD=needD.filter(k=>D.indexOf(k)<0);if(mD.length>0)p.push('D MISSING '+mD.join('|'));if(cnt(D,/\u8bf7\u7528\u6237\u53cd\u9988/g)<6)p.push('D FEEDBACK PHRASE '+cnt(D,/\u8bf7\u7528\u6237\u53cd\u9988/g));const needE=['fallback/load.ts','loader.ts','packages','src/types/declaration'];const mE=needE.filter(k=>E.indexOf(k)<0);if(mE.length>0)p.push('E MISSING '+mE.join('|'));if(p.length>0){console.error('CATEGORY GATE FAILED :: '+p.join(' ;; '));process.exit(1)}console.log('OK categories A'+rows(A)+' B'+rows(B)+' C'+rows(C)+' D'+rows(D)+' E'+rows(E)+' F'+cnt(F,/^- \*\*#04-08-F-\d+/gm))" non-zero exit, 或 stderr 含 "CATEGORY GATE FAILED"(A/B/C/D/E 任一类行数低于下限、任一行缺命中处或基准侧锚点、A 未覆盖符号级关键词、B 未覆盖新增成员与签名、C 未覆盖 8 个定位文件或未交叉引用 ID、D 未覆盖关键被删成员或缺「请用户反馈」措辞、E 未覆盖加载文件与对照扫描、F 少于 8 条) node -e "const{execSync}=require('child_process');const names=['MaterialManager','renderAnimatedWith','getBlockClsByAlias','getIdentifierByAlias','getAliasByIdentifier'];const bad=[];for(const n of names){let o='',st=0;try{o=execSync('git grep -n -- '+n+' -- packages-user',{encoding:'utf8',stdio:['ignore','pipe','pipe']})}catch(e){o=(e.stdout||'')+(e.stderr||'');st=e.status}const lines=o.replace(/\s+$/,'').length>0?o.replace(/\s+$/,'').split(/\r?\n/):[];if(lines.length>0){bad.push(n+' => '+lines.slice(0,3).join(' ; '))}else if(st!==0&&st!==1){bad.push(n+' grep status '+st)}}if(bad.length>0){console.error('STALE SYMBOL STILL REFERENCED: '+bad.join(' || '));process.exit(1)}console.log('OK zero stale symbol references ('+names.length+' names)')" non-zero exit, 或 stderr 含 "STALE SYMBOL STILL REFERENCED"(`MaterialManager` / `renderAnimatedWith` / `getBlockClsByAlias` / `getIdentifierByAlias` / `getAliasByIdentifier` 在 `packages-user` 源码面仍存在引用,即 A 类的「0 引用」结论为臆造) @@ -395,12 +395,12 @@ Phase 4 第七个增量(用户裁定 2026-09-22 · D-38 / D-39 / D-40 / D-41 / (5) **提交**:以 `docs(04-08): TextureManager 新接口影响范围清点(只读,D-38/D-40 第一步)` 提交**仅** `.planning/phases/04-render-adaptation/04-TEXTURE-INTERFACE-IMPACT.md`、`.planning/phases/04-render-adaptation/04-08-PLAN.md` 与 `.planning/phases/04-render-adaptation/04-08-SUMMARY.md` 三个文件;**不得**把任何生产源码改动或用户既有的未提交改动纳入本次提交(尤其不得提交 `.planning/phases/04-render-adaptation/04-CONTEXT.md` 的既有未提交改动),不得改写 / 覆盖 / 改号 `04-01`..`04-07` 的既有产物。 - node -e "const fs=require('fs'),{execSync}=require('child_process');const t=fs.readFileSync('.planning/phases/04-render-adaptation/04-TEXTURE-INTERFACE-IMPACT.md','utf8');const seg=h=>{const i=t.indexOf(h);if(i<0)return '';const rest=t.slice(i+h.length);const j=rest.search(/\r?\n## /);return j<0?rest:rest.slice(0,j)};const ms=seg('## \u65b9\u6cd5');if(ms.indexOf('\u53ea\u8bfb\u8d77\u59cb\u57fa\u7ebf')<0){console.error('READONLY BASELINE NOT RECORDED');process.exit(1)}const rec=(ms.match(/^[ MARCUD?!]{2} (?:packages|packages-user|src)[^\r\n]*/gm)||[]).map(s=>s.replace(/\s+$/,''));let raw='';try{raw=execSync('git status --porcelain -- packages packages-user src',{encoding:'utf8'})}catch(e){console.error('GIT STATUS FAILED '+(e.message||''));process.exit(1)}const out=raw.replace(/\s+$/,'');const cur=out.length>0?out.split(/\r?\n/).map(s=>s.replace(/\s+$/,'')):[];const extra=cur.filter(l=>rec.indexOf(l)<0);const gone=rec.filter(l=>cur.indexOf(l)<0);if(extra.length+gone.length>0){console.error('READONLY BASELINE CHANGED extra=['+extra.join(' ; ')+'] missing=['+gone.join(' ; ')+']');process.exit(1)}console.log('OK read-only unchanged entries '+cur.length)" - non-zero exit, 或 stderr 含 "READONLY BASELINE NOT RECORDED" / "READONLY BASELINE CHANGED"(`packages` / `packages-user` / `src` 下出现了新增 / 消失 / 状态码变化的 worktree 条目 —— 违反只读约束;用户既有改动不得回滚 / 暂存 / 提交) + node -e "const fs=require('fs'),{execSync}=require('child_process');const t=fs.readFileSync('.planning/phases/04-render-adaptation/04-TEXTURE-INTERFACE-IMPACT.md','utf8');const seg=h=>{const i=t.indexOf(h);if(i<0)return '';const rest=t.slice(i+h.length);const j=rest.search(/\r?\n## /);return j<0?rest:rest.slice(0,j)};const ms=seg('## \u65b9\u6cd5');if(ms.indexOf('\u53ea\u8bfb\u8d77\u59cb\u57fa\u7ebf')<0){console.error('READONLY BASELINE NOT RECORDED');process.exit(1)}const rec=(ms.match(/^[ MARCUD?!]{2} (?:packages|packages-user|src)[^\r\n]*/gm)||[]).map(s=>s.replace(/\s+$/,''));let raw='';try{raw=execSync('git status --porcelain -- packages packages-user src',{encoding:'utf8'})}catch(e){console.error('GIT STATUS FAILED '+(e.message||''));process.exit(1)}const out=raw.replace(/\s+$/,'');const cur=out.length>0?out.split(/\r?\n/).map(s=>s.replace(/\s+$/,'')):[];const extra=cur.filter(l=>rec.indexOf(l)<0);const gone=rec.filter(l=>cur.indexOf(l)<0);if(extra.length+gone.length>0){console.error('READONLY BASELINE CHANGED extra=['+extra.join(' ; ')+'] missing=['+gone.join(' ; ')+']');process.exit(1)}const ro=execSync('git status --porcelain -- .planning/REQUIREMENTS.md',{encoding:'utf8'}).replace(/\s+$/,'');if(ro.length>0){console.error('READ-ONLY VIOLATION .planning/REQUIREMENTS.md: '+ro);process.exit(1)}const req=fs.readFileSync('.planning/REQUIREMENTS.md','utf8').split(/\r?\n/);const notPending=['REND-01','REND-02'].filter(id=>{const r=req.find(l=>l.indexOf('| '+id+' |')>=0);return !r||!/\|\s*Pending\s*\|\s*$/.test(r)});if(notPending.length>0){console.error('REND STATUS NOT PENDING: '+notPending.join(','));process.exit(1)}console.log('OK read-only unchanged entries '+cur.length+'; REND-01/REND-02 still Pending')" + non-zero exit, 或 stderr 含 "READONLY BASELINE NOT RECORDED" / "READONLY BASELINE CHANGED" / "READ-ONLY VIOLATION" / "REND STATUS NOT PENDING"(`packages` / `packages-user` / `src` 下出现了新增 / 消失 / 状态码变化的 worktree 条目,或 `.planning/REQUIREMENTS.md` 被改动,或该文件中 `REND-01` / `REND-02` 不再是 `Pending` —— 违反只读约束或「REND 保持 Pending」真值不成立;用户既有改动不得回滚 / 暂存 / 提交) node -e "const fs=require('fs'),{execSync}=require('child_process');const t=fs.readFileSync('.planning/phases/04-render-adaptation/04-TEXTURE-INTERFACE-IMPACT.md','utf8');const a=t.match(/\u9519\u8bef\u603b\u884c\u6570\uff1a\s*(\d+)/);const b=t.match(/texture \u5f52\u5c5e\u9519\u8bef\u884c\u6570\uff1a\s*(\d+)/);if(!a||!b){console.error('MISSING TSC BASELINE FIGURES');process.exit(1)}let o='';try{o=execSync('pnpm exec vue-tsc --noEmit',{encoding:'utf8',maxBuffer:1<<28,stdio:['ignore','pipe','pipe']})}catch(e){o=(e.stdout||'')+(e.stderr||'')}const n=(o.match(/error TS\d+/g)||[]).length;if(String(n)!==a[1]){console.error('TSC TOTAL CHANGED '+n+' vs '+a[1]);process.exit(1)}console.log('OK tsc total unchanged '+n+' texture '+b[1])" non-zero exit, 或 stderr 含 "MISSING TSC BASELINE FIGURES" / "TSC TOTAL CHANGED"(类型错误总行数与文档记录不一致 —— 生产源码被改动或基线未如实记录;后者使只读证明失效) - node -e "const fs=require('fs');const t=fs.readFileSync('.planning/phases/04-render-adaptation/04-TEXTURE-INTERFACE-IMPACT.md','utf8');const seg=h=>{const i=t.indexOf(h);if(i<0)return '';const rest=t.slice(i+h.length);const j=rest.search(/\r?\n## /);return j<0?rest:rest.slice(0,j)};const A=seg('## A \u7b26\u53f7\u6539\u540d'),B=seg('## B \u6210\u5458\u4e0e\u7b7e\u540d'),C=seg('## C \u5730\u56fe\u6e32\u67d3\u6d88\u8d39\u8005'),D=seg('## D \u88ab\u5220\u4e14\u65e0\u66ff\u4ee3'),E=seg('## E \u52a0\u8f7d\u76f8\u5173'),F=seg('## F \u672a\u80fd\u4ece\u9605\u8bfb\u786e\u5b9a'),Z=seg('## \u5904\u7f6e');const cnt=(s,re)=>(s.match(re)||[]).length;const rows=s=>cnt(s,/^\|\s*#04-08-[ABCDE]-\d+\s*\|/gm);const anch=l=>cnt(l,/[A-Za-z0-9_\-.\/]+\.tsx?:\d+/g);const p=[];if(!Z)p.push('MISSING DISPOSITION');if(rows(A)<5)p.push('A ROWS '+rows(A));if(rows(B)<4)p.push('B ROWS '+rows(B));if(rows(C)<8)p.push('C ROWS '+rows(C));if(rows(D)<6)p.push('D ROWS '+rows(D));if(rows(E)<4)p.push('E ROWS '+rows(E));if(cnt(F,/^- \*\*#04-08-F-\d+/gm)<8)p.push('F ITEMS '+cnt(F,/^- \*\*#04-08-F-\d+/gm));for(const pr of [['A',A],['B',B],['C',C],['D',D],['E',E]]){const bad=pr[1].split(/\r?\n/).filter(l=>/^\|\s*#04-08-[ABCDE]-\d+\s*\|/.test(l)).filter(l=>anch(l)<2);if(bad.length>0)p.push(pr[0]+' ROWS WITHOUT TWO-SIDED ANCHOR '+bad.length)}const needD=['renderWithoutCheck','BlockCls','utils.ts'];const mD=needD.filter(k=>D.indexOf(k)<0);if(mD.length>0)p.push('D MISSING '+mD.join('|'));if(cnt(D,/\u8bf7\u7528\u6237\u53cd\u9988/g)<6)p.push('D FEEDBACK PHRASE');if(cnt(E,/\u4ec5\u62a5\u544a/g)<1)p.push('E REPORT-ONLY PHRASE');if(p.length>0){console.error('CATEGORY GATE FAILED :: '+p.join(' ;; '));process.exit(1)}console.log('OK final categories A'+rows(A)+' B'+rows(B)+' C'+rows(C)+' D'+rows(D)+' E'+rows(E)+' F'+cnt(F,/^- \*\*#04-08-F-\d+/gm))" - non-zero exit, 或 stderr 含 "CATEGORY GATE FAILED"(A/B/C/D/E 行数低于下限、任一行缺命中处或基准侧锚点、D 未覆盖关键被删成员或缺「请用户反馈」措辞、E 缺「仅报告」措辞、F 少于 8 条、缺 `## 处置`) + node -e "const fs=require('fs');const t=fs.readFileSync('.planning/phases/04-render-adaptation/04-TEXTURE-INTERFACE-IMPACT.md','utf8');const seg=h=>{const i=t.indexOf(h);if(i<0)return '';const rest=t.slice(i+h.length);const j=rest.search(/\r?\n## /);return j<0?rest:rest.slice(0,j)};const A=seg('## A \u7b26\u53f7\u6539\u540d'),B=seg('## B \u6210\u5458\u4e0e\u7b7e\u540d'),C=seg('## C \u5730\u56fe\u6e32\u67d3\u6d88\u8d39\u8005'),D=seg('## D \u88ab\u5220\u4e14\u65e0\u66ff\u4ee3'),E=seg('## E \u52a0\u8f7d\u76f8\u5173'),F=seg('## F \u672a\u80fd\u4ece\u9605\u8bfb\u786e\u5b9a'),Z=seg('## \u5904\u7f6e');const cnt=(s,re)=>(s.match(re)||[]).length;const rows=s=>cnt(s,/^\|\s*#04-08-[ABCDE]-\d+\s*\|/gm);const anch=l=>cnt(l,/[A-Za-z0-9_\-.\/]+\.tsx?:\d+/g);const p=[];if(!Z)p.push('MISSING DISPOSITION');if(rows(A)<5)p.push('A ROWS '+rows(A));if(rows(B)<4)p.push('B ROWS '+rows(B));if(rows(C)<8)p.push('C ROWS '+rows(C));if(rows(D)<6)p.push('D ROWS '+rows(D));if(rows(E)<4)p.push('E ROWS '+rows(E));if(cnt(F,/^- \*\*#04-08-F-\d+/gm)<8)p.push('F ITEMS '+cnt(F,/^- \*\*#04-08-F-\d+/gm));for(const pr of [['A',A],['B',B],['C',C],['D',D],['E',E]]){const bad=pr[1].split(/\r?\n/).filter(l=>/^\|\s*#04-08-[ABCDE]-\d+\s*\|/.test(l)).filter(l=>anch(l)<2);if(bad.length>0)p.push(pr[0]+' ROWS WITHOUT TWO-SIDED ANCHOR '+bad.length)}const cbad=C.split(/\r?\n/).filter(l=>/^\|\s*#04-08-C-\d+\s*\|/.test(l)).filter(l=>!/#04-08-[ABD]-\d+/.test(l));if(cbad.length>0)p.push('C ROWS WITHOUT CROSS-REF '+cbad.length);const needD=['renderWithoutCheck','BlockCls','utils.ts'];const mD=needD.filter(k=>D.indexOf(k)<0);if(mD.length>0)p.push('D MISSING '+mD.join('|'));if(cnt(D,/\u8bf7\u7528\u6237\u53cd\u9988/g)<6)p.push('D FEEDBACK PHRASE');if(cnt(E,/\u4ec5\u62a5\u544a/g)<1)p.push('E REPORT-ONLY PHRASE');if(p.length>0){console.error('CATEGORY GATE FAILED :: '+p.join(' ;; '));process.exit(1)}console.log('OK final categories A'+rows(A)+' B'+rows(B)+' C'+rows(C)+' D'+rows(D)+' E'+rows(E)+' F'+cnt(F,/^- \*\*#04-08-F-\d+/gm))" + non-zero exit, 或 stderr 含 "CATEGORY GATE FAILED"(A/B/C/D/E 行数低于下限、任一行缺命中处或基准侧锚点、D 未覆盖关键被删成员或缺「请用户反馈」措辞、E 缺「仅报告」措辞、F 少于 8 条、C 行未交叉引用 A/B/D 记录 ID、缺 `## 处置`) node -e "const{execSync}=require('child_process');const names=['MaterialManager','renderAnimatedWith','getBlockClsByAlias','getIdentifierByAlias','getAliasByIdentifier'];const bad=[];for(const n of names){let o='',st=0;try{o=execSync('git grep -n -- '+n+' -- packages-user',{encoding:'utf8',stdio:['ignore','pipe','pipe']})}catch(e){o=(e.stdout||'')+(e.stderr||'');st=e.status}const lines=o.replace(/\s+$/,'').length>0?o.replace(/\s+$/,'').split(/\r?\n/):[];if(lines.length>0){bad.push(n+' => '+lines.slice(0,3).join(' ; '))}else if(st!==0&&st!==1){bad.push(n+' grep status '+st)}}if(bad.length>0){console.error('STALE SYMBOL STILL REFERENCED: '+bad.join(' || '));process.exit(1)}console.log('OK zero stale symbol references')" non-zero exit, 或 stderr 含 "STALE SYMBOL STILL REFERENCED"(A 类的「0 引用」结论为臆造) node -e "const fs=require('fs'),{execSync}=require('child_process');const dir='.planning/phases/04-render-adaptation';const prot=['04-01','04-02','04-03','04-04','04-05','04-06','04-07'].map(n=>dir+'/'+n+'-PLAN.md').concat(['04-01','04-02','04-03','04-04','04-05','04-06','04-07'].map(n=>dir+'/'+n+'-SUMMARY.md'));const args=prot.map(p=>'\"'+p+'\"').join(' ');const o=execSync('git status --porcelain -- '+args,{encoding:'utf8'}).replace(/\s+$/,'');if(o.length>0){console.error('EXISTING 04-01..04-07 ARTIFACTS MODIFIED: '+o);process.exit(1)}const plans=fs.readdirSync(dir).filter(f=>/-PLAN\.md$/.test(f)).sort();const extra=plans.filter(p=>!/^04-0[1-8]-PLAN\.md$/.test(p));if(extra.length>0){console.error('UNEXPECTED PLAN FILES '+extra.join(','));process.exit(1)}if(plans.length!==8){console.error('PLAN COUNT '+plans.length+' expected 8');process.exit(1)}if(!fs.readdirSync(dir).some(f=>f==='04-08-SUMMARY.md')){console.error('MISSING 04-08 SUMMARY');process.exit(1)}console.log('OK 04-01..04-07 untouched; plans '+plans.join(','))" @@ -410,7 +410,7 @@ Phase 4 第七个增量(用户裁定 2026-09-22 · D-38 / D-39 / D-40 / D-41 / - `## 处置` 定稿,明写:本步只读、生产代码零改动、唯一交付物、相对只读起始基线零变化、**第二步未规划**(D-40)、范围限 `packages-user` 消费者面(`packages/` 与 `src/` 仅报告)、**D 类须用户反馈后才可能进入第二步**、**E 类归用户自行适配新加载系统(D-39 / D-41)**、**`material/` 内命中归用户(D-42)**、`tileType` / `renderWithoutCheck` 替代 / 自建 processor 由用户裁决、D-12 / D-18 / D-19 / D-22 边界、`REND-01` / `REND-02` 保持 Pending,并(如与规划日不同)说明实测差异 - - 六道门禁全部转绿:只读门禁(porcelain 逐条一致)、类型门禁一致性(错误总行数等于记录值)、类别门禁(A ≥ 5 / B ≥ 4 / C ≥ 8 / D ≥ 6 / E ≥ 4 / F ≥ 8 且每行双锚点与关键词齐备)、符号零命中复验、既有计划保护 + 计划范围门禁(`04-01..04-07-*` 未被改写、无 `04-09+`、`04-08-SUMMARY.md` 已落盘)、CRLF 门禁 + - 六道门禁全部转绿:只读门禁(porcelain 逐条一致;同时覆盖 `.planning/REQUIREMENTS.md` 并断言 `REND-01` / `REND-02` 行仍为 `Pending`)、类型门禁一致性(错误总行数等于记录值)、类别门禁(A ≥ 5 / B ≥ 4 / C ≥ 8 / D ≥ 6 / E ≥ 4 / F ≥ 8 且每行双锚点与关键词齐备;C 行均交叉引用 A / B / D 记录 ID)、符号零命中复验、既有计划保护 + 计划范围门禁(`04-01..04-07-*` 未被改写、无 `04-09+`、`04-08-SUMMARY.md` 已落盘)、CRLF 门禁 - 人工代码复核结论已记录(抽查 A 类 3 处 `BlockCls` import 与 `hero.ts:167` / `vertex.ts:852`、D 类 3 处 `renderWithoutCheck` 与 `material/utils.ts:2`、E 类 `fallback/load.ts` 9 处),偏差均已修正 - `04-08-SUMMARY.md` 已产出,如实记录当日实测值(porcelain / 类型错误数)、A–F 实际行数与关键结论、六道门禁结果、人工复核结论、与规划日的差异说明、`REND-01` / `REND-02` 仍 Pending 与「第二步未规划」;**未写入任何被删接口的替代方案** - 提交仅含 `04-TEXTURE-INTERFACE-IMPACT.md`、`04-08-PLAN.md`、`04-08-SUMMARY.md` 三个文件;未纳入任何生产源码改动,未提交 `04-CONTEXT.md` 的既有未提交改动,未改写 `04-01..04-07-*` @@ -454,7 +454,7 @@ Phase 4 第七个增量(用户裁定 2026-09-22 · D-38 / D-39 / D-40 / D-41 / -- **只读门禁**:`node -e "const{execSync}=require('child_process');console.log(JSON.stringify(execSync('git status --porcelain -- packages packages-user src',{encoding:'utf8'})))"` 的输出与交付文档 `## 方法` 的「只读起始基线」条目逐条一致(不新增、不消失、状态码不变)。 +- **只读门禁**:`node -e "const{execSync}=require('child_process');console.log(JSON.stringify(execSync('git status --porcelain -- packages packages-user src',{encoding:'utf8'})))"` 的输出与交付文档 `## 方法` 的「只读起始基线」条目逐条一致(不新增、不消失、状态码不变);并断言 `.planning/REQUIREMENTS.md` 的 `REND-01` / `REND-02` 行仍为 `Pending`(该文件亦纳入只读 porcelain 集合)。 - **类型门禁一致性**:`node -e "const{execSync}=require('child_process');let o='';try{o=execSync('pnpm exec vue-tsc --noEmit',{encoding:'utf8',maxBuffer:1<<28,stdio:['ignore','pipe','pipe']})}catch(e){o=(e.stdout||'')+(e.stderr||'')}console.log((o.match(/error TS\d+/g)||[]).length)"` 的输出等于文档记录的「错误总行数」(规划日 202);「texture 归属错误行数」(规划日 23 = `client-base` 1 + `client-modules` 22)为规划日人工归类、执行日按当日实测重新归类,只要求文档逐行列出(**非机器断言**,由人工复核保证)。 - **类别门禁**:交付文档的 A ≥ 5 行 / B ≥ 4 行 / C ≥ 8 行 / D ≥ 6 行 / E ≥ 4 行 / F ≥ 8 条,A–E 每行 ≥2 个 `file:line` 锚点,A 覆盖符号级关键词,B 覆盖新增成员与签名关键词,C 覆盖 8 个定位文件并携带交叉引用 ID,D 覆盖关键被删成员与 `material/utils.ts` 且含「请用户反馈」措辞,E 包含加载文件与对照扫描结论并含「仅报告」措辞。 - **符号零命中复验**:`git grep -n -- -- packages-user`(`MaterialManager` / `renderAnimatedWith` / `getBlockClsByAlias` / `getIdentifierByAlias` / `getAliasByIdentifier`)逐一无输出。 @@ -475,6 +475,6 @@ Create `.planning/phases/04-render-adaptation/04-08-SUMMARY.md` when done | `.planning/phases/04-render-adaptation/04-TEXTURE-INTERFACE-IMPACT.md` | 交付文档(**唯一** `files_modified`) | `d36ea69` TextureManager 基准在 `packages-user` 消费者面的只读影响台账:元信息块 + 背景 / 方法 / 基准变更(d36ea69)/ 类型门禁实测基线 / A 符号改名与移除的引用点 / B 成员与签名变化的调用点 / C 地图渲染消费者定位索引 / D 被删且无替代(需用户反馈)/ E 加载相关与添加素材(仅报告)/ F 未能从阅读确定(未猜测)/ 处置 | | `.planning/phases/04-render-adaptation/04-08-PLAN.md` | 计划簿记 | 本计划(编号 04-08;`wave: 7`;`depends_on` 04-01..04-07) | | `.planning/phases/04-render-adaptation/04-08-SUMMARY.md` | 执行簿记 | 当日实测值(porcelain / `pnpm exec vue-tsc --noEmit` 错误数)、A–F 实际行数与关键结论、六道门禁结果、人工复核结论、与规划日的差异说明、`REND-01` / `REND-02` 仍 Pending 与「第二步未规划」 | -| 门禁结果(会话内可复跑) | 验证证据 | 只读门禁(porcelain 逐条一致)、类型门禁一致性(错误总行数等于记录值)、类别门禁(A ≥ 5 / B ≥ 4 / C ≥ 8 / D ≥ 6 / E ≥ 4 / F ≥ 8 与双锚点 / 关键词 / 措辞)、符号零命中复验、既有计划保护 + 计划范围门禁(`04-01..04-07-*` 未改写、无 `04-09+`、SUMMARY 已落盘)、CRLF 门禁 | +| 门禁结果(会话内可复跑) | 验证证据 | 只读门禁(porcelain 逐条一致;覆盖 `.planning/REQUIREMENTS.md` 并断言 `REND-01` / `REND-02` 仍为 `Pending`)、类型门禁一致性(错误总行数等于记录值)、类别门禁(A ≥ 5 / B ≥ 4 / C ≥ 8 / D ≥ 6 / E ≥ 4 / F ≥ 8 与双锚点 / 关键词 / 措辞)、符号零命中复验、既有计划保护 + 计划范围门禁(`04-01..04-07-*` 未改写、无 `04-09+`、SUMMARY 已落盘)、CRLF 门禁 | | 显式**不**产出 | 边界声明 | 任何生产代码改动、任何测试文件、任何依赖变更、任何修复方案或第二步实施计划(D-40:第二步待用户审阅本文件后另行规划)、任何针对 `material/` 与加载面的修改方案(D-39 / D-41 / D-42) |