From e600a147a0f321884aa56e4f2a6296dd3a4bb987 Mon Sep 17 00:00:00 2001 From: A-znk <145305286+Yuki-zik@users.noreply.github.com> Date: Wed, 8 Jul 2026 14:35:16 +0800 Subject: [PATCH] fix(ci): normalize line endings to fix Windows CRLF test failure The 'extension applies on-demand stable ID tracking policy' test failed only on windows-latest: source-scanning assertions match multi-line \n string literals, but core.autocrlf=true checks out sources as CRLF so indexOf never matches. Add a root .gitattributes (* text=auto eol=lf) to force LF checkout on all platforms, plus a readSource() EOL-normalizing helper in test/extension.tests.js so existing CRLF working copies also pass. No runtime code changed. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .gitattributes | 22 ++++++++++++++++++++++ agent/tasks.md | 11 ++++++----- agent/timeline.md | 1 + test/extension.tests.js | 28 ++++++++++++++++------------ 4 files changed, 45 insertions(+), 17 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..5bc9fc2 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,22 @@ +# Normalize line endings: store text as LF and check out as LF on all +# platforms. This prevents Windows CRLF checkouts (core.autocrlf=true) from +# breaking source-scanning tests that match multi-line \n string literals. +* text=auto eol=lf + +# Explicit LF for the source and tooling files the tests read. +*.js text eol=lf +*.json text eol=lf +*.md text eol=lf +*.yml text eol=lf +*.svg text eol=lf + +# Binary assets must never be normalized. +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.ico binary +*.woff binary +*.woff2 binary +*.ttf binary +*.eot binary diff --git a/agent/tasks.md b/agent/tasks.md index eac8cc4..a5fc223 100644 --- a/agent/tasks.md +++ b/agent/tasks.md @@ -15,11 +15,12 @@ ## Session Summary -- Active focus: Reworked highlight acrylic so the default effect is text-layer glow/underline without background fill, border blocks, or hidden text. -- Verification: `npx qunit test/highlights.tests.js test/schemes.tests.js`, `npm test`, `npm run webpack`, CDP screenshot `/tmp/taskvision-cdp-visual-final-pass2.png`, visual subagent review, and targeted `git diff --check` passed. +- Active focus: Fixed the Windows-only `master` CI failure (`extension applies on-demand stable ID tracking policy`) caused by CRLF checkouts breaking `\n`-based multi-line source-scanning assertions. +- Root cause + fix: no repo `.gitattributes` + `core.autocrlf=true` ⇒ CRLF working tree; added `.gitattributes` (`* text=auto eol=lf`) to force LF checkout everywhere, and a `readSource()` EOL-normalizing helper in `test/extension.tests.js` for robustness against existing CRLF copies. No runtime code changed. +- Verification: isolated worktree off `origin/master` on Windows (CRLF working tree) reproduced the failure, then `node qunit test/extension.tests.js` (15 passing), `npm test` (145 passing), and `npm run webpack` all passed; `git check-attr` confirms `eol=lf`; staged diff is only `.gitattributes` + `test/extension.tests.js`. ## Active Session Task -| Priority | Task | Status | Owner | Due | -| -------- | ------------------------------------- | ------------- | ----- | ---------- | -| P1 | Rework glass highlight transparency model | Completed | AI | 2026-06-04 | +| Priority | Task | Status | Owner | Due | +| -------- | ---------------------------------------- | --------- | ----- | ---------- | +| P1 | Fix Windows CRLF `master` CI test failure | Completed | AI | 2026-07-08 | diff --git a/agent/timeline.md b/agent/timeline.md index 23e3d39..1e19f28 100644 --- a/agent/timeline.md +++ b/agent/timeline.md @@ -2,6 +2,7 @@ | 日期时间 | 任务/变更 | 修改文件 | 实现逻辑 | 修改动机 | 结果/备注 | | ---------------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 2026-07-08 14:33 | 修复 Windows CRLF 导致的 master CI 测试失败 | .gitattributes(新增), test/extension.tests.js | 定位 `origin/master` CI 仅 `windows-latest` 失败于 `extension applies on-demand stable ID tracking policy`(`test/extension.tests.js:158`);根因为仓库无 `.gitattributes` 且 Windows `core.autocrlf=true` 使源码以 CRLF 检出,而该断言用 `indexOf` 匹配含 `\n` 的跨行字符串,CRLF 工作树下永不命中(LF 命中、CRLF 未命中已本机复现)。修复:新增仓库根 `.gitattributes`(`* text=auto eol=lf` + `*.js/*.json/*.md/*.yml/*.svg` 显式 LF + 图片/字体 `binary` 保护)根治各平台检出换行;并在 `test/extension.tests.js` 引入 `readSource()`(读后 `replace(/\r\n/g,'\n')`),将 12 处源码扫描断言改为经其读取,使既有 CRLF 工作副本亦健壮。仅动测试与属性文件,未改运行时代码 | `05bb7a7`(glass/alpha 高亮修正,本身无辜)推送触发 master CI,首次在 Windows 上暴露 `9ae1b2b` 引入的既有跨平台测试缺陷;需在不改运行时逻辑前提下修复 CI 并根治此类 EOL 敏感回归 | 已完成;隔离 worktree(基于 `origin/master`)在本机 Windows(CRLF 工作树)先复现 `not ok ... stable ID tracking policy`,应用修复后 `node qunit test/extension.tests.js`(15 passing)、`npm test`(145 passing)、`npm run webpack` 均通过;`git check-attr` 确认 `eol=lf`;`git diff --cached --stat` 仅 `.gitattributes` 与 `test/extension.tests.js` 两文件、无内容 churn | | 2026-06-16 13:14 | 跨模型 deep-research:调研升级方向并产出 roadmap | agent/reports/taskvision-upgrade-research-2026-06-16.md(仅新增,未改源码) | deep-research 方法论主循环复刻(websearch+gh+codex search 三通道核实竞品与 2026 AI 任务管理趋势),定位差异化空缺=「扫真实代码 TODO+状态机+持久源锚+富上下文+安全 agent surface」完整组合无成熟者;经 mcp__codex__codex 跨模型对抗(REVISE/15 条)reconcile,P0 重构为 LanguageModelTool 或 MCP 双 surface + 只读共享核心 + 安全护栏(P2→P0)+ 类型化窄适配层 | 用户要求「与 codex 一起 deep-research 找网络/GitHub 热门相关项目对项目优化升级」 | 已完成;关键事实三通道核对、核心论断经 codex 对抗降级精确化;codex 反例 CodeGraph 经核实驳回(纯语义图不碰 TODO),ContextCode(13 装机/MCP 与 TODO 树割裂)精确化;未改源码/构建;后续可据 P0 落地 | | 2026-06-04 14:47 | 深化玻璃高亮透明度模型 | src/highlights.js, src/schemes.js, test/highlights.tests.js, test/schemes.tests.js, README.md, README_zh.md, package.json, agent/tasks.md, agent/timeline.md | 将 8 位 hex 背景从“继承 alpha”改为“颜色提示 + 显式填充控制”;默认 glass scheme 不再生成 `backgroundColor` 或 border,避免背景 decoration 覆盖字形;默认 acrylic 改为文字层轻辉光和下划线,不再使用背景块;显式 `glassOpacity` / `glassBorderOpacity` / `glowOpacity` 仍可打开对应效果;将默认 `glassType` 收敛为 `tag`;把 `null` / `0` opacity 视为未设置,修复 VS Code 未配置 number 返回 0 时把文字变透明的问题;文档说明默认无填充/无边框、文字层 acrylic 和 `tag` / `line` / `whole-line` 的视觉差异 | 用户截图显示高亮仍是彩色长条且短徽标仍遮住标签字形;根因先后定位为默认 fill/border 覆盖、VS Code 默认 number 配置返回 0 导致文字被透明化,以及需要在不使用背景块的前提下恢复 acrylic 质感 | 已完成;验证执行 `npx qunit test/highlights.tests.js test/schemes.tests.js`(30 passing)、`npm test`(145 passing)、`npm run webpack`,均通过;CDP 截图 `/tmp/taskvision-cdp-visual-final-pass2.png` 显示无长彩条/无矩形背景块/文字可读,视觉子 agent 验收通过;`dist/extension.js` 已刷新;目标文件 `git diff --check` 通过 | | 2026-06-03 19:10 | 纠正 F5 高亮回归根因并恢复 Marketplace 语义 | src/highlights.js, test/highlights.tests.js, agent/tasks.md, agent/timeline.md | 以 Marketplace 2.0.1 正常、F5 当前构建异常为基线重新排查;撤销新增的 legacy `type` 到四通道范围 fallback,恢复 `colorType` / `glowType` / `glassType` / `fontType` 默认语义,避免旧配置 `type: tag` 把文字颜色从 `text` 压成 `tag`、把玻璃背景从 `whole-line` 压成 `tag`;同时修正未配置 ruler opacity 时生成 `rgba(...,NaN)` 的无效颜色 | 用户明确指出 Marketplace 正式版没有高亮显示问题,F5 新版本才异常,需要推翻上一轮错误假设并重新准确定位真实回归 | 已完成;新增 editor-level mock 覆盖 `triggerHighlight()` 到 `setDecorations()` 的可见样式输出,验证文字颜色、text-shadow、玻璃背景、边框和非空 ranges;验证执行 `npx qunit test/highlights.tests.js`(12 passing)、`npm test`(136 passing)、`npm run webpack`、`git diff --check`,均通过;本地 F5 加载的 `dist/extension.js` 已刷新并确认不再包含错误 fallback | diff --git a/test/extension.tests.js b/test/extension.tests.js index ae50058..2174ada 100644 --- a/test/extension.tests.js +++ b/test/extension.tests.js @@ -1,7 +1,11 @@ var fs = require('fs'); +function readSource(file) { + return fs.readFileSync(file, 'utf8').replace(/\r\n/g, '\n'); +} + QUnit.test('extension resolves highlight updates to workspace first', function (assert) { - var source = fs.readFileSync('src/extension.js', 'utf8'); + var source = readSource('src/extension.js'); assert.ok(source.indexOf('function resolveConfigTarget()') !== -1); assert.ok(source.indexOf('vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0') !== -1); @@ -10,14 +14,14 @@ QUnit.test('extension resolves highlight updates to workspace first', function ( }); QUnit.test('extension writes customHighlight updates using resolved target', function (assert) { - var source = fs.readFileSync('src/extension.js', 'utf8'); + var source = readSource('src/extension.js'); assert.ok(source.indexOf("currentConfig.update('customHighlight', updated, customHighlightTarget);") !== -1); assert.ok(source.indexOf("cfg.update('customHighlight', updated, customHighlightTarget);") !== -1); }); QUnit.test('extension exposes independent color/glow/glass/font scope controls', function (assert) { - var source = fs.readFileSync('src/extension.js', 'utf8'); + var source = readSource('src/extension.js'); assert.ok(source.indexOf("value: 'scope-color'") !== -1); assert.ok(source.indexOf("value: 'scope-glow'") !== -1); @@ -30,7 +34,7 @@ QUnit.test('extension exposes independent color/glow/glass/font scope controls', }); QUnit.test('extension refreshes when regex and global opacity settings change', function (assert) { - var source = fs.readFileSync('src/extension.js', 'utf8'); + var source = readSource('src/extension.js'); assert.ok(source.indexOf('if (e.affectsConfiguration("taskvision.regex.regex")) {\n return;\n }') === -1); assert.ok(source.indexOf('e.affectsConfiguration("taskvision.highlights.foregroundOpacity")') !== -1); @@ -40,7 +44,7 @@ QUnit.test('extension refreshes when regex and global opacity settings change', }); QUnit.test('extension updates font appearance in a single customHighlight write', function (assert) { - var source = fs.readFileSync('src/extension.js', 'utf8'); + var source = readSource('src/extension.js'); assert.ok(source.indexOf('var updateConfigValues = function (values)') !== -1); assert.ok(source.indexOf("updateConfig('fontWeight', font.fontWeight);\n updateConfig('fontStyle', font.fontStyle);") === -1); @@ -48,7 +52,7 @@ QUnit.test('extension updates font appearance in a single customHighlight write' }); QUnit.test('extension source accepts built-in taskvision icons during validation', function (assert) { - var source = fs.readFileSync('src/icons.js', 'utf8'); + var source = readSource('src/icons.js'); assert.ok(source.indexOf("!octicons[icon] && icon !== 'taskvision' && icon !== 'taskvision-filled'") !== -1); }); @@ -85,7 +89,7 @@ QUnit.test('package localizes contributed command titles', function (assert) { }); QUnit.test('buildCodiconNames fails instead of overwriting codicons with a minimal fallback', function (assert) { - var source = fs.readFileSync('buildCodiconNames.js', 'utf8'); + var source = readSource('buildCodiconNames.js'); assert.ok(source.indexOf('process.exitCode = 1') !== -1); assert.ok(source.indexOf('writeMinimalFile') === -1); @@ -136,7 +140,7 @@ QUnit.test('package routes tree context menus by task/context/review node types' }); QUnit.test('tree source differentiates task/context/review labels and context values', function (assert) { - var source = fs.readFileSync('src/tree.js', 'utf8'); + var source = readSource('src/tree.js'); assert.ok(source.indexOf('treeItem.contextValue = "task";') !== -1); assert.ok(source.indexOf('treeItem.contextValue = "context";') !== -1); @@ -149,7 +153,7 @@ QUnit.test('tree source differentiates task/context/review labels and context va }); QUnit.test('extension applies on-demand stable ID tracking policy', function (assert) { - var source = fs.readFileSync('src/extension.js', 'utf8'); + var source = readSource('src/extension.js'); assert.ok(source.indexOf('function shouldEnsureStableIdForNode(node, options, referencedStableIds)') !== -1); assert.ok(source.indexOf('if (ensureOptions.forceStableIds === true)') !== -1); @@ -160,7 +164,7 @@ QUnit.test('extension applies on-demand stable ID tracking policy', function (as }); QUnit.test('extension limits forced stable IDs to the AI export scope', function (assert) { - var source = fs.readFileSync('src/extension.js', 'utf8'); + var source = readSource('src/extension.js'); assert.ok(source.indexOf('function getSyncNodesForRoot(rootPath, syncOptions)') !== -1); assert.ok(source.indexOf('Array.isArray(options.scopeNodes)') !== -1); @@ -169,7 +173,7 @@ QUnit.test('extension limits forced stable IDs to the AI export scope', function }); QUnit.test('extension writes AI status report before updating export baseline', function (assert) { - var source = fs.readFileSync('src/extension.js', 'utf8'); + var source = readSource('src/extension.js'); var exportStart = source.indexOf('function exportAiContext(node)'); var markIndex = source.indexOf('taskMetaStore.markTasksExported', exportStart); var reportIndex = source.indexOf('aiContext.writeStatusReport', exportStart); @@ -181,7 +185,7 @@ QUnit.test('extension writes AI status report before updating export baseline', }); QUnit.test('extension registers commands even when ripgrep is unavailable', function (assert) { - var source = fs.readFileSync('src/extension.js', 'utf8'); + var source = readSource('src/extension.js'); var missingRipgrepIndex = source.indexOf("TaskVision: Failed to find vscode-ripgrep"); var firstCommandIndex = source.indexOf("vscode.commands.registerCommand('taskvision.openUrl'"); var missingRipgrepBlock = source.slice(missingRipgrepIndex, firstCommandIndex);