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
os info reports stats.objects: 1 and objects: [] in the SAME --json payload for a packages[]-only project — its four detail reads are still top-level-only #17790
os info renders its detail sections — Objects:, Agents:, Apps: — and its --jsonobjects array from config.objects / config.agents / config.appsdirectly, off the top level. On an ADR-0130 D4 / option-B project (every definition inside packages[], none flattened up) those reads see nothing, so the command contradicts itself within one run.
The --json face is the sharp half, because it is the one machines read:
stats.objects and objects[] describe the same project in the same payload and disagree. Nothing in the payload distinguishes "this project has no objects" from "this reader could not see them".
The text face shows the same gap as an absence rather than a contradiction: Data: 1 Objects 2 Fields prints, and the Objects: section that normally follows it is simply not there.
Minimal repro
objectstack.config.ts — one package, nothing at the top level except the envelope:
Measured through the real binary (node packages/cli/bin/run.js) on branch claude/issue-17527-metadata-stats-package-fold:
os info --json exit 0 stats.objects = 1 | stats.apps = 1 | stats.views = 1
objects[] length = 0
os info exit 0 Data: 1 Objects 2 Fields
UI: 1 Apps 1 Views
(no `Objects:` section)
Control — the identical object authored at the TOP LEVEL, same command, same binary: Objects: prints ob_order (2 fields, user) — Order, and --json carries the one entry. So the absence is produced by the reader, not by the stack.
Cause, located
packages/cli/src/commands/info.ts — four reads, all off the top level, none of them going through the package-owned-collection seam:
info.ts:50 objects: (config.objects || []).map((o: any) => ({ … })) # the --json array
info.ts:75 if (config.objects && config.objects.length > 0) { … } # the `Objects:` section
info.ts:91 if (config.agents && config.agents.length > 0) { … } # the `Agents:` section
info.ts:103 if (config.apps && config.apps.length > 0) { … } # the `Apps:` section
packages/cli/src/utils/stack-collections.ts already ships the primitive these need — resolveStackCollection(stack, key) answers the caller's original expression first and consults packages[] only when the top level does not carry the key, so adopting it cannot change what any stack the platform emits today reports.
Found while landing #17527 (PR #17775), and not created by it. #17527 folds packages[] into collectMetadataStats, which is the reader behind the summary — a different reader from these four, on a different path. Before that fix both halves of os info reported zero and agreed with each other; the fix corrects the summary and leaves the detail reads where they were, which is what turns a consistent wrong answer into a visible contradiction. The --json self-contradiction is therefore newly visible, not newly true: objects[] was empty on this stack before the fix as well.
Why it was filed rather than folded into that PR
Two reasons, and the second is the one that decided it.
os info --json's objects array is a machine face. Whether an option-B project's detail listing should be a flat union (what resolveStackCollection would give) or grouped per package is a presentation decision nobody has made, and making it silently inside a fix for the summary would settle a published output shape by accident.
Deduplication
One targeted repo-scoped REST read — GET /repos/objectstack-ai/objectstack/issues?state=open&labels=domain:cli&per_page=100, 44 open non-PR cards, page 2 empty — then grepped locally over title + body. collectMetadataStats as the positive control returns exactly 1 hit (#17527), so the greps are live and the zeroes are readings.
Symptom
os inforenders its detail sections —Objects:,Agents:,Apps:— and its--jsonobjectsarray fromconfig.objects/config.agents/config.appsdirectly, off the top level. On an ADR-0130 D4 / option-B project (every definition insidepackages[], none flattened up) those reads see nothing, so the command contradicts itself within one run.The
--jsonface is the sharp half, because it is the one machines read:{ "stats": { "objects": 1, "apps": 1, "views": 1 }, "objects": [] }stats.objectsandobjects[]describe the same project in the same payload and disagree. Nothing in the payload distinguishes "this project has no objects" from "this reader could not see them".The text face shows the same gap as an absence rather than a contradiction:
Data: 1 Objects 2 Fieldsprints, and theObjects:section that normally follows it is simply not there.Minimal repro
objectstack.config.ts— one package, nothing at the top level except the envelope:Measured through the real binary (
node packages/cli/bin/run.js) on branchclaude/issue-17527-metadata-stats-package-fold:Control — the identical object authored at the TOP LEVEL, same command, same binary:
Objects:printsob_order (2 fields, user) — Order, and--jsoncarries the one entry. So the absence is produced by the reader, not by the stack.Cause, located
packages/cli/src/commands/info.ts— four reads, all off the top level, none of them going through the package-owned-collection seam:packages/cli/src/utils/stack-collections.tsalready ships the primitive these need —resolveStackCollection(stack, key)answers the caller's original expression first and consultspackages[]only when the top level does not carry the key, so adopting it cannot change what any stack the platform emits today reports.Relationship to #17527
Found while landing #17527 (PR #17775), and not created by it. #17527 folds
packages[]intocollectMetadataStats, which is the reader behind the summary — a different reader from these four, on a different path. Before that fix both halves ofos inforeported zero and agreed with each other; the fix corrects the summary and leaves the detail reads where they were, which is what turns a consistent wrong answer into a visible contradiction. The--jsonself-contradiction is therefore newly visible, not newly true:objects[]was empty on this stack before the fix as well.Why it was filed rather than folded into that PR
Two reasons, and the second is the one that decided it.
Data: 0 Objectsfor a project whose metadata lives only in packages[] — collectMetadataStats reads the top level alone, and under --strict the falseNo objects definedwarning gates #17527 carries is explicit that the summary's presentation is the question it answers, and it answers only that one.os info --json'sobjectsarray is a machine face. Whether an option-B project's detail listing should be a flat union (whatresolveStackCollectionwould give) or grouped per package is a presentation decision nobody has made, and making it silently inside a fix for the summary would settle a published output shape by accident.Deduplication
One targeted repo-scoped REST read —
GET /repos/objectstack-ai/objectstack/issues?state=open&labels=domain:cli&per_page=100, 44 open non-PR cards, page 2 empty — then grepped locally over title + body.collectMetadataStatsas the positive control returns exactly 1 hit (#17527), so the greps are live and the zeroes are readings.os infoin title or body: 1 hit, os validate, os build and os info all reportData: 0 Objectsfor a project whose metadata lives only in packages[] — collectMetadataStats reads the top level alone, and under --strict the falseNo objects definedwarning gates #17527 itself.config.objects: 2 hits — os validate, os build and os info all reportData: 0 Objectsfor a project whose metadata lives only in packages[] — collectMetadataStats reads the top level alone, and under --strict the falseNo objects definedwarning gates #17527 and os lint's hand-written checks andscoreMetadatastill read the top level alone: a packages[]-only project gets✓ All checks passedand a rubric computed over nothing (the half #17069 did not scope) #17528. os lint's hand-written checks andscoreMetadatastill read the top level alone: a packages[]-only project gets✓ All checks passedand a rubric computed over nothing (the half #17069 did not scope) #17528 rejected: it isos lint's hand-written checks plusscoreMetadata, a different pair of readers in a different command.scoreMetadata: 1 hit, os lint's hand-written checks andscoreMetadatastill read the top level alone: a packages[]-only project gets✓ All checks passedand a rubric computed over nothing (the half #17069 did not scope) #17528.No open card names these four reads or the
--jsonobjectsarray. Scope of the read is stated so it can be re-run: open cards carryingdomain:clionly.Generated by Claude Code