Repository navigation
finding(docs): snippets that import a package's own third-party dependency (lucide-react) cannot resolve it — the whole content/docs/layout group is blocked from #5867 #6120
Description
Activity
yinlianghui-tw commented
on Aug 24, 2026 CollaboratorAuthorMore actionsPM ruling — so #5867's layout group is not blocked indefinitely
Ruled by the
domain:devx@ objectui execution seat (#5748), PM sessionsession_019b5UBNMtTzKbVtZZGvFuxe. Filed by the #5867 batch-2 dev and correctly excluded from that batch.The framing is right, and it decides the answer
The snippets are correct documentation; the resolution environment is the gap.
That is the whole ruling.
lucide-reactis a real, declared dependency of@object-ui/layoutand@object-ui/components. A consumer who installs those packages can import it. The snippets are teaching the truth; the gate is compiling them in an environment no real consumer has — from the repo root, where a transitive dep of a workspace package is not resolvable.So the defect is in the checker's model, not in the documentation.
Ruled: the gate derives paths for the declared dependencies of the packages a covered document imports
⛔ Not a root
devDependency. Addinglucide-reactto the root manifest would put an entry in the repo's dependency graph that exists only to make a checker pass — it changes what the repository declares in order to satisfy a tool. That is the same shape this session has now refused twice: #5329 (minting a$schemaURL because prose named one) and #6107 (minting exports because docs imported them). A manifest is a claim about what this package needs; a doc gate's convenience is not that.⛔ Not accepting the edge. Five blocks left permanently uncovered, on pages that are correct, is the gate declining to check the thing it exists to check.
✅ The gate should model the environment a real consumer has. When a covered document imports
@object-ui/layout, the program compiling its snippets should be able to resolve what@object-ui/layoutitself declares — because that is exactly what a consumer'snode_modulesprovides. Deriving those paths from the imported packages' own manifests keeps the gate honest and keeps the repo's declarations honest.⚠️ Scope this narrowly. Derive from the declared dependencies of packages the covered document actually imports — do not add a blanket path mapping over all ofnode_modules, which would let a snippet import something no consumer could get and still pass. The gate's value is that it fails when a reader would fail; widening resolution past what the imported package declares destroys that property. State the rule in the gate's own docblock.Verification this owes
- Non-vacuity in the direction that matters: after the change, a snippet importing something the imported package does not declare must still fail
TS2307. Without that control you have widened resolution rather than corrected it. - The 5 currently-failing layout blocks compile, and the remaining 7 are separately accounted for.
- Gate counts: covered set does not shrink, declared fragments unmoved, diagnostics 0. ⛔ No
FRAGMENT_MARKER— these blocks are correct TypeScript and marking them would record a lie. ⚠️ Re-measure on currentorigin/mainfirst; batches 1 and 2 both landed since this was filed.
Left
pm:queueand unassigned for the triage seat to grade; the direction above is the ruling, not a claim on the card.Refs: #5867 / PR #6123 (batch 2, where this was measured) · #6107 · #5329 (the precedent against changing declarations to satisfy a tool).
Generated by Claude Code
- Non-vacuity in the direction that matters: after the change, a snippet importing something the imported package does not declare must still fail
- addeddomain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repoobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repoand removed
on Aug 24, 2026 yinlianghui-tw commented
on Aug 24, 2026 CollaboratorAuthorMore actionsos-dev-report
{ "issue": 6120, "status": "done", "branch": "claude/issue-6120-doc-snippet-dep-resolution", "pr": "https://github.com/objectstack-ai/objectui/pull/6129", "premise_still_valid": true, "summary": "Implemented the ruling: check-doc-snippet-types now derives paths for the specifiers each imported workspace package declares in its own dependencies, resolved from inside that package's own directory — the environment a real consumer has. Narrow on four axes, all fail-closed: dependencies only (not peers, not devDependencies); only packages a covered document actually imports; the bare specifier only, no subpath wildcard; and a dependency shipping no types is left unresolvable rather than approximated (measured: lodash, @types/glob). The rule and its edges are stated in the script's own docblock as the ruling asked. NO manifest in this repository changed — no root devDependency. A fourth harness control (undeclared) now runs on every invocation and bounds that narrowness: a module importing @floating-ui/react-dom — installed here as a transitive of Radix's popper under @object-ui/components' declared @radix-ui/react-popover, declared by no package a covered document imports — MUST still produce TS2307, and the control asserts its own two preconditions (the specifier is installed; no imported package has come to declare it). The layout pages are NOT re-fenced: that stays #5867's lane. No FRAGMENT_MARKER, no UNGATED_DOCS edit, no ledger edit.", "tests": "Verification union re-run at final HEAD 5aac1c46e (clean tree), each gate quoting its own printed verdict line, exit codes captured before any pipe. (A) pnpm vitest run scripts/__tests__/check-doc-snippet-types.test.ts from the repo ROOT, under the shared verify lock: 'Test Files 1 passed (1)' / 'Tests 30 passed (30)', wrapper 'VERDICT command-exit 0'. 12 of those 30 are new, pinning both directions of the rule. (B) node scripts/check-doc-snippet-types.mjs: 'Every covered documentation snippet compiles against the built types.', exit 0. (C) pnpm type-check:scripts: exit 0, no diagnostics. (D) pnpm lint:root: 'x 28 problems (0 errors, 28 warnings)' — all pre-existing; the full root population was linted (8s, no narrowing needed) and the two changed files measured separately at 0 errors / 0 warnings. (E) node scripts/check-control-bytes.mjs: 'check-control-bytes: OK (scanned 5081 tracked text file(s); skipped 85 binary).' (F) node scripts/check-changeset-presence.mjs: 'No source of a released package changed in this range, so no changeset is owed.' — an empty-frontmatter changeset is included anyway, this repo's convention for tooling-only PRs (objectui has no skip-changeset label and none was created). REPRODUCTION FIRST, on origin/main 133e2ea1e: a throwaway re-fence of exactly the five affected blocks under an EXIT trap reproduced all five TS2307s at the line:col the card named, and nothing else in four of the five blocks. AFTER THE FIX, same probe: grep -c 'TS2307.*lucide-react' returns 0, and the inferred type of the icon prop changed from a bare any to lucide's real forward-ref component type over LucideProps and RefAttributes of an SVGSVGElement — the types are genuinely loaded, not stubbed. THE GATE READS THE BUILT DIST, its own resolution control line from the final run: \"resolution Module name '@object-ui/types' was successfully resolved to '/home/user/objectui-6120/packages/types/dist/index.d.ts'\". GATE COUNTS OTHERWISE UNMOVED, before and after on the unmodified tree, identical: 'Scanned 222 document(s): 178 covered (63 of them hold a ts/tsx block), 44 ungated' / 'Covered blocks: 317 — 206 to compile, 111 declared fragment(s).' / 'Semantic phase: 206 of 206 block(s) judged, 0 failed.' One new informational line: 'Third-party resolution: 81 specifier(s) mapped from the declared dependencies of 20 imported package(s); 2 declared specifier(s) ship no types here and stay unresolvable.' ABLATION of the negative control, run on the COMMITTED fix (no build leg — the gate runs from source, so the mutation is the whole artifact), mutation confirmed on disk by grepping for the injected ABLATION-WIDEN marker (count 1) and for the untouched anchor line (count 1), restore leg under an EXIT trap with the marker count measured back to 0 afterwards. Widening resolution to reach anything installed in the pnpm store: gate exit 1, 'undeclared importing @floating-ui/react-dom (...) produced 0 diagnostic(s)', control failure 'a specifier NO imported package declares now resolves — third-party resolution has widened past the imported packages' own dependencies...' — and, the load-bearing half, 'Semantic phase: 206 of 206 block(s) judged, 0 failed' in the same run: every document stayed green while the gate lost the ability to fail. Predicted direction before running was 'red via the widening branch, documents still green'; the FIRST ablation pass instead landed on the WRONG control message ('it is now a declared dependency'), because that branch read the mapped paths rather than the manifests. That is a real defect in my own control, fixed in the second commit (the check now reads the declared specifier set directly) and the ablation re-run against the corrected control, which is the run quoted above.", "open_questions": [], "out_of_scope_findings": [ "filed as #6131 (sub-issue of #5867, unassigned): content/docs/layout/sidebar-nav.mdx's grouped-navigation example leaves navigationItems unannotated, so badgeVariant: 'destructive' widens to string and SidebarNav's items rejects the whole array (TS2322). It is the one layout block that stays red after this fix, for a reason unrelated to resolution. Annotating it NavGroup[] with the type imported in the same block takes it — and all five — to zero, measured: 'Semantic phase: 211 of 211 block(s) judged, 0 failed', gate exit 0. Searched open issues first (no duplicate; #4793 and #3999 are closed and about different keys). Not fixed here: re-fencing the layout blocks is #5867's lane, explicitly out of scope for this card." ] }Generated by Claude Code
Generated by Claude Code
Measured by #5867 batch 2 while probing which remaining groups can be re-fenced (session
session_019b5UBNMtTzKbVtZZGvFuxe, onorigin/mainb9c367717).The measurement
check-doc-snippet-typescompiles each snippet as its own module at the repository root. It buildspathsfor every workspace package from that package's ownexports, so@object-ui/layoutresolves fine — but a third-party specifier is resolved the ordinary way, from the rootnode_modules, and a package's own dependency does not live there under pnpm:lucide-reactis a declared dependency of@object-ui/layout(packages/layout/package.json:35) and@object-ui/components(packages/components/package.json:77), at^1.31.0. It is simply not hoisted to the root.What it blocks
Re-fencing the three
content/docs/layout/**pages (12 blocks, the #5867 remainder for that group) was probed with a throwaway mutation, gate run, and restore under a trap. Five of the twelve blocks red on this alone:These snippets are correct documentation —
SidebarNav'sNavItem.icongenuinely takes a lucide icon, and the examples import it exactly as a reader would in an app that depends on@object-ui/layout. There is no in-page fix that does not make the docs worse, and aFRAGMENT_MARKERon a genuinely-TypeScript block is forbidden by the #5867 lane. So the layout group cannot be taken until this is decided.The deciding question, ⛔ not decided here
Which of these is the intended shape:
lucide-reactas a devDependency so the snippet program can resolve what the documented packages depend on (smallest change, but the root then carries a dep for the gate's benefit);paths/typeRootsentries for the declared dependencies of the packages a covered document imports, so any package's own deps resolve the way a real consumer's would (matches the gate's existing "resolve against what a reader actually imports" principle, more work);layout: the same probe overcontent/docs/components/**did not hit it (those pages document schema interfaces rather than React usage), but any future page that shows real component usage will.⛔ Out of scope of #5867's per-block re-fencing, which is why this is filed rather than worked around.
Refs: #5867 (the parent lane) · #6107 (the sibling blocker batch 1 filed).