oschema skill prescribes manually declaring sid/created_at/updated_at — generator already auto-injects them #275

Closed
opened 2026-05-20 07:35:58 +00:00 by timur · 2 comments
Owner

Problem

skills/oschema/oschema.md (the authoritative source of the oschema skill loaded by Claude) instructs authors that every root object MUST declare a sid: str field, and every example also declares created_at: otime / updated_at: otime by hand. Excerpts:

Root objects ... Mark a type as a root object using the `[rootobject]` annotation in the comment above the type definition. Root objects **MUST** have a `sid: str` field.
# Task [rootobject]
Task = {
    sid: str             # REQUIRED for root objects
    title: str
    status: Status
    created_at: otime
}

Why this is wrong

The Rust struct generator in hero_rpc (crates/generator/src/rust/rust_struct.rs) auto-injects sid: SmartId, created_at: u64, and updated_at: u64 on every root object, and silently skips any schema-declared duplicates:

// For root objects, add server-managed fields first (sid, created_at, updated_at)
// These fields are automatically managed by the server and not part of the schema
if obj.is_root_object {
    output.push_str("    pub sid: SmartId,\n");
    output.push_str("    pub created_at: u64,\n");
    output.push_str("    pub updated_at: u64,\n");
}

for field in &obj.fields {
    // Skip server-managed fields if they were explicitly defined in schema
    if obj.is_root_object
        && (field.name == "sid" || field.name == "created_at" || field.name == "updated_at")
    {
        continue;
    }

The parser detects root-object-ness from either a sid field or a [rootobject] comment marker (crates/oschema/src/parser.rs:655-661), so [rootobject] alone is sufficient — sid: str was never required.

The net effect of the current skill:

  1. Authors hand-write boilerplate that's already automatic.
  2. The sid: str line in the schema is misleading — the generated field is pub sid: SmartId (not String).
  3. The created_at: otime line in the schema is misleading — the generated field is pub created_at: u64 (not OTime).
  4. New OSchema authors copy the bad pattern from examples — that's how it ended up in hero_service/schemas/catalog/catalog.oschema and propagated.

What to change in the skill

  1. Rule: root objects are declared with [rootobject] in the leading comment. Do not declare sid, created_at, or updated_at — they are auto-injected by the generator and managed by the server.
  2. Update all examples in skills/oschema/oschema.md (Project / Task / Product / etc.) to drop the explicit sid / created_at / updated_at lines.
  3. Add a note that the actual generated types are sid: SmartId, created_at: u64, updated_at: u64 — so anything written in the schema for these names is ignored anyway.

Acceptance

  • skills/oschema/oschema.md no longer says root objects "MUST have a sid: str field."
  • Every example in the file marks root objects with [rootobject] and omits sid/created_at/updated_at.
  • The Claude-installed copy at ~/.claude/skills/oschema/SKILL.md picks up the change after the next mother-skill sync.

Refs

  • Discovered while addressing lhumina_code/hero_rpc#72 (the hero_service catalog schema redeclares both these auto-fields and types that live in herolib_core::base).
  • Generator code: hero_rpc/crates/generator/src/rust/rust_struct.rs.
  • Parser code: hero_rpc/crates/oschema/src/parser.rs.
## Problem `skills/oschema/oschema.md` (the authoritative source of the `oschema` skill loaded by Claude) instructs authors that every root object **MUST** declare a `sid: str` field, and every example also declares `created_at: otime` / `updated_at: otime` by hand. Excerpts: ``` Root objects ... Mark a type as a root object using the `[rootobject]` annotation in the comment above the type definition. Root objects **MUST** have a `sid: str` field. ``` ```oschema # Task [rootobject] Task = { sid: str # REQUIRED for root objects title: str status: Status created_at: otime } ``` ## Why this is wrong The Rust struct generator in `hero_rpc` ([`crates/generator/src/rust/rust_struct.rs`](https://forge.ourworld.tf/lhumina_code/hero_rpc/src/branch/development/crates/generator/src/rust/rust_struct.rs#L368-L389)) **auto-injects** `sid: SmartId`, `created_at: u64`, and `updated_at: u64` on every root object, and silently skips any schema-declared duplicates: ```rust // For root objects, add server-managed fields first (sid, created_at, updated_at) // These fields are automatically managed by the server and not part of the schema if obj.is_root_object { output.push_str(" pub sid: SmartId,\n"); output.push_str(" pub created_at: u64,\n"); output.push_str(" pub updated_at: u64,\n"); } for field in &obj.fields { // Skip server-managed fields if they were explicitly defined in schema if obj.is_root_object && (field.name == "sid" || field.name == "created_at" || field.name == "updated_at") { continue; } ``` The parser detects root-object-ness from **either** a `sid` field **or** a `[rootobject]` comment marker ([`crates/oschema/src/parser.rs:655-661`](https://forge.ourworld.tf/lhumina_code/hero_rpc/src/branch/development/crates/oschema/src/parser.rs#L655)), so `[rootobject]` alone is sufficient — `sid: str` was never required. The net effect of the current skill: 1. Authors hand-write boilerplate that's already automatic. 2. The `sid: str` line in the schema is **misleading** — the generated field is `pub sid: SmartId` (not `String`). 3. The `created_at: otime` line in the schema is **misleading** — the generated field is `pub created_at: u64` (not `OTime`). 4. New OSchema authors copy the bad pattern from examples — that's how it ended up in `hero_service/schemas/catalog/catalog.oschema` and propagated. ## What to change in the skill 1. **Rule:** root objects are declared with `[rootobject]` in the leading comment. **Do not declare `sid`, `created_at`, or `updated_at`** — they are auto-injected by the generator and managed by the server. 2. **Update all examples** in `skills/oschema/oschema.md` (Project / Task / Product / etc.) to drop the explicit `sid` / `created_at` / `updated_at` lines. 3. **Add a note** that the actual generated types are `sid: SmartId`, `created_at: u64`, `updated_at: u64` — so anything written in the schema for these names is ignored anyway. ## Acceptance - `skills/oschema/oschema.md` no longer says root objects "MUST have a `sid: str` field." - Every example in the file marks root objects with `[rootobject]` and **omits** `sid`/`created_at`/`updated_at`. - The Claude-installed copy at `~/.claude/skills/oschema/SKILL.md` picks up the change after the next mother-skill sync. ## Refs - Discovered while addressing https://forge.ourworld.tf/lhumina_code/hero_rpc/issues/72 (the `hero_service` catalog schema redeclares both these auto-fields and types that live in `herolib_core::base`). - Generator code: `hero_rpc/crates/generator/src/rust/rust_struct.rs`. - Parser code: `hero_rpc/crates/oschema/src/parser.rs`.
Author
Owner

Worked on branch issue-275-oschema-skill-sid (commit 8118f85).

Edits to skills/oschema/oschema.md

  1. Rule rephrased — Root Objects section no longer says root objects MUST declare sid: str. Replaced with an explicit auto-injection callout: the generator injects sid: SmartId, created_at: u64, updated_at: u64; authors MUST NOT declare them.
  2. Examples scrubbed of manually declared sid: str / created_at: otime / updated_at: otime:
    • Task (Root Objects intro)
    • Product (Field Decorators)
    • Project + Task (Complete Example with Root Objects)
    • User + Task (Using SID in Schemas) — also reframed as [rootobject] declarations with a typed sid reference field instead of demoing manual sid: str.
    • User (Name Fields Consistently) — renamed created_at to signup_date to avoid copy-paste confusion.
  3. Reference fields converted from project_sid: str to project_sid: sid so the typed reference is the path of least resistance.

Verification

  • grep 'sid: str' skills/oschema/oschema.md → 0 hits.
  • grep 'created_at\|updated_at' skills/oschema/oschema.md → only the new auto-injection callout at line 347.

Ready for review.

Worked on branch `issue-275-oschema-skill-sid` (commit 8118f85). ### Edits to `skills/oschema/oschema.md` 1. **Rule rephrased** — Root Objects section no longer says root objects MUST declare `sid: str`. Replaced with an explicit auto-injection callout: the generator injects `sid: SmartId`, `created_at: u64`, `updated_at: u64`; authors MUST NOT declare them. 2. **Examples scrubbed** of manually declared `sid: str` / `created_at: otime` / `updated_at: otime`: - `Task` (Root Objects intro) - `Product` (Field Decorators) - `Project` + `Task` (Complete Example with Root Objects) - `User` + `Task` (Using SID in Schemas) — also reframed as `[rootobject]` declarations with a typed `sid` reference field instead of demoing manual `sid: str`. - `User` (Name Fields Consistently) — renamed `created_at` to `signup_date` to avoid copy-paste confusion. 3. **Reference fields** converted from `project_sid: str` to `project_sid: sid` so the typed reference is the path of least resistance. ### Verification - `grep 'sid: str' skills/oschema/oschema.md` → 0 hits. - `grep 'created_at\|updated_at' skills/oschema/oschema.md` → only the new auto-injection callout at line 347. Ready for review.
Author
Owner

Resolved in PR #278 (squash-merged into development).

  • Root Objects rule rephrased — [rootobject] is sufficient; sid/created_at/updated_at are auto-injected by the generator and MUST NOT be declared.
  • Auto-injection callout added naming real generated types (sid: SmartId, created_at: u64, updated_at: u64).
  • All example blocks (Task / Product / Project / SID demos / naming-convention examples) scrubbed.
  • Reference fields converted from project_sid: str to typed project_sid: sid.

Verification (grep 'sid: str') → 0 hits in example blocks.

Resolved in PR #278 (squash-merged into `development`). - Root Objects rule rephrased — `[rootobject]` is sufficient; sid/created_at/updated_at are auto-injected by the generator and MUST NOT be declared. - Auto-injection callout added naming real generated types (`sid: SmartId`, `created_at: u64`, `updated_at: u64`). - All example blocks (Task / Product / Project / SID demos / naming-convention examples) scrubbed. - Reference fields converted from `project_sid: str` to typed `project_sid: sid`. Verification (`grep 'sid: str'`) → 0 hits in example blocks.
timur closed this issue 2026-05-20 23:32:00 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_skills#275
No description provided.