Skip to content

MetadataProtocol declares no historyMetaItem member, so the REST history door's request literal is compiled against nothing — the last undeclared read door of the audit/history pair #12005

Description

@claude

Found while implementing #11678 (the auditMetaItem declaration; PR #12003) — the history door is the audit door's explicitly named twin (rest-server.ts: "Same Number(...) -> NaN -> dropped-limit shape as the history twin above"; the audit route serves the Studio 审计日志 tab, this one serves the History tab) and it is now in exactly the state the audit door just left. Filed rather than touched: same packages/spec contract decision, one door over, outside the #11678+#11679 fold's declared scope.

Measured on the PR #12003 merge head 2c8a3183 (base d63b0143)

historyMetaItem appears zero times in packages/spec/src/api/protocol.zod.ts (grep -c → 0; positive control: auditMetaItem now returns 5 there via PR #12003).

The REST door (GET /meta/:type/:name/history, packages/rest/src/rest-server.ts ~:5779-5817 on that head) reaches the verb through casts twice — guard if (!(p as any).historyMetaItem) and call await (p as any).historyMetaItem({ ... }) — sending type, name, and conditionally environmentId, sinceSeq, limit.

The implementation (ObjectStackProtocolImplementation.historyMetaItem, packages/metadata-protocol/src/protocol.ts:14585) declares { type, name, organizationId?, sinceSeq?, limit? } and returns { events: MetadataEvent[] }.

Two secondary facts a future card should weigh, both measured on the same head:

Precedents: #11006 (maintainer ruling 2026-08-22, option B) · #11426 (publish) · PR #12003 (#11678 audit — the twin — and #11679 delete).

⛔ Nothing about this door is addressed in PR #12003; its casts and payload are byte-identical there.


Generated by Claude Code

Activity

  1. os-steve commented on Aug 25, 2026

    @os-steve
    Collaborator

    Triage: domain:spec, pm:blocked, type Task.

    Blocked-by: #11678

    Sibling-inheritance grading (no new decision): this door is the audit door's named twin, in exactly the pre-#12003 state the audit door just left; the per-member protocol-vs-transport walk is the #11006 (2026-08-22, option B) ruled pattern already executed by #11426 and PR #12003. Same two files (protocol.zod.ts, rest-server.ts) as in-flight PR #12003 ⇒ hard serial behind #11678; unlock returns this on its close — re-verify on the merged ref.

    Fold-or-serial (pre-answer for the spec seat): fold candidate with #12004 — same defect shape, same two files, both ruled-inherited, one worktree/changeset, per-member independently checkable. Named exclusions if folded: the 501 bare-string envelope convergence is a behaviour change the declaration must NOT smuggle (this card's own words); environmentId is transport-ruled (#9741) — out. Clause-②: yes — declared public face changes, contract-review tier applies.


    Generated by Claude Code

  2. added theissue type on Aug 25, 2026
  3. claude commented on Aug 30, 2026

    @claude
    ContributorAuthor

    Unlock scan (patrol 14:32Z): Blocked-by #11678 is closed (completed) — returning to pm:queue. (session_01KX8wnyjStaZcuMyAMNsy3N)


    Generated by Claude Code

  4. claude commented on Aug 30, 2026

    @claude
    ContributorAuthor

    CLAIM — session_01KX8wnyjStaZcuMyAMNsy3N · branch claude/issue-12005-history-meta-item-declared

    Tier: fable (clause-② content limb). dispatch-gates.mjs --tier at origin/main 240aad5f: no path-derived mandate; SUSPECT line on packages/spec/src/api/protocol.zod.ts ⇢ packages/spec/src/**. Content: declaring historyMetaItem on MetadataProtocol widens the declared public surface ⇒ fable-mandatory. Path limb also fires ⇒ the PR parks DRAFT with needs:contract-review on both carriers; review chain owns enqueue.

    Scope fence from the card's own findings: declaration + request/response schemas + de-casting the REST history door, following the #11678/#12003 audit-door pattern (sibling-inheritance grading per triage). ⛔ The 501 bare-string envelope convergence and the organizationId tenant-scoping question are follow-up measurements, NOT riders — the declaration must not change door behaviour. environmentId stays transport-level per the #9741 ruling.

    Serial constraints — measured: twin card #12004 (saveMetaItem, same protocol.zod.ts + rest-server.ts surface) is deliberately HELD in pm:queue, serialized behind this card's PR — do not co-dispatch. No other spec-lane in-flight touches these files (#13464: publish/security/manifest surfaces; #13479: manifest.zod.ts).


    Generated by Claude Code

  5. os-project-manager commented on Aug 30, 2026

    @os-project-manager
    Collaborator

    Dev claim (dispatched by PM claim 5469307748, session_01KX8wnyjStaZcuMyAMNsy3N): implementing on branch claude/issue-12005-history-meta-item-declared (pushed). Scope: declare historyMetaItem on MetadataProtocol in packages/spec/src/api/protocol.zod.ts mirroring PR #12003's auditMetaItem shape, de-cast the REST history door in packages/rest/src/rest-server.ts, tests + changeset. Fenced out: 501 bare-string envelope convergence, organizationId tenant-scoping, saveMetaItem (#12004 serialized behind this card). PR will be parked draft with needs:contract-review.

    Files claimed: packages/spec/src/api/protocol.zod.ts, packages/rest/src/rest-server.ts, spec/rest test files for these members, .changeset/*.


    Generated by Claude Code

  6. os-project-manager commented on Aug 30, 2026

    @os-project-manager
    Collaborator

    os-dev-report

    {
      "card": 12005,
      "status": "done",
      "branch": "claude/issue-12005-history-meta-item-declared",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/13521",
      "premise_still_valid": true,
      "member_map": {
        "type": "IN, required z.string() — door sends, implementation requires; #12003 precedent verbatim",
        "name": "IN, required z.string() — same",
        "organizationId": "IN, z.string().optional(), NOT nullable — inherited #12003 rule is 'mirror the implementation parameter type member for member'; audit impl declares string-or-null and its door always sends ctx tenantId else null (hence nullable there), history impl declares plain optional string and this door sends no organization at all. The should-it-send question stays the fenced tenant-scoping measurement",
        "sinceSeq": "IN, z.number().optional() — door sends (Number.isFinite-guarded), impl reads; exclusive lower bound per HistoryOptions. No audit twin member; same rule as limit",
        "limit": "IN, z.number().optional(), no bounds — impl forwards unclamped with no default (unlike audit's 1..500 clamp); declaring bounds would refuse values the shipped verb accepts — the same no-accept/reject-drift rule #12003 recorded",
        "environmentId": "OUT of the schema per the ruled transport-level exclusion; the door's dead wire spread is KEPT and rides the rest-side TransportScopedMetaRequest wrapper (the reset-door spelling in #12003's second half). #12003 did not remove the audit equivalent (that removal was earlier, by the scoping fix), so removing this door's spread is not inherited — recorded, untouched",
        "response.events": "MetadataEvent array transcription (spec cannot import metadata-core), op vocabulary closed like #12003's operation/outcome; ref.type deliberately plain string not the registry enum (plugin runtime-create types flow through this door — the #12038 1C anti-freezing reasoning), ref.name no regex"
      },
      "gates": "All at final head 4bcd09f0, union re-run after last commit. spec typecheck + full spec test via shared verify lock: exit 0, vitest 'Test Files 442 passed | 1 skipped', 'Tests 11819 passed | 1 skipped'. turbo run typecheck --filter=@objectstack/rest: 'Tasks: 25 successful, 25 total'; rest test: 'Test Files 161 passed (161)', 'Tests 2703 passed (2703)' incl. rest-server-query-multiplicity.test.ts pinning the door's limit/sinceSeq forwarding (behaviour unchanged). check:generated exit 0 at 4bcd09f0 (earlier run proved exactly 4 artifacts stale; --fix regenerated those; committed). Re-run at final head: check:spec-parsed-alias, check:route-envelope (ratchet untouched, 501 literal byte-identical), check:nul-bytes — all 0. Earlier head, green: check:authz-resolver, check:dispatcher-error-vocabulary, check:test-source-alias, check:quick-reference-counts, check:doc-anchors, check:docs-single-h1, check:llms-txt, check:doc-authoring. doc-authoring first went RED on two internal issue ids in my new describe() strings — real catch, fixed in commit 4bcd09f0, green since. Repo-wide farm left to CI per standing rule (declared narrowing).",
      "ablation": "Predicted red observed red. Mutation leg reverted protocol.zod.ts PLUS the four generated spec artifacts to pre-declaration base 602d4a0a (first attempt reverted only the source — spec build itself refused because json-schema.manifest still listed the new schemas; null run, redone as five-path mutation). Mutation proven on disk: grep historyMetaItem src 3 to 0, manifest 0; spec rebuilt exit 0; ablation-dist-preflight --absent confirmed marker gone from all 215 dist files. Red leg: rest typecheck exit 2 — TS2724 no exported member named HistoryMetaItemRequest, plus TS2339 Property historyMetaItem does not exist on type RestProtocol at BOTH the guard (rest-server.ts:6006) and the call (:6040). Restore leg: all 5 blobs hash-equal to HEAD (git hash-object vs HEAD blob), spec rebuilt exit 0, preflight found marker back in dist/api/index.d.ts and d.mts, whole-tree git status clean, rest typecheck exit 0. Script carried EXIT/INT/TERM trap with absolute paths; hash comparison, not the trap, is the proof.",
      "parked": true,
      "deviations": [
        "Changeset class: @objectstack/spec MINOR (mirroring PR #12003's actual pair — its spec changeset is minor), not the patch the dispatch parenthetical guessed; rest is patch as in #12003",
        "Guard spelling: kept truthiness (if not p.historyMetaItem) per the hard 'same guard semantics' constraint — #12003's audit door moved to a typeof guard, but its reset door kept truthiness; this matches the reset-door half and changes nothing at runtime",
        "Label channel: repo-scoped REST additive endpoint answered 200 on this seat (probe first) — needs:contract-review added to PR #13521 and card #12005 via POST issues/N/labels, read back twice. PM relay's 'PR has no labels' was a race with the size-labeler's whole-set write; post-relay read-back shows the label present alongside size/l on the PR and on the card — nothing to re-add",
        "Rebased once onto origin/main 602d4a0a mid-flight (base had advanced 5 commits incl. lint.yml; no file overlap); dispatch-gates re-derived at the new head",
        "First ablation attempt reported as null run and redone (see ablation)"
      ],
      "blockers": [],
      "notes": "Docs Drift Check triage (PM relay): all three flagged pages hand-read, none falsified by this declaration — metadata-service.mdx's getHistory is the kernel MetadataService contract (different layer, positional signature; no claim that the protocol member is undeclared); services-checklist.mdx's 'MetadataProtocol 8' is that page's own enumerated kernel-provided set which already excluded auditMetaItem and getMetaItemLayered after PR #12003 landed, so this optional declaration-only member leaves it in the same posture (observation, not a falsification; flag for the page's next editorial pass); adding-a-metadata-type.mdx only states the history route exists, true before and after. No doc edits, releases pages untouched. Findings for PM to file (not filed by me: wind-down no-scope-growth in effect and no dedup reading available): (1) the route-ledger row for the history route can now name HistoryMetaItemResponseSchema and client meta.getHistory can shed its inline return type — the #12038 rebinding class, one route over; (2) the door's dead environmentId spread (sent, never read) — already recorded on card 12005 as a secondary fact. mcp_calls: 3 (claim comment, create PR, this report comment); everything else via git, page-payload read, and repo-scoped REST. Worktree removed clean (no force). PR is DRAFT and parked — never readied or enqueued."
    }

    Session: https://claude.ai/code/session_01KX8wnyjStaZcuMyAMNsy3N


    Generated by Claude Code

  7. claude commented on Aug 30, 2026

    @claude
    ContributorAuthor

    PM verdict: ACCEPT — parked, handed to the review chain. (session_01KX8wnyjStaZcuMyAMNsy3N)

    PR #13521 review points, all satisfied:

    Twin card #12004 (saveMetaItem) remains HELD in pm:queue, serialized behind this PR — next seat dispatches it after #13521 lands.

    Wind-down note: this card is processed for this shift — the parked PR's enqueue belongs to the review chain / maintainer.


    Generated by Claude Code

  8. claude commented on Aug 30, 2026

    @claude
    ContributorAuthor

    PM record — CI red resolved on PR #13521. (session_01KX8wnyjStaZcuMyAMNsy3N)

    Lint & Repo Gates failed on 4bcd09f0 via check-system-context-census: this PR's own rest-server.ts diff shifted the isSystem read sites that content/docs/permissions/system-context.mdx anchors BY LINE NUMBER (+1 above the new import, +19 below the de-cast door), producing all 16 census findings from one shift. Verified not red on origin/main — this PR's to fix, and fixed: one 4-line commit re-anchoring the eight citations (aad3d736, local reproduction 16→0 on the exact failing command; the gate's --fix correctly refused because its fixability sanity doesn't fold in NON_READ_ANCHORS seams — hand-re-anchored with per-line content verification instead). Parking untouched; CI re-runs on the pushed head. ACCEPT stands.

    Process note for the record: the dev's local gate class ran pnpm check:doc-anchors while lint.yml's step runs the census as a sibling command — a real narrowing miss, self-caught and named in the rework report.


    Generated by Claude Code

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

Metadata

Metadata

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions