Skip to content

feat(types): a user-defined type claims its label as an alias#293

Open
Stvad wants to merge 1 commit into
masterfrom
claude/defined-type-alias-parity-0rwu1g
Open

feat(types): a user-defined type claims its label as an alias#293
Stvad wants to merge 1 commit into
masterfrom
claude/defined-type-alias-parity-0rwu1g

Conversation

@Stvad

@Stvad Stvad commented Jul 2, 2026

Copy link
Copy Markdown
Owner

What

A user-defined type (block-type block) doubles as a navigable page — it already carries PAGE_TYPE — but createTypeBlock only wrote content + block-type:label, never an alias. That left a parity gap with the rest of the reference system:

  • Typing [[Task]] for an existing "Task" type missed the alias index and minted a separate alias-seat page, so a workspace could end up with two "Task" pages.
  • The user-defined-types design (docs/user-defined-types/design.html) is explicit that the type is the [[label]] page — the block-id is the type-id, i.e. the same id [[Person]] references already resolve to.

Change

createTypeBlock now sets alias = [label] on the new type-definition block.

  • Reference resolution is lookup-first, so [[label]] now resolves to the type block instead of creating a duplicate seat.
  • Rename parity is self-maintaining: writeBlockTypeLabel keeps content in lockstep with the label, and the existing aliasSyncProcessor reconciles content → alias — so the alias tracks the label with no new wiring (covered by alias/test/syncProcessor.test.ts, "replaces old content entry").

Consequence (behavior change)

A type's name is now workspace-unique: the block_aliases_workspace_alias_unique trigger rejects creation — as a structured alias.collision — when a live block already claims the label. A duplicate-named type was ambiguous for [[label]] resolution anyway. The ExtractTypeDialog already surfaces the error message. (Adopting an existing page as a type — the deferred Roam-isa "promote to type" flow — remains out of scope.)

Tests

  • typeExtraction.test.ts: assert the created type carries alias = [label]; new tests that [[label]] (aliasLookup) resolves to the type block and that a label colliding with an existing page alias is rejected (alias.collision). Updated the "distinct ids on repeat calls" test to use distinct labels (its intent is fresh block ids, not shared names).
  • yarn run check green (compile, lint, 4199 tests).

Also refreshes the design.ts sketch to match and fixes its stale createChild import path.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BjEKpBXkcqBgMTLNNjepzy


Generated by Claude Code

A `block-type` block already doubles as a navigable page (it carries
PAGE_TYPE), but `createTypeBlock` only wrote `content` + `block-type:label`
— never an `alias`. So typing `[[Task]]` for an existing "Task" type
missed the alias index and minted a *separate* alias-seat page, leaving
two "Task" pages. Per the user-defined-types design (block-id = type-id:
the type is the same block `[[Person]]` references resolve to), the type
should BE its `[[label]]` page.

Set `alias = [label]` on the new type-definition block. Reference
resolution is lookup-first, so `[[label]]` now resolves to the type
block. Rename parity is self-maintaining: `writeBlockTypeLabel` keeps
`content` in lockstep with the label and the existing `aliasSyncProcessor`
reconciles content → alias, so the alias tracks the label with no new
wiring.

Consequence: a type's name is now workspace-unique — the
`block_aliases_workspace_alias_unique` trigger rejects creation (as a
structured `alias.collision`) when a live block already claims the label.
A duplicate-named type was ambiguous for `[[label]]` anyway.

Also refresh the design.ts sketch to match and fix its stale
`createChild` import path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BjEKpBXkcqBgMTLNNjepzy

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: adc9eda35b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// `block_aliases_workspace_alias_unique` trigger rejects this tx (as a
// structured `alias.collision`) if a live block already claims the
// label — a duplicate-named type is ambiguous for `[[label]]` anyway.
await tx.setProperty(newId, aliasesProp, [trimmedLabel])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Backfill aliases for existing type blocks

When a workspace already has user-defined types created before this change, only new createTypeBlock calls execute this write, so those existing block-type rows never claim their labels. aliasSyncProcessor skips rows with an empty alias list and writeBlockTypeLabel only changes label/content, so renaming an old type still won't seed an alias; after upgrade, [[Task]] can still miss the existing type and mint a duplicate alias-seat page unless a workspace backfill/migration seeds these aliases or handles collisions.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants