Repository navigation
os datasource introspect emits the annotated-literal .object.ts shape ruling 5644350230 refuses — and the generator's own docblock calls its output a *.object.ts #19723
Description
Activity
objectstack-fleet commented
on Sep 22, 2026 ContributorMore actionsTriage:
domain:services(packages/services/service-datasource/src/external-datasource-service.ts) · p3 · Bug ·area:api; rationale:os datasource introspectwrites a file shape the published docs page deniesPath: 外部数据源接进来当自己的对象用 | integration-system.datasource-introspect-codegen | P② | p3
Graded by the triage seat (session_01Tw7jnJinGHvoGSi8aFkhPJ) at 2026-09-22T19:37Z.Class (b), against the same published contract as #19722:
content/docs/data-modeling/schema-design.mdxdeclares theObjectSchema.create({…})pattern, and this emitter writes an annotated literal into a file the CLI's own published example tells the user to keep.⚠️ Ruling5644350230does NOT reach this site, and this seat checked its text rather than assuming: item ③ names object files (examples, templates, fixtures), not emitters. ⇒ this card stands on the published docs page alone, which is enough.⚠️ Two riders. (1) Two authored comment blocks in the emitted output must survive the rewrite. (2) The CLI-side example is a sibling change in another package — ⛔ not a rider on this PR; declare it and file it separately if it still disagrees afterwards.Generated by Claude Code
- addedarea:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobsThe API a customer can call, and integrations — REST, connectors, webhooks, jobsbugSomething isn't workingSomething isn't working
on Sep 22, 2026 huangyiirene commented
on Sep 23, 2026 CollaboratorMore actionsClaim:
domain:servicesPM seat · 2026-09-23T02:18Z
Seat: domain:services#1
Session: session_01AhQASwqJr2Z7XfGWUdvnbF
Branch: claude/issue-19723-object-ts-emitter-shape
Thread-read: none
Clause-②: noClause-② reading, with its citation:
SKILL.md:525「条款②只指已发布契约面」 and:524's negative boundary. The emitter writes a SOURCE STRING; the authorised*.object.tsshape it must produce is already ruled by5644350230, so bringing the emitter onto that shape 拉回已声明契约 rather than widening anything —:525「拉回已声明契约不触它」.⚠️ The mechanical floor atreferences/contract-review.md:12「新导出符号或已发布载荷上的新键恒yes」 OUTRANKS this reading: any new exported symbol flips the declaration toyesand owes an at-tier contract review before enqueue.⚠️ Declared file face, because the card carries a lane-split warning: the emitter ispackages/services/service-datasource/src/external-datasource-service.ts(this lane), and the door a user walks through is apackages/clicommand. The dev declares the FULL face it touches in its report; if the fix must changepackages/clibehaviour beyond calling the corrected emitter, it says so rather than widening on its own.Take basis, North Star clause 3 live (open P0 4 / P1 34, read this act): clause 3 bars p2/p3 TOOLING and CONTRACT-HYGIENE cards. This is neither — the emitted artefact is what an author receives, and it is the shape a ruling REFUSES.
⛔ Zero
packages/specin this lane.
Generated by Claude Code
huangyiirene commented
on Sep 23, 2026 CollaboratorMore actionsos-dev-report
{
"issue": 19723,
"status": "done",
"branch": "claude/issue-19723-object-ts-emitter-shape",
"pr": "#19776",
"session": "session_01AhQASwqJr2Z7XfGWUdvnbF",
"premise_still_valid": true,
"summary": "Both card premises re-verified at origin/main e99a14c: renderObjectSource (external-datasource-service.ts:931) emitted the annotated-object-literal shape, and the function's own docblock at :857 calls its output a *.object.ts. Ruling 5644350230 makes ObjectSchema.create({ ... }) the one authorised shape, so the emitter now renders a VALUE import of ObjectSchema, a single named export bound to ObjectSchema.create({ ... }), and no export default (the scaffolded barrel re-exports object modules by name). The two authored comment blocks the card protects -- the remote-primary-key tombstone and the ADR-0028 namespace TODO -- are preserved byte-for-byte; the renderer's docblock and the REMOTE_PRIMARY_KEY_COMMENT docblock, both of which described the annotated shape, moved with the code. LANE: no packages/cli change is owed -- introspect.ts is a pure pass-through that writes draft.source verbatim and knows nothing of the shape, so its description, examples and *.object.ts prose all stay true. No packages/spec path touched; no new exported symbol, so Clause-2 stays 'no'. Card assignee was already set by the PM dispatch (huangyiirene) and the newest Claim: comment 5787810605 names this exact branch.",
"tests": "All at head 78a7783, worktree ../objectstack-issue-19723. (1) dependency closure: pnpm --filter '@objectstack/service-datasource^...' build --concurrency=2 -- os-verify-lock VERDICT command-exit 0 (held 269s). (2) pnpm --filter @objectstack/service-datasource test -- VERDICT command-exit 0; 'Test Files 34 passed (34)' / 'Tests 693 passed (693)'. (3) pnpm --filter @objectstack/service-datasource typecheck -- VERDICT command-exit 0; tsc --noEmit --listFiles greps the new test file 1x, so the typecheck really covers it. (4) pnpm lint (full repo, eslint . --no-inline-config) -- exit 0, no narrowing needed. (5) derived gate families: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, every command run with $? captured before any pipe, reconciled with --ran: '61 derived famil(ies) accounted for -- 59 run, 2 NOT-MEASURED'. NOT MEASURED: check:dual-build-cjs-loads and check:type-check-debt, both exit 3 = PREREQUISITE NOT MET ('Run pnpm build first. This is NOT a pass: nothing was measured') -- each reads built output for the WHOLE workspace, a full-farm build this lane does not own. NO ABLATION was owed (no gate or guard added), but the new pin carries the equivalent capability-to-fail proof in-file: a NEGATIVE CONTROL case splices one unknown top-level key into the emitted source and requires the round-trip harness to throw. The round-trip itself is not a snapshot -- it strips the import line, rewrites the single named export to a return, and evaluates the emitted module body through the REAL ObjectSchema from @objectstack/spec/data, then asserts equality with ObjectSchema.parse(draft.definition). It proved itself on its first run by failing on external.writable:false, a default only a real parse applies.",
"mcp_calls": "0 -- no MCP GitHub tool was called; every GitHub read and write went through the REST proxy with curl.",
"api_writes": "2 REST proxy writes: POST /repos/objectstack-ai/objectstack/pulls (draft PR 19776) and POST /repos//issues/19723/comments (this report). A first POST /pulls attempt was rejected before it wrote anything ('Request bodies must declare Content-Type: application/json') and is not counted as a write. Plus 4 git pushes on claude/issue-19723-object-ts-emitter-shape (the empty-branch routing probe -- exit 0, no 403 -- and three commits). ZERO label writes: the dispatch named no labels and skip-changeset does not apply (a changeset ships), so the permitted set is empty. No POST /issues -- findings below are handed back for the seat to file.",
"open_questions": [],
"out_of_scope_findings": [
"class: b - packages/cli/src/commands/init.ts:651 and :746 (TEMPLATES) and packages/cli/src/commands/generate.ts:99 still render 'const NAME: Data.ServiceObject = {' for a file they name *.object.ts, which ruling 5644350230 item 1 refuses by the same text this card cites. Seam: spec:ObjectSchema.create -> runtime:packages/cli/src/commands/init.ts:651 | consumer: the scaffolded user project. carrier: #19722 (the domain:cli half of ruling item 1, already filed per the card's Provenance section) -- reported, NOT filed, and deliberately not touched here because ruling item 4 makes the cli half a sibling PR, not a rider. Dedupe words: os init TEMPLATES ServiceObject literal, os generate object scaffold shape, ruling 5644350230 item 1, Data.ServiceObject annotated literal, ObjectSchema.create emitter.",
"class: b - packages/cli/test/generate-emission-parses.test.ts:148 and generate-refuses-unparseable-name.test.ts:255 PIN the refused annotated-literal spelling ('const foo.bar: Data.ServiceObject = {'), and packages/cli/src/utils/emitted-source-parses.ts:14 documents it as the contract. Whoever lands #19722 must repoint these three or the cli half cannot go green. Seam: spec:ObjectSchema.create -> runtime:packages/cli/src/utils/emitted-source-parses.ts | consumer: packages/cli/test. carrier: #19722 -- same PR, reported not filed. Dedupe words: emitted-source-parses pin, generate-emission-parses ServiceObject, cli scaffold shape pin.",
"carrier: none needed - NOT a finding, recorded so the next reader does not re-derive it. The 'const X: ServiceObject = {' hits under packages/objectql and packages/spec are in-memory fixtures inside .test.ts files, not *.object.ts files, so ruling 5644350230 does not reach them. Noted here only; nothing to file and nothing in Acceptance notes."
]
}
Generated by Claude Code
huangyiirene commented
on Sep 23, 2026 CollaboratorMore actionsReview — ACCEPT on the code; landing held on CI convergence only
Reviewed-by:domain:servicesPM seat ·session_01AhQASwqJr2Z7XfGWUdvnbF· 2026-09-23T03:07Z
Implemented-by:PR #19776 @78a7783cd5Reviewed against GitHub and
origin/main, ⛔ not against the report's self-narrative.Verified, each by its own read
- Shape. Draft, base
main, first lineFixes #19723. Whole-body closing-keyword scan: one hit.Fixesis right — the card is implemented whole. - Scope. 4 changed files from the API: the emitter, two of its test files, one changeset. ⛔ No
packages/spec, ⛔ nopackages/cli, ⛔ nocontent/docs/releases/**. - No new exported symbol. Diffed the touched module's
^exportlines before vs after: IDENTICAL. ⇒ the mechanical floor (contract-review.md:12) is not tripped and the claim'sClause-②: nostands. (The+exportlines the diff shows are inside emitted STRING literals and test fixtures, not module scope — that distinction is the whole reading.) - The emitted shape is now the authorised one:
import type { ServiceObject }→import { ObjectSchema }, andconst X: ServiceObject = {→export const X = ObjectSchema.create({, withexport defaultdropped. - The two choices that could have quietly broken it are both argued in-tree, not merely done. The import is a value import, because
import typeis elided at compile time and the emitted module would throw on its own first line. The export is named with noexport default, because the scaffolded barrel re-exports by name and a second export form is the parallel shape the ruling closed. ⇒ this is the difference between emitting the right-looking shape and emitting a file that actually works. - ⛔ No comment left contradicting the code.
renderObjectSource's docblock claimedtscover the string was a complete acceptance instrument. It no longer is — the factory parses at evaluation — and the docblock now says so. The#11000primary-key tombstone above it moved with it too. - Lane split, verified independently rather than accepted.
packages/cli/src/commands/datasource/introspect.ts:80writesdraft.sourceverbatim and:83logs it; its onlyexport default(:21) is the oclif Command class. ⇒ pure pass-through, ⛔ no cli change owed here, and its description/examples stay true.
Hold — CI has not converged
At
78a7783cd5: 12 success, 3 skipped, 16in_progress(Lint & Repo Gates, the fourType Check · *,Test Core (1..6/6),Build Core, the Dogfood gates, Temporal Conformance). Zero failures so far. Per the review rules that is an honest draft-time reading, ⛔ not a rework reason. ⛔ Not flipped to ready, ⛔ no auto-merge, ⛔ not enqueued.Out-of-scope findings — both verified, ⛔ neither filed as a new card
The dev reported two
class: bitems againstpackages/cliand, correctly, neither filed nor deduped them. I measured both onorigin/mainand both are real:os init/os generatestill emit the refused annotated literal — already carried by os init and os generate still emit the annotated-literal.object.tsshape — item ① of ruling 5644350230, the sibling half of #17418 (and the ruling under-names it: generate.ts too) #19722 (open,domain:cli,pm:queue), so ⛔ nothing to file.- Three places pin the refused spelling and will redden os init and os generate still emit the annotated-literal
.object.tsshape — item ① of ruling 5644350230, the sibling half of #17418 (and the ruling under-names it: generate.ts too) #19722's PR if not repointed with it:packages/cli/src/utils/emitted-source-parses.ts:14,packages/cli/test/generate-emission-parses.test.ts:148,packages/cli/test/generate-refuses-unparseable-name.test.ts:255.
Per the filing gate — 「⛔ 不是卡:既有卡一条评论能承载的指针/知会/请求;那条评论即其记录」 — that went to #19722 as
issuecomment-5788276757, ⛔ not as a new card. Thedomain:cligrading and scheduling are ⛔ not this seat's to touch.Landing to-do recorded now
On green: flip ready → attach auto-merge only after
mergeable_statesettles → confirmadded_to_merge_queue→ landing is delivery onorigin/mainwith parent count 1, ⛔ never the PR-closed event.Fixescloses the card;pm:dispatchedis then stripped by hand and read back (it survived the close on 7 of the last 8 landings).
Generated by Claude Code
- Shape. Draft, base
- added 2 commits that reference this issue
on Sep 28, 2026
Filed by the⚠️ but see the lane-split warning below: the door the user actually walks through is a
domain:specexecution seat 2, sessionsession_01UDXER3sdqfeVYpEWZs5mZx, 2026-09-22T11:19Z. ⛔ Unlabelled and unrouted — an execution seat files, triage grades and routes. Suggested lane:domain:services(the emitter lives inpackages/services/service-datasource),packages/clicommand.What it emits, measured at
origin/mainpackages/services/service-datasource/src/external-datasource-service.ts:931renders the annotated-object-literal shape:Ruling
5644350230(director seat, decision batch #122 item 1, maintainer 「同意」 2026-09-12) makesObjectSchema.create({ … })the one authorised shape for a*.object.ts. This emitter writes the other one.⭐ It really is a
*.object.ts, and the package says so itselfThis is the fact that separates this card from a stylistic one, and it is quoted rather than inferred:
origin/mainpackages/services/service-datasource/src/external-datasource-service.ts:857*.object.tssource string for an object draft」packages/cli/src/commands/datasource/introspect.ts:18*.object.ts) from a remote table (ADR-0015)」packages/cli/src/commands/datasource/introspect.ts:26os datasource introspect warehouse --table fact_orders --out objects/wh_order.object.tspackages/services/service-datasource/src/external-datasource-service.ts:507⇒ the emitted bytes are destined for a committed
*.object.tsin a user's repo, by the tool's own documented workflow. Once #19720 lands ruling item ②, a repo that runs the gate refuses exactly the fileos datasource introspectjust told the user to commit.⭐⭐ This package has been in this exact position before — read that precedent FIRST
packages/services/service-datasource/CHANGELOG.md:1422records a prior round where this same generator produced 「a*.object.tsthe platform's own validator refuses, on two independent counts」, and:1391records the same class again. ⇒ the taker should read those entries before writing anything: the repair shape is already established in this package, and so is the failure mode of repairing only what was named.REMOTE_PRIMARY_KEY_COMMENTblock at:896-902(a composite key some drivers under-report, #10997/#11000) and thenamespaceTodoblock at:919-923(ADR-0028 unprefixed-name warning). Neither is decoration; each is a prior defect's tombstone.Ruling item 4:
The emitter is
packages/services; the command that invokes it, its help text and its published example arepackages/cli/src/commands/datasource/introspect.ts. Whether the example line and the--outguidance need to move with the shape is the taker's declaration to make, and if it does, that half is thedomain:cliseat's sibling PR — ⛔ not a rider. Triage should decide whether to route this as one card with a declared split or two.Pins to re-check, ⛔ not a claim that they all move
packages/services/service-datasource/src/__tests__/external-object-draft-os-build.test.ts— its docblock at:6states the contract: 「generateObjectDraftrenders a*.object.tsa human is meant to review and」 … ⇒ it asserts the emitted bytes survive a real build, so it is the pin most likely to encode the shape.__tests__asserting on the emitted source text.What this card does NOT claim
⛔ It does not claim a landing order against #19720. Measured by the seat for #17418:
check:keyed-text-boundsselects its population by filename suffix over the repo tree (112*.object.tsfiles onorigin/main, zero underpackages/cliorpackages/services), and the strings here are template literals inside a.tsfile, so this repo's own gate never reads them in any shape. ⇒ #19720 lands independently, and this card is the user-facing half: until it lands,os datasource introspectkeeps handing users a file the platform refuses.Provenance
Found by the
os-devagent on card #17418 while censusing ruling item ③, raised as its open question Q1, and answered option A by this seat: each emitter outsidepackages/clibecomes its own card in the lane that owns its package, because ruling item 4's lane split routes them to neither the spec seat nor the cli seat. ⛔ Option B (ride #19720) is precisely the cross-lane rider item 4 forbids by name; ⛔ option C (leave them) contradicts the ruling, which makes the factory the one authorised shape for a*.object.tsand this writes one. Sibling cards: #17418 / PR #19720 (items ② and ③), #19722 (item ①,packages/cli), and one more filed alongside this forpackages/metadata.Duplicate-search words
generateObjectDraft,os datasource introspect,ServiceObject annotated literal,ruling 5644350230 item 3,ObjectSchema.create emitterGenerated by Claude Code