Skip to content

finding: GET /api/v1/meta/diagnostics reports 94/94 registry entries INVALID — the platform's own __search column carries a key FieldSchema rejects, plus a missing config #7561

Closed

Description

@huangyiirene

Symptom

GET /api/v1/meta/diagnostics reports 94 of 94 registry entries as INVALID — every entry in the registry, sys_* and showcase_* alike, with no exceptions.

There are only two distinct error shapes behind all 94:

fields.__search: Unrecognized key 'index'
config: expected record, received undefined

Both are self-inflicted: __search is the platform's own auto-added search column, and the entries are otherwise healthy — they serve, they round-trip, and nothing else in the run pointed at broken schemas.

Expected: a healthy registry validates clean, and the diagnostics endpoint's verdict means something. At 94/94 INVALID the endpoint carries no signal — a genuinely broken object is indistinguishable from the baseline, so any future gate or dashboard built on it reads as permanently red.

Root cause

Not located by the report at code level. The evidence constrains it to the validator/producer seam rather than to the objects being validated:

  • The rejected key index on fields.__search is written by the platform when it provisions the search companion column, and FieldSchema does not admit that key — so either the writer is stamping a key it should not, or the schema is missing a key it must accept. One of the two is wrong; the report does not say which.
  • config: expected record, received undefined is the same class: the shape validation expects a record that the registry entry does not carry at all.

Same family as a fix that already landed — read it first

#6810 (closed 2026-08-08, domain:engine-core): "applySystemFields stamps indexed on organization_id — a key FieldSchema rejects by name, so every registry-backed object read answers _diagnostics: { valid: false }". That is the identical mechanism — a platform-stamped system field carrying a key the field schema rejects, invalidating every object — one field and one key over.

Its fix closed before this build was cut (a86db175, 2026-08-10), so the organization_id/indexed instance is presumably gone and this is the next instance of the same class (__search/index) rather than a regression of #6810 itself. Worth treating as such: fixing one key at a time will keep producing this card. The durable fix is to make the system-field stamper and FieldSchema share one vocabulary, and — separately — to give the diagnostics endpoint a pin that fails when the baseline registry is not clean, since 94/94 INVALID survived undetected until a manual QA read.

Reproduction

  1. Boot the showcase.
  2. GET /api/v1/meta/diagnostics.
  3. All 94 entries report INVALID; group the errors — only the two shapes above appear, across both sys_* and showcase_* entries.

Source

Extracted from the QA run #7514 (framework a86db17, console 09987b68).

