You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
finding(cli): app-generator's two tests pin a DIFFERENT generator branch depending on cwd — so CI never covers the standalone branch #7807
{
"issue": 7807,
"status": "done",
"branch": "claude/issue-7807-app-generator-inworkspace-case",
"pr": "https://github.com/objectstack-ai/objectui/pull/7810",
"premise_still_valid": true,
"summary": "Added two cases to the 'generation onto disk' block of packages/cli/src/__tests__/app-generator.test.ts that NAME the generator context branch instead of inheriting it from the ambient cwd: one pins IN-WORKSPACE against the file-derived REPO_ROOT (the file's existing import.meta.url walk-up, PR 7796's spelling, not a third one), the other pins STANDALONE against the test's own mkdtemp directory, both sides naming the same path. The existing two cases are byte-identical: the only removed line in the entire diff is the vitest import, which gains vi. process.chdir() is not an available lever here - it throws ERR_WORKER_UNSUPPORTED_OPERATION under the unit project's pool: 'threads' (measured in this container with a worker_threads probe) - so process.cwd() is stubbed for exactly the one synchronous writer call and restored in a finally. I added the second, explicitly-STANDALONE case because one explicit IN-WORKSPACE case alone would not equalise coverage: from the repo root the ambient pair also pins IN-WORKSPACE, so STANDALONE would still be unreached in the only form CI runs. Two files changed (GitHub changed_files = 2, matching the recorded base a617bb8d3): the test file and an empty-frontmatter changeset. No non-test file under packages/*/src was touched, and packages/cli/package.json's test script is untouched - the Clause-2 void condition did not trigger.",
"tests": "All readings below are at the final commit afa1722c9 (git rev-parse --short HEAD), working tree clean, and every exit code was captured by redirecting first (cmd > log 2>&1; EXIT=$?), never through a pipe. BOTH-CWD COUNTS, before -> after for the file: (A) repo root, 'pnpm exec vitest run packages/cli/src/__tests__/app-generator.test.ts': before 'Test Files 1 passed (1)' / 'Tests 41 passed (41)' exit 0 -> after 'Test Files 1 passed (1)' / 'Tests 43 passed (43)' exit 0. (B) cwd packages/cli, 'pnpm exec vitest run --root ../.. packages/cli/src/__tests__/app-generator.test.ts': before '1 passed (1)' / '41 passed (41)' exit 0 -> after '1 passed (1)' / '43 passed (43)' exit 0. DECLARED NARROWING: (B) is the criterion-named package-dir command narrowed to this one file. I also ran the unnarrowed criterion form, 'pnpm --filter @object-ui/cli test' (cwd packages/cli, whole package): before 'Test Files 16 passed (16)' / 'Tests 246 passed (246)' exit 0 -> after 'Test Files 16 passed (16)' / 'Tests 248 passed (248)' exit 0. The narrowing was necessary because an appended path argument cannot narrow that form - Vitest unions positional filters with the script's own 'packages/cli/', so all 16 files still ran (measured: 16 files / 246 tests with the path appended). +2 exactly in every column, and no case went from passing to failing; a --reporter=verbose run at afa1722c9 lists all five cases of the block, including the untouched original two, with a check mark. LIT CONTROL: mutation writingFrom(REPO_ROOT, ...) -> writingFrom(process.cwd(), ...), one occurrence, under trap '... git checkout HEAD -- ABSOLUTE_PATH' EXIT INT TERM with the absolute path taken from git rev-parse --show-toplevel. Mutation confirmed ON DISK before either leg ran: anchor counts flipped OLD=1 NEW=0 -> OLD=0 NEW=1, and the blob hash moved - HEAD_BLOB=f50502442602fdc45a8cf07df185283cc4060158, PRE_HASH=f50502442602fdc45a8cf07df185283cc4060158 (equal, so the tree really was at HEAD), MUT_HASH=f9c5acd8baa1096eed34f439a14b6656296b23ea (differs). Neither hash was empty; the script treats an empty hash as FAILURE and exits. Leg A (cwd packages/cli): RED AND NAMES ITSELF - 'FAIL |unit| packages/cli/src/__tests__/app-generator.test.ts > generation onto disk > writes the IN-WORKSPACE map when the generator runs at a workspace root', 'AssertionError: expected { ...(8) } to deeply equal { ...(8) }', 'Tests 1 failed | 42 passed (43)', exit 1. Leg B (repo root): green, '43 passed (43)', exit 0 - which is the finding restated: under the ambient form the only invocation CI runs stays green while the branch it pins has silently changed. RESTORATION PROVED BY STATE, not by whether the trap fired: POST_HASH=f50502442602fdc45a8cf07df185283cc4060158 (equal to HEAD_BLOB), 'git diff HEAD --stat' produced 0 bytes, 'git status --porcelain' empty. NO REBUILD LEG APPLIES and none was run: the test imports the subject as '../utils/app-generator.js', a relative source specifier Vite resolves to the .ts source, so no dist/ artifact sits between the mutation and the run. TYPE-CHECK THROUGH TURBO: 'pnpm exec turbo run type-check --filter=@object-ui/cli --concurrency=2' -> 'Tasks: 10 successful, 10 total', exit 0. (A bare tsc --noEmit is not this repo's gate; the turbo task dependsOn ^build.) CHANGESET GATE, verdict lines verbatim. Before: '❌ 1 source file(s) of 1 released package(s) changed, and this change adds no changeset:'. So it was owed; I added .changeset/cli-app-generator-explicit-branch-cases.md with an EMPTY frontmatter. After: '✅ 1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s): .changeset/cli-app-generator-explicit-branch-cases.md.' followed by 'Every one of them has an EMPTY frontmatter - declared as releasing nothing, which is the explicit exemption and a complete answer to this gate.', exit 0. 'node scripts/check-changeset-no-major.mjs' also exit 0: '✅ No changeset declares a `major` bump.' LINT: eslint on the changed file only - 1 file linted, 0 errors, 0 warnings, exit 0. Repo-wide 'pnpm lint' was NOT run; that is CI's run. CONTROL-CHARACTER SCAN: grep -naP over the changed test file and over the PR body found nothing (exit 1). No objectui gate script named check-nul-bytes.mjs exists in this repo, so the scan is the manual form.",
"mcp_calls": "1 - a single search_issues, for the out-of-scope dedup check; it returned 'API rate limit already exceeded for user ID 318158314' and yielded no reading. Every other GitHub read and write in this run went through repo-scoped REST (issue body, comments, PR create, PR body read-back, this comment).",
"open_questions": [],
"out_of_scope_findings": [
"NOT FILED - both dedup channels were unavailable, handing this to PM to file. Finding: a path argument appended to a package's test script silently WIDENS instead of narrowing. Measured: 'pnpm --filter @object-ui/cli test packages/cli/src/__tests__/app-generator.test.ts' ran 'Test Files 16 passed (16)' / 'Tests 246 passed (246)' - the whole package - because the script is 'vitest run --root ../.. packages/cli/' and Vitest UNIONS positional filters, so the script's own 'packages/cli/' filter re-admits every file. It exits 0 and reads as a green narrowed run, and an agent reporting 'the file is green, 246 tests' would be reporting the package's count as the file's. This is a third member of the family scripts/vitest-invocation-guard.mjs already documents as traps 1 and 2, and it is documented nowhere: I grepped AGENTS.md and the guard's docstring and neither mentions it. Related to objectui#7800 (the test script's cwd devx decision, still open) - a decision there could remove the baked-in filter and with it this hazard - so it may belong as a sub-issue of that card rather than standalone. Suggested shape: label 'finding', no assignee, no pm:queue. WHY I DID NOT FILE IT: the required search-before-filing could not be performed. The repo-scoped REST read channel works in this seat, but the REST search endpoint returned HTTP 403 for both the real query and a known-hit control query; the fallback single targeted MCP search_issues then failed with 'API rate limit already exceeded for user ID 318158314'. With no working dedup channel I did not file blind."
]
}
Activity
claude commentedon Sep 5, 2026
Claim: PM loop, round 6 — all-opus lane (Clause-②⚠️ Void condition: 任何
no)Session:
session_01KbJQ1y1J12nZxYzFWhP8Q3Branch:
claude/issue-7807-app-generator-inworkspace-caseWorktree:
objectui-issue-7807Domain:
domain:uiFile surface:
packages/cli/src/__tests__/app-generator.test.ts· 本卡自己的.changeset/*.md. ⛔ Stop on breach: 任何非测试文件的改动(含createTempApp/contextOfCurrentProcess的实现)、任何packages/cli/package.json的test脚本改动、任何改动现有那两条测试的行为。Explain in the report.Container & model: S,
mode:subagent,model: opusClause-②: no —— 新增一条测试用例。不改变契约接受/拒绝行为,不扩大公开面。
packages/*/src下非测试文件的改动。Serial constraints cleared: **PR #7806(卡 #7799)**已交付,它判定
app-generator.test.tsimmune 并未修改它(本卡正是从它的探针读数析出的);若 #7806 尚未落地也不阻塞(本卡加的是新用例,不碰它判过的东西)。在飞的 #7798 / #7795 / #7744 / #7745 都与packages/cli/不相交。事实(#7799 的席位实测,⛔ 不必重测)
app-generator.test.ts在两种 cwd 下都绿(各 41 passed),且经反向探针确认非空过。但探针同时暴露:逐字节相同的那两条测试,在两种调用下钉的是不同分支 ——pnpm exec vitest run packages/cli/…(仓根,CI 跑的形式)pnpm --filter @object-ui/cli testpackages/cli探针读数:仓根 41p;包目录
1 failed | 40 passed,AssertionError: PROBE7799 ambient isMonorepo: expected false to be true。⇒ CI 只跑仓根形式 ⇒
STANDALONE分支在 CI 里从来没被覆盖过,而且从读代码上看不出来(看起来两条测试覆盖了两个分支)。⛔ 最要紧的一条:不要"修"现有那两条
contextOfCurrentProcess()有意在比较的两侧都镜像 ambient cwd,而被测的真实createTempApp也用同样方式派生自己的上下文 ⇒ 这个镜像两种 cwd 下都自洽。交付物
加第三条用例,用文件派生的根显式钉住 IN-WORKSPACE 分支,保留原有的 ambient 镜像不动 ⇒ 两个分支在任何调用下都被覆盖。
⭐ 路径派生用从
import.meta.url逐段上溯(PR #7796 落地、#7806 沿用的同一个拼法)。⛔ 不要引入第三种拼法;⛔ 不要用new URL(rel, import.meta.url)—— 它在本仓测试变换里被 Vite 重写成http://localhost:3000/@fs/…,fileURLToPath两种 cwd 下都抛。⭐ 验收 —— 判据是「两种 cwd 下覆盖相同」
pnpm exec vitest run packages/cli/src/__tests__/app-generator.test.ts;包目录pnpm --filter @object-ui/cli test。⇒ 新用例在两边都跑到、都绿。trap … EXIT INT TERM下用git checkout HEAD -- <绝对路径>还原,以 blob hash 相等 + 空git diff HEAD证明 —— ⛔ 证明靠状态,不靠 trap 是否触发;变异前先用 hash 确认落盘(空 hash 读作失败,不读作「没什么可比」)。app-generator.test.ts在两种 cwd 下的用例数对照(改动前各 41p,改动后应为 41+N,且没有任何一条从通过变成不通过)。pnpm --filter @object-ui/cli type-check(走 turbo),报出任务数与 exit code。变更集
改动落在发版包
src/下(虽是测试文件)⇒ 按AGENTS.md:161大概率需要。⛔ 不要凭规则推断:跑node scripts/check-changeset-presence.mjs,判词原样抄进报告;欠就写空 frontmatter。⛔ 永不写major。① 裸
tsc --noEmit不是本仓的门(turbotype-checkdependsOn ^build,到不了非包目录;构建前的TS2882是未满足的前提、不是结果)② 本容器没有「未认证的探测」,匿名样子的POST是真写入,探通道只用GET③scripts/__tests__/check-doc-links.test.ts共享盒上 15s 超时是已知 flake ④ ⛔new URL(rel, import.meta.url)被 Vite 重写(见上)⑤ ⛔ 不用两点式origin/main..HEAD报文件数(并发落地会虚报,实测 4 vs 真实 2),以记录的 base 或 GitHub 的changed_files为准。契约复审
Clause-② 是
no⇒ 不排在契约复审后面。Generated by Claude Code
claude commentedon Sep 5, 2026
os-dev-report
{ "issue": 7807, "status": "done", "branch": "claude/issue-7807-app-generator-inworkspace-case", "pr": "https://github.com/objectstack-ai/objectui/pull/7810", "premise_still_valid": true, "summary": "Added two cases to the 'generation onto disk' block of packages/cli/src/__tests__/app-generator.test.ts that NAME the generator context branch instead of inheriting it from the ambient cwd: one pins IN-WORKSPACE against the file-derived REPO_ROOT (the file's existing import.meta.url walk-up, PR 7796's spelling, not a third one), the other pins STANDALONE against the test's own mkdtemp directory, both sides naming the same path. The existing two cases are byte-identical: the only removed line in the entire diff is the vitest import, which gains vi. process.chdir() is not an available lever here - it throws ERR_WORKER_UNSUPPORTED_OPERATION under the unit project's pool: 'threads' (measured in this container with a worker_threads probe) - so process.cwd() is stubbed for exactly the one synchronous writer call and restored in a finally. I added the second, explicitly-STANDALONE case because one explicit IN-WORKSPACE case alone would not equalise coverage: from the repo root the ambient pair also pins IN-WORKSPACE, so STANDALONE would still be unreached in the only form CI runs. Two files changed (GitHub changed_files = 2, matching the recorded base a617bb8d3): the test file and an empty-frontmatter changeset. No non-test file under packages/*/src was touched, and packages/cli/package.json's test script is untouched - the Clause-2 void condition did not trigger.", "tests": "All readings below are at the final commit afa1722c9 (git rev-parse --short HEAD), working tree clean, and every exit code was captured by redirecting first (cmd > log 2>&1; EXIT=$?), never through a pipe. BOTH-CWD COUNTS, before -> after for the file: (A) repo root, 'pnpm exec vitest run packages/cli/src/__tests__/app-generator.test.ts': before 'Test Files 1 passed (1)' / 'Tests 41 passed (41)' exit 0 -> after 'Test Files 1 passed (1)' / 'Tests 43 passed (43)' exit 0. (B) cwd packages/cli, 'pnpm exec vitest run --root ../.. packages/cli/src/__tests__/app-generator.test.ts': before '1 passed (1)' / '41 passed (41)' exit 0 -> after '1 passed (1)' / '43 passed (43)' exit 0. DECLARED NARROWING: (B) is the criterion-named package-dir command narrowed to this one file. I also ran the unnarrowed criterion form, 'pnpm --filter @object-ui/cli test' (cwd packages/cli, whole package): before 'Test Files 16 passed (16)' / 'Tests 246 passed (246)' exit 0 -> after 'Test Files 16 passed (16)' / 'Tests 248 passed (248)' exit 0. The narrowing was necessary because an appended path argument cannot narrow that form - Vitest unions positional filters with the script's own 'packages/cli/', so all 16 files still ran (measured: 16 files / 246 tests with the path appended). +2 exactly in every column, and no case went from passing to failing; a --reporter=verbose run at afa1722c9 lists all five cases of the block, including the untouched original two, with a check mark. LIT CONTROL: mutation writingFrom(REPO_ROOT, ...) -> writingFrom(process.cwd(), ...), one occurrence, under trap '... git checkout HEAD -- ABSOLUTE_PATH' EXIT INT TERM with the absolute path taken from git rev-parse --show-toplevel. Mutation confirmed ON DISK before either leg ran: anchor counts flipped OLD=1 NEW=0 -> OLD=0 NEW=1, and the blob hash moved - HEAD_BLOB=f50502442602fdc45a8cf07df185283cc4060158, PRE_HASH=f50502442602fdc45a8cf07df185283cc4060158 (equal, so the tree really was at HEAD), MUT_HASH=f9c5acd8baa1096eed34f439a14b6656296b23ea (differs). Neither hash was empty; the script treats an empty hash as FAILURE and exits. Leg A (cwd packages/cli): RED AND NAMES ITSELF - 'FAIL |unit| packages/cli/src/__tests__/app-generator.test.ts > generation onto disk > writes the IN-WORKSPACE map when the generator runs at a workspace root', 'AssertionError: expected { ...(8) } to deeply equal { ...(8) }', 'Tests 1 failed | 42 passed (43)', exit 1. Leg B (repo root): green, '43 passed (43)', exit 0 - which is the finding restated: under the ambient form the only invocation CI runs stays green while the branch it pins has silently changed. RESTORATION PROVED BY STATE, not by whether the trap fired: POST_HASH=f50502442602fdc45a8cf07df185283cc4060158 (equal to HEAD_BLOB), 'git diff HEAD --stat' produced 0 bytes, 'git status --porcelain' empty. NO REBUILD LEG APPLIES and none was run: the test imports the subject as '../utils/app-generator.js', a relative source specifier Vite resolves to the .ts source, so no dist/ artifact sits between the mutation and the run. TYPE-CHECK THROUGH TURBO: 'pnpm exec turbo run type-check --filter=@object-ui/cli --concurrency=2' -> 'Tasks: 10 successful, 10 total', exit 0. (A bare tsc --noEmit is not this repo's gate; the turbo task dependsOn ^build.) CHANGESET GATE, verdict lines verbatim. Before: '❌ 1 source file(s) of 1 released package(s) changed, and this change adds no changeset:'. So it was owed; I added .changeset/cli-app-generator-explicit-branch-cases.md with an EMPTY frontmatter. After: '✅ 1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s): .changeset/cli-app-generator-explicit-branch-cases.md.' followed by 'Every one of them has an EMPTY frontmatter - declared as releasing nothing, which is the explicit exemption and a complete answer to this gate.', exit 0. 'node scripts/check-changeset-no-major.mjs' also exit 0: '✅ No changeset declares a `major` bump.' LINT: eslint on the changed file only - 1 file linted, 0 errors, 0 warnings, exit 0. Repo-wide 'pnpm lint' was NOT run; that is CI's run. CONTROL-CHARACTER SCAN: grep -naP over the changed test file and over the PR body found nothing (exit 1). No objectui gate script named check-nul-bytes.mjs exists in this repo, so the scan is the manual form.", "mcp_calls": "1 - a single search_issues, for the out-of-scope dedup check; it returned 'API rate limit already exceeded for user ID 318158314' and yielded no reading. Every other GitHub read and write in this run went through repo-scoped REST (issue body, comments, PR create, PR body read-back, this comment).", "open_questions": [], "out_of_scope_findings": [ "NOT FILED - both dedup channels were unavailable, handing this to PM to file. Finding: a path argument appended to a package's test script silently WIDENS instead of narrowing. Measured: 'pnpm --filter @object-ui/cli test packages/cli/src/__tests__/app-generator.test.ts' ran 'Test Files 16 passed (16)' / 'Tests 246 passed (246)' - the whole package - because the script is 'vitest run --root ../.. packages/cli/' and Vitest UNIONS positional filters, so the script's own 'packages/cli/' filter re-admits every file. It exits 0 and reads as a green narrowed run, and an agent reporting 'the file is green, 246 tests' would be reporting the package's count as the file's. This is a third member of the family scripts/vitest-invocation-guard.mjs already documents as traps 1 and 2, and it is documented nowhere: I grepped AGENTS.md and the guard's docstring and neither mentions it. Related to objectui#7800 (the test script's cwd devx decision, still open) - a decision there could remove the baked-in filter and with it this hazard - so it may belong as a sub-issue of that card rather than standalone. Suggested shape: label 'finding', no assignee, no pm:queue. WHY I DID NOT FILE IT: the required search-before-filing could not be performed. The repo-scoped REST read channel works in this seat, but the REST search endpoint returned HTTP 403 for both the real query and a known-hit control query; the fallback single targeted MCP search_issues then failed with 'API rate limit already exceeded for user ID 318158314'. With no working dedup channel I did not file blind." ] }Generated by Claude Code
claude commentedon Sep 5, 2026
落地记录 —— PR #7810 已合入(本 lane 今日第 23 次落地)
merged: true@ 2026-09-05T23:32:42Z,squashef4533727#7807closed_at23:32:43Z;后继载体 AGENTS.md claimspnpm --filter FIELD testreaches the same conclusion as CI — falsified twice; the sentence needs a maintainer #7800 / finding(types): 39 handler keys a registered renderer READS are declared by no arm — accepted and KEPT by the passthrough, onmaintoday #7804 / finding(devx): check-skill-examples.mjs 的两处--build-filter命令同样没说它只建一个裁剪过的闭包 #7811 / finding(devx): a path appended to a package'stestscript WIDENS instead of narrowing —pnpm --filter @object-ui/cli test <one file>runs all 16 files and exits 0 #7814 / finding(core):formatRelativeDatestill ignoresoptions.style, and its out-of-window ABSOLUTE fallback is the one place the key is already well-defined #7816 / finding(app-shell): onManageChanged reads a failed package-list refresh as "the package was deleted" and evicts the author to /home #7821 / finding(app-shell): the object-view relay hands ListView a SIX-KEY projection of the chart block, dropping the whole ADR-0021 shape (dataset / dimensions / values) #7823 / finding(app-shell): Studio's "lock the API name on first save" has no real save signal — the draft auto-save is deliberately not counted, leaving one same-mount window open #7824 全部仍 open,逐一复核过⭐ 三条值得留下来的读数
1. ⭐⭐ 点灯对照把这张卡的论点变成了一句可复读的话。
变异
writingFrom(REPO_ROOT, …)→writingFrom(process.cwd(), …)后:packages/cliwrites the IN-WORKSPACE map when the generator runs at a workspace root,Tests 1 failed | 42 passed (43)43 passed (43)⇒ 这正是卡片的论点原样复述:在 CI 唯一会跑的那个调用下它保持绿色,而它所钉住的分支已经悄悄变了。 ⭐ 一个会在事故树上空过的钉子。
2.
process.chdir()在本仓的单元 project 下不可用 —— 这是实测,不是推测。pool: 'threads'下process.chdir()抛ERR_WORKER_UNSUPPORTED_OPERATION(用worker_threads探针在本容器实测)。⇒ 只能对恰好一次同步写调用打桩process.cwd(),并在finally里还原。⛔ 想用chdir写这类测试的下一位不必再试一遍。3. 为什么加了两个用例而不是一个 —— 一个不够,理由是覆盖而不是对称。
只加一个显式 IN-WORKSPACE 用例不能把覆盖补平:从仓库根跑时,原有的那对 ambient 用例也钉 IN-WORKSPACE,于是 STANDALONE 在 CI 唯一会跑的形式下仍然无人触及。⇒ 必须同时加一个显式 STANDALONE 用例(对着测试自己的
mkdtemp目录),两侧指向同一条路径。vi)——「不动既有断言」这件事被做成了可核对的事实。Generated by Claude Code