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.
Triage: classified finding (dead config entry, zero importers, unpublished surface — real but nobody hits it). Two notes for whoever first-touches it: ① it carries an embedded A/B call (delete vs restore the App Data Symlink DX) that needs a one-line ruling before dispatch; ② it is soft-coupled to #5168 — once #5168 lands, this alias is the sole KNOWN_MISSING_PACKAGE_ALIAS_TARGETS entry and its disposal must update that carve-out or #5168's pin reddens, so grade it together with or after #5168's landing.
Triage (objectui finding round): promoted to pm:queue · domain:devx · type Task, direction A (delete the dead alias line) by inheritance from the #3593 sibling ruling — same file, same failure shape (an alias outliving its target), zero consumers measured (the declaration is the repo's only @app reference). Premise-first: if the dev finds evidence the "App Data Symlink" DX is still wanted (any doc, script or workflow referencing it), stop and report instead of deleting — that would fork to direction B. Same PR must remove the KNOWN_MISSING_PACKAGE_ALIAS_TARGETS carve-out entry, or #5168's shrink-only pin goes red. S, sonnet; Clause-②: no.
⚠️ Dissent — pm:retriage. The 2026-08-21 ruling rests on a premise the dev measured FALSE. Claim released, ⛔ nothing deleted.
domain:devx @ objectui seat (#5748), round R6, PM session session_019b5UBNMtTzKbVtZZGvFuxe. Same write: pm:dispatched → pm:queue + pm:retriage, assignee cleared. ⛔ The standing pm:queue label is not stripped, and ⛔ this seat does not re-adjudicate — that is the triage seat's alone.
packages/runner/vite.config.ts:25 and KNOWN_MISSING_PACKAGE_ALIAS_TARGETS are untouched. The folded PR #5966 carries #4820 only.
The premise-first stop condition fired, exactly as designed
The dispatch order said: "if you find any doc, script or workflow showing the 'App Data Symlink' DX is still wanted, stop and report a fork — ⛔ do not delete, and ⛔ do not implement direction B either." The dev stopped and did neither. This is the mechanism working, not a failed run.
What was measured
packages/runner/src/app-data is not a dead pointer. It is a documented, deliberately git-ignored, user-supplied directory:
evidence
content
packages/runner/.gitignore:1
src/app-data
packages/runner/README.md:85
"git-ignored and absent from a fresh checkout, so every load returns null until you copy or symlink your own metadata directory into it" — and Development Workflow step 1 tells the reader to create it
content/docs/utilities/runner.mdx:156
"src/app-data/ is git-ignored … you supply it, by copying or symlinking your own metadata directory there"
packages/runner/src/lib/MetadataLoader.ts:19-28
LocalBundleLoader reads it
Why this invalidates the ruling's grounds rather than merely adding colour
Both directions on this card were argued from facts that do not hold:
Direction B says what is missing is "the symlink convention and the docs". Both already exist, in the four places above. There is nothing to restore.
The narrower statement that is true, and which neither direction saw: the alias has zero importers because LocalBundleLoader consumes the directory through relative import.meta.glob('../app-data/…'), never through @app. So deleting it would not break the documented DX — but "unused alias" is a different justification from the one that was ruled, and a dev seat must not silently re-base a maintainer/triage ruling onto different grounds. Hence pm:retriage rather than a quiet A-with-caveats.
For the triage seat — the options as they now stand
A′ — delete the alias on the narrower measured ground (unused alias; the loader uses relative globs, so nothing breaks), and empty KNOWN_MISSING_PACKAGE_ALIAS_TARGETS. Same action as the standing ruling, different and now-true reasoning. Cost: removes the ergonomic @app entry point for the very DX the docs teach.
B′ — keep the alias and teach the gate that this target is legitimately optional ("expected-absent, user-supplied") rather than carved out pending a card. Preserves the affordance and fixes the pin's checkout-state dependence. Cost: a new gate concept, larger than either card was scoped for.
C — keep the alias, drop only the carve-out. ⛔ Not viable: reddens CI immediately.
D — re-triage from the measurement.
The dev's recommendation, recorded ⛔ not as a ruling: D, then A′ — and if A′, land it with the corrected reasoning in the commit, not the #3593 analogy. Its four-axis read: zero importers today and the documented DX demonstrably does not route through the alias, so the preserved affordance is hypothetical; declared = enforced argues against a line that only reads as wired-up; an alias resolving into a maybe-present directory is exactly the ambiguous surface that produces confident wrong AI output; and B′ invents a gate classification for one entry with no pull behind it.
⚠️ A separate live defect this uncovered — filed, ⛔ not folded in
#5168's shrink-only pin is checkout-state dependent for this entry: it asserts the carved-out target is still declared and still missing, via fs.existsSync. Any developer who follows the README's Development Workflow step 1 and creates src/app-data/ makes the target exist, and the pin fails on their machine. It is green in CI only because CI never creates the directory. That holds under A′, B′ and D alike, so it is not "resolved by whichever direction wins" until this card actually moves. Filed separately rather than left in a report.
Retriage re-ruling (triage seat, session session_01Nd9biL1MqkZT5tzgTEp7AB, 2026-08-24). Dissent upheld — re-ruled A′.pm:retriage removed in the same stroke; pm:queue and type Task stand.
What the dissent proved: the 2026-08-21 direction-A ruling was grounded on the #3593 inheritance ("an alias outliving its target"), and that premise is measured false — #3593's target was a deleted package; this target is a documented, deliberately git-ignored, user-supplied directory (.gitignore:1, README.md:85, runner.mdx:156, MetadataLoader.ts:19-28). Direction B's premise ("the symlink convention and docs are missing") is equally false — both exist. Neither original direction survives on its stated grounds.
Re-ruling — A′, the same action on the narrower measured ground: delete the "@app" alias line because it has zero importers and the documented App Data Symlink DX demonstrably does not route through it (LocalBundleLoader consumes the directory via relative import.meta.glob('../app-data/…'), never via @app). Deleting the line changes no behaviour and breaks no documented workflow. Same-PR obligation unchanged: empty the KNOWN_MISSING_PACKAGE_ALIAS_TARGETS entry so #5168's pin stays green.
B′ rejected on the four axes: it invents a gate classification ("expected-absent, user-supplied") for a single entry with no pull behind it, to preserve an affordance that is measured hypothetical (zero importers today). Startup-focus and declared=enforced both point at A′; the dev's own four-axis read agrees.
Premise-first stop, rewritten to match: if any doc, script or workflow teaches the @app alias itself (not the directory) as an entry point, stop and report a fork.
Note: #5968 (the #5168 pin's checkout-state dependence) stays its own card — A′ incidentally removes the only entry that exhibits it, but the class (fs.existsSync-based still-missing assertions) is #5968's subject, not this card's.
Claimed by the domain:devx @ objectui execution seat (#5748), PM session session_019b5UBNMtTzKbVtZZGvFuxe. Branch: claude/issue-5380-app-alias-removal.
⛔ The ruling is A′ and it is NOT mine to re-open — read the comments, not the body
⚠️This card's body presents an open A/B fork and says the choice is the maintainer's. That is superseded. The history matters and must not be re-litigated:
2026-08-24 — a dev's premise-first stop falsified that ground: packages/runner/src/app-data is not a dead pointer. It is a documented, deliberately git-ignored, user-supplied directory (.gitignore:1, README.md:85, runner.mdx:156, MetadataLoader.ts:19-28). packages/runner/vite.config.ts 的 @object-ui/data-objectql 别名指向不存在的包目录(全仓唯一引用) #3593's target was a deleted package; this one is absent by design in a fresh checkout. ⛔ Direction B's premise was also false — the symlink convention and docs both already exist.
2026-08-24 12:34Z — triage upheld the dissent and re-ruled A′: the same action on the narrower measured ground.
A′: delete the "@app" alias line because it has ZERO importers and the documented App Data Symlink DX demonstrably does not route through it — LocalBundleLoader consumes the directory via relative import.meta.glob('../app-data/…'), never via @app. Deleting the line changes no behaviour and breaks no documented workflow.
⛔ The commit and PR body must carry the corrected ground. Do not cite the #3593 analogy — it is the premise that was measured false, and repeating it would re-enter a wrong reason into the record on a card whose whole history is about that.
Premise re-verified by me just now, on origin/main
packages/runner/vite.config.ts:25 still declares "@app": path.resolve(__dirname, "./src/app-data").
packages/runner/src/app-data is absent from the tree — expected, since it is gitignored and user-supplied. ⛔ Do not read that absence as "dead"; that is precisely the mistake this card already made once.
KNOWN_MISSING_PACKAGE_ALIAS_TARGETS at scripts/__tests__/vitest-config-alias-targets-3944.test.ts:815 still carries the single entry { config: 'packages/runner/vite.config.ts', specifier: '@app' }.
⚠️ Same-PR obligation, and a trap inside it
Remove the carve-out entry in the same PR, or #5168's shrink-only pin reds — it asserts each carved-out entry is still declared and still missing, and after your deletion it is no longer declared.
⭐ But check what an EMPTY list does before you assume removing the entry is enough. That entry is the list's only member, so your change empties it. vitest-config-alias-targets-3944.test.ts:824 already carries a pin whose comment reads "a zero-hit parse would make every case below vacuous" — evidence this file already worries about collapsing to nothing. Read every assertion that consumes KNOWN_MISSING_PACKAGE_ALIAS_TARGETS and determine whether an empty list makes any of them red or, worse, silently vacuous. If a case becomes vacuous, say so and propose the fix rather than shipping a pin that can no longer fail. ⛔ Do not delete the list itself without saying why.
An emptied shrink-only ratchet is a good outcome — #6092 closed exactly that way tonight — but only if the pins around it still mean something at zero.
Premise-first stop, rewritten to match A′
⛔ The old stop condition ("any doc showing the App Data Symlink DX is wanted") is spent — that evidence exists and is precisely why the ruling moved to A′. The live condition is narrower:
If any doc, script or workflow teaches the @app ALIAS ITSELF — not the directory — as an entry point, stop and report a fork.
Nothing may import @app or @app/... either. Re-derive that; the card's "repo's only reference is the declaration itself" is days old.
Scope and coordination
packages/runner/vite.config.ts and scripts/__tests__/vitest-config-alias-targets-3944.test.ts. ⛔ Nothing else.
⚠️#4820's ratchet (KNOWN_MISSING_TARGETS) lives in the same file and the two were once folded into one dispatch. I could not find #4820 in the last 60 commits on main, so check whether it is in flight before you edit — if another branch is touching that file, note the overlap and the union in your PR body per AGENTS.md's shared-surface guidance. ⛔ Do not touch #4820's list.
#5968 stays its own card: A′ incidentally removes the only entry exhibiting the fs.existsSync checkout-state dependence, but the class is #5968's subject. ⛔ Do not fold it in.
Root vitest only (objectui#3378). Exit codes captured by redirect before any pipe. ⚠️ Derive the gate list from package.json and .github/workflows/ rather than from this order.
{
"issue": 5380,
"status": "done",
"branch": "claude/issue-5380-app-alias-removal",
"pr": "https://github.com/objectstack-ai/objectui/pull/6197",
"premise_still_valid": true,
"summary": "Implemented A' as re-ruled. Deleted the \"@app\" alias and its DX comment from packages/runner/vite.config.ts on the corrected ground only (zero importers; LocalBundleLoader consumes src/app-data via relative import.meta.glob('../app-data/...'), never via the alias) -- the #3593 analogy is cited nowhere in the commit or PR body, and the code comment now records explicitly that src/app-data is user-supplied and gitignored BY DESIGN, not a dead target, so the retired reasoning cannot be re-derived. Premise re-derived fresh, not inherited: a repo-wide sweep for '@app' returns exactly three hits (the declaration plus two prose lines in the ratchet file); nothing imports @app or any subpath; README.md and runner.mdx teach the directory path and never the alias, so the rewritten stop condition did not fire. Same-PR obligation met and its consequence analysed rather than assumed -- see open_questions[0] for the vacuity finding and the fix shipped for it. Two process notes for PM: (1) the single commit carries a 'Part of #5380' trailer while the PR body carries 'Fixes #5380' -- the trailer should not have been in the commit under squash discipline, and I did not amend because that would require a force-push, which is absolutely forbidden; the card still closes correctly via the PR body and nothing contradicts. (2) Direct REST via curl is unavailable from this seat ('GitHub access is not enabled for this session'), so all GitHub writes went through the MCP tools; objectui has no skip-changeset label and none was owed anyway.",
"tests": "All gates run on branch head bbcc46e7a (tree unchanged since the commit; git status clean). Heavy runs serialised through scripts/pm/os-verify-lock.sh; exit codes captured by redirect before any pipe; root vitest only. GREEN: vitest run on scripts/__tests__/vitest-config-alias-targets-3944.test.ts + scripts/__tests__/side-effects-declaration-consistency.test.ts + packages/runner/ -> exit 0, 'Test Files 5 passed (5)', 'Tests 332 passed (332)'. pnpm lint:root -> exit 0 ('28 problems (0 errors, 28 warnings)', all pre-existing no-explicit-any in untouched files). eslint . in packages/runner -> exit 0 (0 errors, 20 warnings). pnpm type-check:scripts -> exit 0, and tsc --listFiles confirms the edited test file IS in that program (count 1), so the green is not vacuous. pnpm exec vite build in packages/runner -> exit 0, '4459 modules transformed', 'built in 5.49s' -- this is the one gate that actually consumes the changed file. pnpm check:control-bytes -> exit 0 (scanned 5113 tracked text files). pnpm changeset:check -> exit 0. node scripts/check-changeset-presence.mjs -> exit 0, 'No source of a released package changed in this range, so no changeset is owed' (2 files changed, 0 published source), so no changeset was added. ATTRIBUTED RED: packages/runner 'pnpm type-check' first exited 2 with TS2307 'Cannot find module @object-ui/types' plus implicit-any fallout -- the unbuilt-dependency-dist trap in a fresh worktree, not this diff. Attribution established BEFORE rerunning: tsc --listFiles shows packages/runner/vite.config.ts is not in that program at all (tsconfig.json includes only 'src', count 0) and every erroring file is untouched by this PR; after turbo run build --filter=@object-ui/runner^... (14 tasks successful) the same command exits 0. TWO DIRECTED REVERSE-VERIFICATIONS, both with the mutation confirmed on disk (this test reads files from disk and does not resolve through any package dist, so no rebuild leg applies; the confirmation step was still run in both directions because an editor exit code is not evidence -- and it caught a real no-op: my first perl deletion of the alias matched zero times and exited 0, leaving the file untouched, which the grep -c anchor check detected). (a) Same-PR obligation is real: after deleting the alias but BEFORE touching the carve-out, the ratchet file went red exactly as predicted -- 'x the objectui#5168 carve-out still describes exactly the entries it was written for', 'Tests 1 failed | 288 passed (289)'. (b) The NEW emptiness assertion can fail: re-populating the emptied list with one probe entry (injected marker present 1, empty-list declaration present 0 -- confirmed on disk before the run) turned it red with the intended message -- 'x the objectui#5168 carve-out is empty, so no package alias entry is exempt', 'AssertionError: KNOWN_MISSING_PACKAGE_ALIAS_TARGETS is a shrink-only ratchet that reached empty when objectui#5380 landed ... expected [ { ...(2) } ] to deeply equal []', 'Tests 1 failed | 288 passed (289)'. The restore leg ran from a trap EXIT/INT/TERM and was confirmed on disk by absent-marker (probe present 0, empty-list declaration present 1) with git status clean. After both legs the file is green: 'Test Files 1 passed (1)', 'Tests 289 passed (289)'.",
"open_questions": [
{
"question": "The emptied list makes BOTH of the carve-out pin's existing expectations silently vacuous. Reported per the dispatch order's instruction to say so and propose the fix rather than ship an unfailable pin -- the fix is already implemented in this PR, so this is a finding to review, not a blocker.",
"options": [
"Read of all three consumers of KNOWN_MISSING_PACKAGE_ALIAS_TARGETS at zero: (1) isCarvedOut, which filters the per-entry existence it.each -- STRONGER at zero, nothing is filtered so every package alias entry is now guarded with no exemptions, and no case goes red because the @app entry is deleted from the config too; (2) the pin's 'declared' expectation -- SILENTLY VACUOUS, it filters an empty list and compares [] with [], so it cannot fail for any repo state; (3) the pin's 'nowResolving' expectation -- SILENTLY VACUOUS, isCarvedOut is false for everything so it is [] unconditionally. Neither (2) nor (3) can ever red again.",
"A: ship the emptied list as-is -- rejected, that is precisely a pin that can no longer fail, the trap this same file guards against three cases earlier ('a zero-hit parse would make every case below vacuous').",
"B (SHIPPED): keep the list and add a leading assertion on emptiness ITSELF, which does bite at zero, plus an in-line comment stating the two expectations below are knowingly vacuous rather than accidentally so. This is the exact shape #4820 already left on KNOWN_MISSING_TARGETS in this same file when it reached empty in cb83cec1a / #5966 -- in-repo precedent, same file, same author intent, not an invention.",
"C: delete the list and its pin entirely -- rejected, and the reason is recorded rather than left implicit as the dispatch order required: deleting it removes the subject of the emptiness assertion and unwires isCarvedOut, which turns a future re-population from a visible act that reds into an ordinary edit nobody sees."
],
"recommendation": "B, shipped and ablated. The emptiness assertion is proven non-vacuous by reverse-verification (b) above. Empty is the ratchet's terminal state and re-populating it now needs a card of its own, which the failure message says."
}
],
"out_of_scope_findings": []
}
Coordination checked, as the order asked.#4820 is not in flight — it already landed as cb83cec1a (#5966); KNOWN_MISSING_TARGETS is already [] on main, and this PR does not touch it (git diff BASE..HEAD | grep -c KNOWN_MISSING_TARGETS returns 0). A scan of the 40 most recently active remote branches for commits touching either file since their merge-base with main found none, so there is no union to declare in the PR body. #5968 untouched and left as its own card; the new code comment names it explicitly so nobody reads the emptied ratchet as a fix for the fs.existsSync checkout-state dependence class.
CI had not converged when this report was filed — per dispatch contract the report lands at draft-PR time and gate convergence is PM's read.
PM review — ACCEPTED. PR #6197 armed. ⭐ The vacuity check found a real defect, and the fix has in-repo precedent I verified.
Reviewed against a pinned ref. 15 green, 0 failures, 8 running.
The ruling landed on the corrected ground, and the retired premise is now un-re-derivable
A′ implemented: the "@app" line and its DX: App Data Symlink comment are gone, on the measured ground only — zero importers, and LocalBundleLoader reads the directory through relative import.meta.glob('../app-data/…'), never through the alias. The #3593 analogy appears nowhere in the commit or PR body, as ordered.
⭐ Better than ordered: the replacement comment block writes the correction into the file, so the falsified premise cannot be re-derived by the next reader:
⚠️src/app-data/ is NOT a dead target. It is git-ignored and user-supplied … absent from a fresh checkout by design. What was dead was the unused alias, not the thing it named.
That is the exact mistake this card made once and had to be corrected by a dissent. Recording it at the site means the third attempt starts from the truth.
Premise re-derived rather than inherited: a repo-wide @app sweep returns exactly three hits — the declaration plus two prose lines in the ratchet file. README.md and runner.mdx teach the directory path and never the alias, so the rewritten stop condition correctly did not fire.
⭐ The vacuity finding — this is why the check was in the order
I asked them to read every consumer of the list and say whether an empty list makes any assertion red or silently vacuous. It makes both existing expectations vacuous:
consumer
at zero
isCarvedOut filtering the per-entry existence check
⭐ stronger — nothing is filtered, so every alias entry is now guarded with no exemptions
the pin's declared expectation
⛔ silently vacuous — filters an empty list, compares [] with []
the pin's nowResolving expectation
⛔ silently vacuous — isCarvedOut is false for everything, so [] unconditionally
Neither can ever red again. Shipping the emptied list as-is would have left two unfailable assertions wearing the appearance of coverage — in a file that guards against exactly that three cases earlier ("a zero-hit parse would make every case below vacuous").
The fix: an assertion on emptiness itself, which does bite at zero, plus an in-line comment stating the two below are knowingly vacuous rather than accidentally so. Option C (delete the list and pin entirely) was rejected with the reason recorded rather than left implicit: deleting it unwires isCarvedOut and turns a future re-population from a visible act that reds into an ordinary edit nobody sees.
⭐ I verified the precedent claim rather than taking it
They justified the shape as "the exact shape #4820 already left on KNOWN_MISSING_TARGETS in this same file". Checked on main:
KNOWN_MISSING_TARGETS is already [] (line 255).
Line 349 carries the identical emptiness assertion, with the same message shape: "is a shrink-only ratchet that reached empty when objectui#4820 landed. Re-populating it needs a card of its own — a new dead target is meant to go red here, not to be carved out."
Same file, same author intent, one list over. Not an invention — and it also settles the coordination question from my order: #4820 has landed, so there is no in-flight conflict on this file. My "not in the last 60 commits" reading was simply too shallow a window; the file itself is the better evidence.
Both directions ablated: (a) deleting the alias before touching the carve-out reds the existing pin, proving the same-PR obligation is real, not ceremonial; (b) re-populating the emptied list reds the new assertion with the intended message — proving it is not itself vacuous.
⭐ The anchored-count discipline caught a real no-op
my first perl deletion of the alias matched zero times and exited 0, leaving the file untouched, which the grep -c anchor check detected
That is the trap in its natural habitat: a zero-hit edit exits 0 and lies. Had they trusted the exit code, the "fix" would have been an empty diff with a full green verification run behind it. This is the third sighting of that class tonight and the first where it actually fired.
The attributed red was handled the same way: packages/runner's type-check first exited 2 with TS2307, and attribution was established before re-running — tsc --listFiles shows vite.config.ts is not in that program at all and every erroring file is untouched by the PR. Unbuilt-dependency-dist, not this diff.
⚠️ Two process notes, both correctly self-reported
The commit carries a Part of #5380 trailer while the PR body carries Fixes #5380. Under squash discipline the trailer should not have been in the commit — but not amending was the right call: fixing it needs a force-push, which is forbidden. The card still closes correctly via the PR body and nothing contradicts. ⛔ Do not force-push to tidy a trailer.
#5968 correctly stays its own card: this PR removes the only instance of the fs.existsSync checkout-state dependence here, and the comment says so explicitly — "not a fix for the class."
Activity
test(scripts): the #3944 alias-target gate now covers the 24 package-…
os-zhuang commentedon Aug 20, 2026
Triage: classified
finding(dead config entry, zero importers, unpublished surface — real but nobody hits it). Two notes for whoever first-touches it: ① it carries an embedded A/B call (delete vs restore the App Data Symlink DX) that needs a one-line ruling before dispatch; ② it is soft-coupled to #5168 — once #5168 lands, this alias is the soleKNOWN_MISSING_PACKAGE_ALIAS_TARGETSentry and its disposal must update that carve-out or #5168's pin reddens, so grade it together with or after #5168's landing.Generated by Claude Code
os-zhuang commentedon Aug 21, 2026
Triage (objectui finding round): promoted to
pm:queue·domain:devx· type Task, direction A (delete the dead alias line) by inheritance from the #3593 sibling ruling — same file, same failure shape (an alias outliving its target), zero consumers measured (the declaration is the repo's only@appreference). Premise-first: if the dev finds evidence the "App Data Symlink" DX is still wanted (any doc, script or workflow referencing it), stop and report instead of deleting — that would fork to direction B. Same PR must remove theKNOWN_MISSING_PACKAGE_ALIAS_TARGETScarve-out entry, or #5168's shrink-only pin goes red. S, sonnet; Clause-②: no.Generated by Claude Code
test(scripts): the objectstack-ai#3944 alias-target gate now covers t…
6 remaining items
yinlianghui-tw commentedon Aug 24, 2026
pm:retriage. The 2026-08-21 ruling rests on a premise the dev measured FALSE. Claim released, ⛔ nothing deleted.domain:devx@ objectui seat (#5748), round R6, PM sessionsession_019b5UBNMtTzKbVtZZGvFuxe. Same write:pm:dispatched→pm:queue+pm:retriage, assignee cleared. ⛔ The standingpm:queuelabel is not stripped, and ⛔ this seat does not re-adjudicate — that is the triage seat's alone.packages/runner/vite.config.ts:25andKNOWN_MISSING_PACKAGE_ALIAS_TARGETSare untouched. The folded PR #5966 carries #4820 only.The premise-first stop condition fired, exactly as designed
The dispatch order said: "if you find any doc, script or workflow showing the 'App Data Symlink' DX is still wanted, stop and report a fork — ⛔ do not delete, and ⛔ do not implement direction B either." The dev stopped and did neither. This is the mechanism working, not a failed run.
What was measured
packages/runner/src/app-datais not a dead pointer. It is a documented, deliberately git-ignored, user-supplied directory:packages/runner/.gitignore:1src/app-datapackages/runner/README.md:85nulluntil you copy or symlink your own metadata directory into it" — and Development Workflow step 1 tells the reader to create itcontent/docs/utilities/runner.mdx:156src/app-data/is git-ignored … you supply it, by copying or symlinking your own metadata directory there"packages/runner/src/lib/MetadataLoader.ts:19-28LocalBundleLoaderreads itWhy this invalidates the ruling's grounds rather than merely adding colour
Both directions on this card were argued from facts that do not hold:
The narrower statement that is true, and which neither direction saw: the alias has zero importers because
LocalBundleLoaderconsumes the directory through relativeimport.meta.glob('../app-data/…'), never through@app. So deleting it would not break the documented DX — but "unused alias" is a different justification from the one that was ruled, and a dev seat must not silently re-base a maintainer/triage ruling onto different grounds. Hencepm:retriagerather than a quiet A-with-caveats.For the triage seat — the options as they now stand
KNOWN_MISSING_PACKAGE_ALIAS_TARGETS. Same action as the standing ruling, different and now-true reasoning. Cost: removes the ergonomic@appentry point for the very DX the docs teach.The dev's recommendation, recorded ⛔ not as a ruling: D, then A′ — and if A′, land it with the corrected reasoning in the commit, not the #3593 analogy. Its four-axis read: zero importers today and the documented DX demonstrably does not route through the alias, so the preserved affordance is hypothetical; declared = enforced argues against a line that only reads as wired-up; an alias resolving into a maybe-present directory is exactly the ambiguous surface that produces confident wrong AI output; and B′ invents a gate classification for one entry with no pull behind it.
#5168's shrink-only pin is checkout-state dependent for this entry: it asserts the carved-out target is still declared and still missing, via
fs.existsSync. Any developer who follows the README's Development Workflow step 1 and createssrc/app-data/makes the target exist, and the pin fails on their machine. It is green in CI only because CI never creates the directory. That holds under A′, B′ and D alike, so it is not "resolved by whichever direction wins" until this card actually moves. Filed separately rather than left in a report.Generated by Claude Code
os-zhuang commentedon Aug 24, 2026
Retriage re-ruling (triage seat, session
session_01Nd9biL1MqkZT5tzgTEp7AB, 2026-08-24). Dissent upheld — re-ruled A′.pm:retriageremoved in the same stroke;pm:queueand type Task stand.What the dissent proved: the 2026-08-21 direction-A ruling was grounded on the #3593 inheritance ("an alias outliving its target"), and that premise is measured false — #3593's target was a deleted package; this target is a documented, deliberately git-ignored, user-supplied directory (
.gitignore:1,README.md:85,runner.mdx:156,MetadataLoader.ts:19-28). Direction B's premise ("the symlink convention and docs are missing") is equally false — both exist. Neither original direction survives on its stated grounds.Re-ruling — A′, the same action on the narrower measured ground: delete the
"@app"alias line because it has zero importers and the documented App Data Symlink DX demonstrably does not route through it (LocalBundleLoaderconsumes the directory via relativeimport.meta.glob('../app-data/…'), never via@app). Deleting the line changes no behaviour and breaks no documented workflow. Same-PR obligation unchanged: empty theKNOWN_MISSING_PACKAGE_ALIAS_TARGETSentry so #5168's pin stays green.B′ rejected on the four axes: it invents a gate classification ("expected-absent, user-supplied") for a single entry with no pull behind it, to preserve an affordance that is measured hypothetical (zero importers today). Startup-focus and declared=enforced both point at A′; the dev's own four-axis read agrees.
Constraints on the implementing PR:
@appalias itself (not the directory) as an entry point, stop and report a fork.Note: #5968 (the #5168 pin's checkout-state dependence) stays its own card — A′ incidentally removes the only entry that exhibits it, but the class (
fs.existsSync-based still-missing assertions) is #5968's subject, not this card's.Generated by Claude Code
yinlianghui-tw commentedon Aug 25, 2026
PM claim + dispatch order — R27. Implementing triage's A′ re-ruling.
Claimed by the
domain:devx@ objectui execution seat (#5748), PM sessionsession_019b5UBNMtTzKbVtZZGvFuxe. Branch:claude/issue-5380-app-alias-removal.⛔ The ruling is A′ and it is NOT mine to re-open — read the comments, not the body
packages/runner/src/app-datais not a dead pointer. It is a documented, deliberately git-ignored, user-supplied directory (.gitignore:1,README.md:85,runner.mdx:156,MetadataLoader.ts:19-28). packages/runner/vite.config.ts 的 @object-ui/data-objectql 别名指向不存在的包目录(全仓唯一引用) #3593's target was a deleted package; this one is absent by design in a fresh checkout. ⛔ Direction B's premise was also false — the symlink convention and docs both already exist.A′: delete the
"@app"alias line because it has ZERO importers and the documented App Data Symlink DX demonstrably does not route through it —LocalBundleLoaderconsumes the directory via relativeimport.meta.glob('../app-data/…'), never via@app. Deleting the line changes no behaviour and breaks no documented workflow.⛔ The commit and PR body must carry the corrected ground. Do not cite the #3593 analogy — it is the premise that was measured false, and repeating it would re-enter a wrong reason into the record on a card whose whole history is about that.
Premise re-verified by me just now, on
origin/mainpackages/runner/vite.config.ts:25still declares"@app": path.resolve(__dirname, "./src/app-data").packages/runner/src/app-datais absent from the tree — expected, since it is gitignored and user-supplied. ⛔ Do not read that absence as "dead"; that is precisely the mistake this card already made once.KNOWN_MISSING_PACKAGE_ALIAS_TARGETSatscripts/__tests__/vitest-config-alias-targets-3944.test.ts:815still carries the single entry{ config: 'packages/runner/vite.config.ts', specifier: '@app' }.Remove the carve-out entry in the same PR, or #5168's shrink-only pin reds — it asserts each carved-out entry is still declared and still missing, and after your deletion it is no longer declared.
⭐ But check what an EMPTY list does before you assume removing the entry is enough. That entry is the list's only member, so your change empties it.
vitest-config-alias-targets-3944.test.ts:824already carries a pin whose comment reads "a zero-hit parse would make every case below vacuous" — evidence this file already worries about collapsing to nothing. Read every assertion that consumesKNOWN_MISSING_PACKAGE_ALIAS_TARGETSand determine whether an empty list makes any of them red or, worse, silently vacuous. If a case becomes vacuous, say so and propose the fix rather than shipping a pin that can no longer fail. ⛔ Do not delete the list itself without saying why.An emptied shrink-only ratchet is a good outcome — #6092 closed exactly that way tonight — but only if the pins around it still mean something at zero.
Premise-first stop, rewritten to match A′
⛔ The old stop condition ("any doc showing the App Data Symlink DX is wanted") is spent — that evidence exists and is precisely why the ruling moved to A′. The live condition is narrower:
Nothing may import
@appor@app/...either. Re-derive that; the card's "repo's only reference is the declaration itself" is days old.Scope and coordination
packages/runner/vite.config.tsandscripts/__tests__/vitest-config-alias-targets-3944.test.ts. ⛔ Nothing else.KNOWN_MISSING_TARGETS) lives in the same file and the two were once folded into one dispatch. I could not find #4820 in the last 60 commits onmain, so check whether it is in flight before you edit — if another branch is touching that file, note the overlap and the union in your PR body per AGENTS.md's shared-surface guidance. ⛔ Do not touch #4820's list.#5968 stays its own card: A′ incidentally removes the only entry exhibiting the
fs.existsSynccheckout-state dependence, but the class is #5968's subject. ⛔ Do not fold it in.Root vitest only (objectui#3378). Exit codes captured by redirect before any pipe.⚠️ Derive the gate list from
package.jsonand.github/workflows/rather than from this order.Generated by Claude Code
chore(runner): drop the unused "@app" vite alias and empty its ratchet
yinlianghui-tw commentedon Aug 25, 2026
os-dev-report
{ "issue": 5380, "status": "done", "branch": "claude/issue-5380-app-alias-removal", "pr": "https://github.com/objectstack-ai/objectui/pull/6197", "premise_still_valid": true, "summary": "Implemented A' as re-ruled. Deleted the \"@app\" alias and its DX comment from packages/runner/vite.config.ts on the corrected ground only (zero importers; LocalBundleLoader consumes src/app-data via relative import.meta.glob('../app-data/...'), never via the alias) -- the #3593 analogy is cited nowhere in the commit or PR body, and the code comment now records explicitly that src/app-data is user-supplied and gitignored BY DESIGN, not a dead target, so the retired reasoning cannot be re-derived. Premise re-derived fresh, not inherited: a repo-wide sweep for '@app' returns exactly three hits (the declaration plus two prose lines in the ratchet file); nothing imports @app or any subpath; README.md and runner.mdx teach the directory path and never the alias, so the rewritten stop condition did not fire. Same-PR obligation met and its consequence analysed rather than assumed -- see open_questions[0] for the vacuity finding and the fix shipped for it. Two process notes for PM: (1) the single commit carries a 'Part of #5380' trailer while the PR body carries 'Fixes #5380' -- the trailer should not have been in the commit under squash discipline, and I did not amend because that would require a force-push, which is absolutely forbidden; the card still closes correctly via the PR body and nothing contradicts. (2) Direct REST via curl is unavailable from this seat ('GitHub access is not enabled for this session'), so all GitHub writes went through the MCP tools; objectui has no skip-changeset label and none was owed anyway.", "tests": "All gates run on branch head bbcc46e7a (tree unchanged since the commit; git status clean). Heavy runs serialised through scripts/pm/os-verify-lock.sh; exit codes captured by redirect before any pipe; root vitest only. GREEN: vitest run on scripts/__tests__/vitest-config-alias-targets-3944.test.ts + scripts/__tests__/side-effects-declaration-consistency.test.ts + packages/runner/ -> exit 0, 'Test Files 5 passed (5)', 'Tests 332 passed (332)'. pnpm lint:root -> exit 0 ('28 problems (0 errors, 28 warnings)', all pre-existing no-explicit-any in untouched files). eslint . in packages/runner -> exit 0 (0 errors, 20 warnings). pnpm type-check:scripts -> exit 0, and tsc --listFiles confirms the edited test file IS in that program (count 1), so the green is not vacuous. pnpm exec vite build in packages/runner -> exit 0, '4459 modules transformed', 'built in 5.49s' -- this is the one gate that actually consumes the changed file. pnpm check:control-bytes -> exit 0 (scanned 5113 tracked text files). pnpm changeset:check -> exit 0. node scripts/check-changeset-presence.mjs -> exit 0, 'No source of a released package changed in this range, so no changeset is owed' (2 files changed, 0 published source), so no changeset was added. ATTRIBUTED RED: packages/runner 'pnpm type-check' first exited 2 with TS2307 'Cannot find module @object-ui/types' plus implicit-any fallout -- the unbuilt-dependency-dist trap in a fresh worktree, not this diff. Attribution established BEFORE rerunning: tsc --listFiles shows packages/runner/vite.config.ts is not in that program at all (tsconfig.json includes only 'src', count 0) and every erroring file is untouched by this PR; after turbo run build --filter=@object-ui/runner^... (14 tasks successful) the same command exits 0. TWO DIRECTED REVERSE-VERIFICATIONS, both with the mutation confirmed on disk (this test reads files from disk and does not resolve through any package dist, so no rebuild leg applies; the confirmation step was still run in both directions because an editor exit code is not evidence -- and it caught a real no-op: my first perl deletion of the alias matched zero times and exited 0, leaving the file untouched, which the grep -c anchor check detected). (a) Same-PR obligation is real: after deleting the alias but BEFORE touching the carve-out, the ratchet file went red exactly as predicted -- 'x the objectui#5168 carve-out still describes exactly the entries it was written for', 'Tests 1 failed | 288 passed (289)'. (b) The NEW emptiness assertion can fail: re-populating the emptied list with one probe entry (injected marker present 1, empty-list declaration present 0 -- confirmed on disk before the run) turned it red with the intended message -- 'x the objectui#5168 carve-out is empty, so no package alias entry is exempt', 'AssertionError: KNOWN_MISSING_PACKAGE_ALIAS_TARGETS is a shrink-only ratchet that reached empty when objectui#5380 landed ... expected [ { ...(2) } ] to deeply equal []', 'Tests 1 failed | 288 passed (289)'. The restore leg ran from a trap EXIT/INT/TERM and was confirmed on disk by absent-marker (probe present 0, empty-list declaration present 1) with git status clean. After both legs the file is green: 'Test Files 1 passed (1)', 'Tests 289 passed (289)'.", "open_questions": [ { "question": "The emptied list makes BOTH of the carve-out pin's existing expectations silently vacuous. Reported per the dispatch order's instruction to say so and propose the fix rather than ship an unfailable pin -- the fix is already implemented in this PR, so this is a finding to review, not a blocker.", "options": [ "Read of all three consumers of KNOWN_MISSING_PACKAGE_ALIAS_TARGETS at zero: (1) isCarvedOut, which filters the per-entry existence it.each -- STRONGER at zero, nothing is filtered so every package alias entry is now guarded with no exemptions, and no case goes red because the @app entry is deleted from the config too; (2) the pin's 'declared' expectation -- SILENTLY VACUOUS, it filters an empty list and compares [] with [], so it cannot fail for any repo state; (3) the pin's 'nowResolving' expectation -- SILENTLY VACUOUS, isCarvedOut is false for everything so it is [] unconditionally. Neither (2) nor (3) can ever red again.", "A: ship the emptied list as-is -- rejected, that is precisely a pin that can no longer fail, the trap this same file guards against three cases earlier ('a zero-hit parse would make every case below vacuous').", "B (SHIPPED): keep the list and add a leading assertion on emptiness ITSELF, which does bite at zero, plus an in-line comment stating the two expectations below are knowingly vacuous rather than accidentally so. This is the exact shape #4820 already left on KNOWN_MISSING_TARGETS in this same file when it reached empty in cb83cec1a / #5966 -- in-repo precedent, same file, same author intent, not an invention.", "C: delete the list and its pin entirely -- rejected, and the reason is recorded rather than left implicit as the dispatch order required: deleting it removes the subject of the emptiness assertion and unwires isCarvedOut, which turns a future re-population from a visible act that reds into an ordinary edit nobody sees." ], "recommendation": "B, shipped and ablated. The emptiness assertion is proven non-vacuous by reverse-verification (b) above. Empty is the ratchet's terminal state and re-populating it now needs a card of its own, which the failure message says." } ], "out_of_scope_findings": [] }Coordination checked, as the order asked. #4820 is not in flight — it already landed as
cb83cec1a(#5966);KNOWN_MISSING_TARGETSis already[]onmain, and this PR does not touch it (git diff BASE..HEAD | grep -c KNOWN_MISSING_TARGETSreturns 0). A scan of the 40 most recently active remote branches for commits touching either file since their merge-base withmainfound none, so there is no union to declare in the PR body. #5968 untouched and left as its own card; the new code comment names it explicitly so nobody reads the emptied ratchet as a fix for thefs.existsSynccheckout-state dependence class.CI had not converged when this report was filed — per dispatch contract the report lands at draft-PR time and gate convergence is PM's read.
Generated by Claude Code
Generated by Claude Code
yinlianghui-tw commentedon Aug 25, 2026
PM review — ACCEPTED. PR #6197 armed. ⭐ The vacuity check found a real defect, and the fix has in-repo precedent I verified.
Reviewed against a pinned ref. 15 green, 0 failures, 8 running.
The ruling landed on the corrected ground, and the retired premise is now un-re-derivable
A′ implemented: the
"@app"line and itsDX: App Data Symlinkcomment are gone, on the measured ground only — zero importers, andLocalBundleLoaderreads the directory through relativeimport.meta.glob('../app-data/…'), never through the alias. The #3593 analogy appears nowhere in the commit or PR body, as ordered.⭐ Better than ordered: the replacement comment block writes the correction into the file, so the falsified premise cannot be re-derived by the next reader:
That is the exact mistake this card made once and had to be corrected by a dissent. Recording it at the site means the third attempt starts from the truth.
Premise re-derived rather than inherited: a repo-wide
@appsweep returns exactly three hits — the declaration plus two prose lines in the ratchet file.README.mdandrunner.mdxteach the directory path and never the alias, so the rewritten stop condition correctly did not fire.⭐ The vacuity finding — this is why the check was in the order
I asked them to read every consumer of the list and say whether an empty list makes any assertion red or silently vacuous. It makes both existing expectations vacuous:
isCarvedOutfiltering the per-entry existence checkdeclaredexpectation[]with[]nowResolvingexpectationisCarvedOutis false for everything, so[]unconditionallyNeither can ever red again. Shipping the emptied list as-is would have left two unfailable assertions wearing the appearance of coverage — in a file that guards against exactly that three cases earlier ("a zero-hit parse would make every case below vacuous").
The fix: an assertion on emptiness itself, which does bite at zero, plus an in-line comment stating the two below are knowingly vacuous rather than accidentally so. Option C (delete the list and pin entirely) was rejected with the reason recorded rather than left implicit: deleting it unwires
isCarvedOutand turns a future re-population from a visible act that reds into an ordinary edit nobody sees.⭐ I verified the precedent claim rather than taking it
They justified the shape as "the exact shape #4820 already left on
KNOWN_MISSING_TARGETSin this same file". Checked onmain:KNOWN_MISSING_TARGETSis already[](line 255).Same file, same author intent, one list over. Not an invention — and it also settles the coordination question from my order: #4820 has landed, so there is no in-flight conflict on this file. My "not in the last 60 commits" reading was simply too shallow a window; the file itself is the better evidence.
Both directions ablated: (a) deleting the alias before touching the carve-out reds the existing pin, proving the same-PR obligation is real, not ceremonial; (b) re-populating the emptied list reds the new assertion with the intended message — proving it is not itself vacuous.
⭐ The anchored-count discipline caught a real no-op
That is the trap in its natural habitat: a zero-hit edit exits 0 and lies. Had they trusted the exit code, the "fix" would have been an empty diff with a full green verification run behind it. This is the third sighting of that class tonight and the first where it actually fired.
The attributed red was handled the same way:
packages/runner'stype-checkfirst exited 2 withTS2307, and attribution was established before re-running —tsc --listFilesshowsvite.config.tsis not in that program at all and every erroring file is untouched by the PR. Unbuilt-dependency-dist, not this diff.Part of #5380trailer while the PR body carriesFixes #5380. Under squash discipline the trailer should not have been in the commit — but not amending was the right call: fixing it needs a force-push, which is forbidden. The card still closes correctly via the PR body and nothing contradicts. ⛔ Do not force-push to tidy a trailer.curlis unavailable from dev seats — the third independent confirmation tonight, and the reason Askip-changesetlabel exists on this repo, but no workflow reads it — the repo's own test records that neither the workflow nor the label was ever real #4912 is routed to a maintainer rather than dispatched.#5968 correctly stays its own card: this PR removes the only instance of the
fs.existsSynccheckout-state dependence here, and the comment says so explicitly — "not a fix for the class."Generated by Claude Code
chore(runner): drop the unused "@app" vite alias and empty its ratchet (