Activity

  1. os-zhuang commented on Aug 11, 2026

    @os-zhuang
    Contributor

    Findings triage: resolved the co-applied pm:queue + finding dual state → pm:queue (kept domain:engine-core). The card's "not located at code level" is now closed one step further: on origin/main @ 34c01a5, the __search companion writer stamps the rejected key at packages/objectql/src/search-companion.ts:155 (index: true, with the docblock at :132 explaining why it wants an index) — the exact #6810 mechanism (applySystemFields/indexed on organization_id) one field over. That makes this a concrete, located defect: a diagnostics endpoint at 94/94 INVALID carries no signal.

    • Routing confirmed by the anchor: packages/objectql ⇒ domain:engine-core (the domain table pins the __search companion to the engine-core row explicitly).
    • Dispatch note: the card's durable-fix framing deserves to survive into the claim — one shared vocabulary between the system-field stampers and FieldSchema, plus a clean-baseline pin on the diagnostics endpoint, rather than a third one-key patch.
    • Dup check: meta: applySystemFields stamps indexed on organization_id — a key FieldSchema rejects by name, so every registry-backed object read answers _diagnostics: { valid: false } #6810 closed 2026-08-08 (predecessor instance, cited); no open card names __search/index or the diagnostics baseline.
    • target:v17: not applied — diagnostics endpoint signal quality; objects serve correctly, no user-facing data error today. Flag for reconsideration if a release gate is built on this endpoint.

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  2. self-assigned this
    on Aug 11, 2026
  3. os-help commented on Aug 11, 2026

    @os-help
    Collaborator

    Claim: PM loop round 1 (engine-core seat, 2026-08-11 shift)
    Session: session_01PemxJmMZT9JphrsNx886Ce
    Branch: claude/issue-7561-search-companion-retired-index
    Worktree: objectstack-issue-7561 (mode:cloud — own clone)
    Domain: domain:engine-core
    File surface: packages/objectql/src/search-companion.ts (the index: true stamp, :155), the diagnostics/validation seam in packages/objectql/src/registry.ts for the config half, packages/objectql/src/metadata-diagnostics.test.ts (or a new baseline pin), changeset. ⛔ packages/spec FieldSchema is NOT expected to change (the retired-key evidence points the other way); if measurement demands a spec touch it goes through the #6017 declare-and-proceed convention (stop on breach; explain in the report)
    Container & model: M (one confirmed mechanism + one unlocated half + a baseline pin), mode:cloud, model: opus
    Serial constraints cleared: search-companion.ts/registry.ts have no live claim (last registry-adjacent merges #7452/#7482 landed; #4953 is a finished-claim anchor); round sibling #7537 owns engine.ts expandRelatedRecords — disjoint; #7557 (which would also touch registry.ts) deliberately held to a later round behind this card.

    Label note for the triage seat: this card was filed with both finding and pm:queue by the QA filer; this seat read the human-set pm:queue as operative and swapped it to pm:dispatched, leaving finding untouched since grading labels are the triage seat's to produce/remove. If that read is wrong, say so and the claim unwinds.


    Generated by Claude Code

  4. os-help commented on Aug 11, 2026

    @os-help
    Collaborator

    Label collision repaired (engine-core seat, session_01PemxJmMZT9JphrsNx886Ce): the triage seat resolved the dual state to pm:queue (dropping finding) at 06:14:12Z, and this seat's claim-time full-replacement label write — composed from a pre-06:14 read — re-added finding while swapping in pm:dispatched. finding is now removed per the triage resolution; final state domain:engine-core + pm:dispatched, read back and verified. The claim itself stands (no competing claim; the triage comment explicitly does not claim). The triage comment's dispatch note (shared stamper/FieldSchema vocabulary + clean-baseline diagnostics pin, not a third one-key patch) is adopted into the dispatch brief.


    Generated by Claude Code

  5. os-help commented on Aug 11, 2026

    @os-help
    Collaborator

    Dispatched mode:cloud: session session_019MfMR4enbnPgoH6efeKmDJ (created 06:18Z, model opus, outcome branch = the claim branch). Report expected as an issue comment here at draft-PR time.


    Generated by Claude Code

  6. os-help commented on Aug 11, 2026

    @os-help
    Collaborator

    os-dev report — #7561 · draft PR #7591 · branch claude/issue-7561-search-companion-retired-index · session_019MfMR4enbnPgoH6efeKmDJ

    premise_still_valid: true — both halves, re-measured on origin/main @ 97b6658

    Filed against a86db175; every quoted fact re-verified against current origin/main (not a working tree). Both error shapes reproduce verbatim from live code:

    fields.__search: Unrecognized key(s) on this field: `index`.   → unrecognized_keys
    config: Invalid input: expected record, received undefined     → invalid_type
    

    PM mechanism assumptions — verdicts

    Assumption Verdict
    index: true stamped at search-companion.ts:155 ✅ confirmed — and it is the only field-level index/indexed writer left in packages/
    Docblock :132 claim "It IS indexed" is false; the stamp buys nothing ✅ confirmed — index removed in 16.x (#2377, ADR-0049); field.zod.ts:~921 states the driver builds indexes from indexes[] and a field-level index: true created no index. No driver reads a field-level index today. Dropping it is behaviour-neutral for indexing.
    config half unlocated ✅ now located — DefaultDatasourcePlugin.registerVisibility (runtime/src/default-datasource-plugin.ts:250) publishes {name,label,driver,origin}; DatasourceSchema.config (spec/src/data/datasource.zod.ts:406) is a required z.record(...). origin is declared (:522) — config is the only missing key. The seam is getMetaDiagnostics → computeMetadataDiagnostics, not registry.ts:456/:1812.
    Follow #6810's fix shape ✅ followed at the producer — ⚠️ with one measured divergence, below

    The one place measurement overrode the precedent

    #6810 moved the retired flag into the object's indexes[]. This card does not, and that is measured, not overlooked:

    Declaring one would buy write amplification on every row for a read path that cannot use it. Recorded in the docblock; a real substring index needs an IndexSchema that can express one → filed as a follow-up consideration in the PR, not done here (per the PM's "measure whether that is worth doing in this card or file it as a finding").

    ⛔ No packages/spec touch — #6017 convention not triggered

    Neither half is fixed by widening FieldSchema (re-opens a key ADR-0049 retired) nor by relaxing DatasourceSchema.config (a real datasource genuinely needs its config; relaxing trades one honest verdict for a permanently weaker one). Both fixed at their producers.

    Security note on the config fix: stamped {}, deliberately not this.def.config. That row is a non-secret projection — the host's real config carries connection credentials, and DatasourceSchema itself refuses an inlined password, so publishing it would put credentials on GET /api/v1/meta/datasources for every metadata reader. No information lost vs. the omitted key; only the spelling changes.

    Durable ask — all three delivered

    • (a) baseline → 0 INVALID: both shapes eliminated at their producers.
    • (b) clean-baseline pin: diagnostics-clean-baseline.test.ts — registry built the way the platform builds one (every stamper live, both tenancy modes); asserts the sweep finds nothing and names both error shapes explicitly, so a reintroduction fails on the reported shape rather than a generic count.
    • (c) class fix: stamped-system-fields-spec-conformance.test.ts — every field applySystemFields + provisionSearchCompanion stamp, across every ownership / managedBy / systemFields / tenancy branch, walked through FieldSchema, plus an explicit "no index/indexed on any stamped field" assertion.

    Both pins guard the guard (fail if the matrix stamps nothing, or if __search stops being provisioned) so neither passes vacuously.

    Reverse-verified red against the unfixed producers — class pin 24/24 failed with Unrecognized key(s) … \index`; baseline pin **4 failed** naming all 5 objects × both tenancy modes plus config: invalid_type`.

    ⚠️ search-companion.test.ts asserted expect(col.index).toBe(true) — it pinned the defect, not the contract. Now pins the key's absence, with a comment saying why.

    Gates — all run, all pass

    check:durability-log-level ✅ · check:engine-double-contract ✅ · check:error-code-casing ✅ · check-engine-split-ratio.mjs ✅ · check:nul-bytes ✅ · spec check:generated n/a (no spec touch) · objectql + runtime typecheck ✅ clean · build closure ✅

    Suites: full @objectstack/objectql 180 files / 3183 pass; runtime (default-datasource-plugin, standalone-stack, app-plugin) 57 pass; plugin-pinyin-search 14 pass. Changeset included (patch ×2).

    CI: in_progress at report time — PM owns convergence, ready-flip and landing.


    Generated by Claude Code

  7. os-help commented on Aug 11, 2026

    @os-help
    Collaborator

    ACCEPT — PR #7591 reviewed and moving to landing (engine-core seat, session_01PemxJmMZT9JphrsNx886Ce). Verified against GitHub, not the report's claims:

    • 6 files, exactly the declared surface minus registry.ts (the config half turned out to live in runtime's datasource-visibility registration, not the registry seam — a measured relocation, reported as such). ⛔ No packages/spec touch — the [PM seat] domain:spec — 🟢 os-bill · session_01T1Y1g92DfSEz93iuCisju4 #6017 convention was correctly not triggered; both halves fixed at their producers.
    • Deletion claim independently verified by this seat on origin/main: zero field-level index/indexed readers in packages/drivers/** or packages/objectql/** (the only .index( hits are Knex's table.index() building from indexes[]; positive control confirms the scan works). Dropping the stamp is behaviour-neutral for indexing, as claimed.
    • The measured divergence from meta: applySystemFields stamps indexed on organization_id — a key FieldSchema rejects by name, so every registry-backed object read answers _diagnostics: { valid: false } #6810 (no indexes[] replacement — the column's only reader is a $contains no B-tree serves, and IndexSchema cannot express a trigram index) is sound and recorded in the docblock rather than silently omitted.
    • The config: {} security judgment is correct: the host's real config carries credentials, DatasourceSchema itself refuses an inlined password, and /meta/datasources is read by every metadata reader.
    • Durable ask delivered in full: producer fixes + baseline pin naming both error shapes + the class pin walking every stamper × every branch through FieldSchema, both guard-the-guard'd; reverse verification red (24/24 and 4) with the card's exact shapes. The old expect(col.index).toBe(true) defect-pin is flipped with a why-comment — a load-bearing flip, not a deletion.
    • CI: 25/25 checks completed, ESLint and TypeScript Type Check job conclusions both success.
    • Cross-package production touch: packages/runtime/src/default-datasource-plugin.ts (cli lane's package family) — producer-side fix per protocol; declared on the cli seat post.

    Flipping ready + arming auto-merge now; the merge queue owns landing.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions