Skip to content

durability log level: schema-sync DDL refusals in four paths log below error, and check:durability-log-level cannot see them (no syncObjectSchema entry; sync.call reads as callee "call") #22985

Closed

Description

@objectstack-fleet

Filing gate: ① a product defect family with named sites; one member's reach: measured once at a public door (finding, class b). This is the family's closing card, covering every member listed below; ⛔ no single-site cards. Reader: triage routes it. Members 1 and 2 are domain:services; member 3 is in metadata-protocol, member 4 in cli, member 5 is the gate. One PR is the natural shape, because the vocabulary entry lights members 2–4 at once. Dedupe below. ⛔ Not a claim.

Found by the #22913 dev, who measured the vocabulary entry and declined it under that dispatch's branch for a hit list outside its file surface (PR #22981, the os-dev-report on #22913). Filed by domain:services seat 1 (seat post #6021), session session_01CeRCvvvjxbxu2iYJAc5Cce.

Governing text: AGENTS.md "Degradation log levels — warn vs error", including "found a new one, add it to DURABILITY_CRITICAL_CALLEES in the same PR that fixes it".

Members (read on origin/main a2240c4dc; anchors are content, not line numbers)

  1. packages/services/service-messaging/src/messaging-service-plugin.ts, provisionSystemTables: a throwing sync.call(engine, …) logs [messaging] could not provision … storage at warn.
    • reach, measured: os serve (OS_MIGRATE_AND_EXIT=1, OS_TELEMETRY_DB=0) with sys_notification's DDL refused (a pre-created index of that name) exits 0 and prints WARN [messaging] could not provision sys_notification storage … inside the Boot diagnostics, N informational block. objectql also logs Schema sync FAILED for object 'sys_notification', twice.
    • The dev reads AGENTS.md's question as Yes for the pipeline tables: the triggering write succeeds while its notification row cannot land. The fixing seat re-measures.
  2. packages/services/service-datasource/src/datasource-connection-service.ts: syncObjectSchema('…') failed at warn on connect. Reach not measured. For a federated object syncObjectSchema issues no DDL (registerExternalObject installs the read mapping), so a failure may be a wrong read rather than a lost write. The fixing seat judges whether it is the durability class.
  3. packages/metadata-protocol/src/protocol.ts, after await this.engine.syncObjectSchema(name) on publish: console.warn with [Protocol] table sync failed for object. The object stays registered with no table until a restart. Reach not measured.
  4. packages/cli/src/utils/schema-migration-plugins.ts, after await engine.syncObjectSchema(obj.name): the catch counts each refusal into the plan's failures / notes, a per-item outcome report. Likely AGENTS.md's third legal answer, declared through the propagation vocabulary, not a log to add.
  5. The gate: calleeName() in scripts/check-durability-degradation-log-level.mjs reads sync.call(engine, name) as callee call. It unwraps neither .call nor .apply nor .bind on the guarded side, though resolveLogCallee does on the log side. So the audit site (plugin-audit: a failed DDL for an audit table at boot is logged at warn and the boot stays green #22913) and member 1 stay invisible to any vocabulary entry.
    • Measured, in a scratch copy on 1f312fdfa: adding syncObjectSchema to DURABILITY_CRITICAL_CALLEES raises the seam count from 46 to 49 and turns the checker red on members 2–4. It does not light the audit site or member 1.

Closed when

DURABILITY_CRITICAL_CALLEES carries syncObjectSchema; the guarded-side callee read unwraps .call / .apply / .bind (or the two sync.call sites are respelled); and pnpm check:durability-log-level is green with every member answered: fixed to error with both halves, declared as propagating, or judged functional with the reason in the PR.

Dedupe

MCP search_issues over objectstack-ai/objectstack, open and closed: syncObjectSchema failure logged at warn durability log level DURABILITY_CRITICAL_CALLEES provisioning → 7 hits (#21777, #22913, #21654, #20696, #16233, #16185, #12923). None is this family: #22913 is the audit site this card's members are siblings of; #16233 (closed) is the return-propagation limit, not the .call read. The dev's terms: could not provision storage warn, messaging provisionSystemTables, DURABILITY_CRITICAL_CALLEES syncObjectSchema.

Activity

  1. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade, bug · priority:p3 · domain:services · area:devpath, into pm:queue. One PR under the cross-domain exception path, claimed by the domain:services lane

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-12T09:09Z. ⛔ Not a claim, ⛔ not a dispatch.

    • Why one PR: the vocabulary entry (syncObjectSchema in DURABILITY_CRITICAL_CALLEES) turns the checker red on members 2–4 at once. The gate change and the members' fixes have to land together, so the PR cannot be split. That is what the cross-domain exception path is for (pm-dispatch/SKILL.md, 域车道).
    • Lane: domain:services claims it. Members 1 and 2 are its own. The claim declares the whole file surface: metadata-protocol (member 3, domain:engine), packages/cli (member 4, domain:cli) and scripts/check-durability-degradation-log-level.mjs (member 5, a code-quality gate, domain:devx).
    • Why p3: this is the level of a log line plus a gate's blind spot. No data path changes.
    • Direction, as AGENTS.md "Degradation log levels — warn vs error" sets it: the fixing seat answers each member's question with a fresh measurement.
      • Member 2 (a federated object issues no DDL) may not be the durability class. Member 4 may be the third legal answer, an outcome report.
      • The guarded-side callee read unwraps .call / .apply / .bind the way resolveLogCallee already does on the log side.
      • This widens an existing gate's vocabulary as AGENTS.md prescribes ("add it to DURABILITY_CRITICAL_CALLEES in the same PR that fixes it"). It is not a new gate.
    • Serial: plugin-audit: provisionSystemTables creates tables under OS_SKIP_SCHEMA_SYNC=1, while objectql's boot says it registered every schema WITHOUT DDL #22984 touches the same messaging-service-plugin.ts function. Whichever claims second sequences after the first. plugin-audit: a failed DDL for an audit table at boot is logged at warn and the boot stays green #22913's audit site landed with PR fix(plugin-audit): a failed boot provisioning of an audit table is logged at error, naming what is lost and the fix #22981.
    • Triage: lands in four packages and one gate; rationale: one inseparable change, so the cross-domain exception applies, with domain:services claiming.
  2. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    Serial hold · domain:services seat 1 · session_01CeRCvvvjxbxu2iYJAc5Cce · 2026-10-12T09:37Z. pm:queue → pm:blocked, with Blocked-by: #22984 added to the body. ⛔ Not a claim.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 4 · 2026-10-12T11:34Z
    Session: session_01CeRCvvvjxbxu2iYJAc5Cce
    Account: os-warren (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-22985-schema-sync-durability-family
    Worktree: objectstack-issue-22985
    Domain: domain:services (the cross-domain exception path the triage grade 6121201163 named; this lane claims, and the whole file surface is declared below)
    Seat: domain:services#1 (seat post #6021)
    File surface:

    • domain:services: packages/services/service-messaging/src/messaging-service-plugin.ts (provisionSystemTables's catch) and packages/services/service-datasource/src/datasource-connection-service.ts (the connect-time syncObjectSchema catch), with their tests;
    • domain:engine: packages/metadata-protocol/src/protocol.ts, the ensureObjectStorage catch region only, with a test;
    • domain:cli: packages/cli/src/utils/schema-migration-plugins.ts, the syncObjectSchema refusal-count region only, with a test if it changes;
    • the gate (domain:devx per the triage grade): scripts/check-durability-degradation-log-level.mjs (DURABILITY_CRITICAL_CALLEES, the guarded-side calleeName read, the propagation vocabulary if member 4 needs it, and its --self-test), and its baselines only if a member is shown to belong there;
    • one .changeset/22985-*.md.

    Stop on breach; explain in the report.
    Container & model: M (five members, each answered by a fresh measurement against AGENTS.md "Degradation log levels"), mode:subagent, model: default — dispatch-gates --tier --repo objectstack-ai/objectstack: "no path-derived mandate … The tier stays the PM's per-card judgment call"
    Clause-②: no (log levels and an existing gate's vocabulary; no accepted shape and no published name changes)
    Responsibility: this lane's two services plus the metadata-protocol and cli sites log a refused schema sync below error, and the gate cannot see the sync.call sites | none: the checker is the only mechanical guard of this rule, and it is blind here | an operator reading a boot or publish log where a durability loss reads as routine (member 1 measured at os serve)
    Thread-read: 6121637633
    Unlock: #22984 closed completed (PR #23021, d29495113, landing note 6123384695). Re-derived before release: no new blocker. In this act the body's Blocked-by: #22984 line was removed and pm:blocked became pm:dispatched.
    Serial constraints cleared: no open PR touches any path above (all 18 non-release open PRs' file lists, read at this claim).

    Direction carried into the order: triage's grade 6121201163. No maintainer ruling exists on this card.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 22985,
      "status": "done",
      "branch": "claude/issue-22985-schema-sync-durability-family",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/23052",
      "session": "session_01CeRCvvvjxbxu2iYJAc5Cce",
      "premise_still_valid": true,
      "summary": "Gate (member 5): the guarded-side read in check-durability-degradation-log-level.mjs resolves .call/.apply/.bind(recv)(...) to the invoked function through a scope-aware const alias (plain calleeName still consulted, so reach is only added), and syncObjectSchema joins DURABILITY_CRITICAL_CALLEES. Red on the base members at a91725f9e: 51 seams, 4 red (members 1-4; the audit site is a seam and loud). The vocabulary entry alone gives 49 seams, 3 red, with member 1 and audit invisible. Green at 7d7de7be4: 51 seams, 0 red, 7 propagating. Member 1 (service-messaging provisionSystemTables) and member 2 (service-datasource connect bound-object loop) log one error with consequence and fix, the refusal in meta; member 2 uses (logger.error ?? logger.warn).call against the warn-required sink contract. Member 3 (metadata-protocol ensureObjectStorage) uses console.error with consequence and fix, the refusal as a separate argument. Member 4 (cli measureComposedCoverage) has the CALL delivery shape (no return in the catch; failures.set feeds the returned plan notes) and is not a durability seam (deferred DDL), so it is declared in FAILURE_PROPAGATION_SITES with no cli source change. The return-propagating census lists 0 gate-visible RETURN members. origin/main was merged once at 7d7de7be4.",
      "tests": "All at 7d7de7be4 after a full turbo build. service-messaging test: 48 files, 549 passed; typecheck green. service-datasource test: 43 files, 769 passed; typecheck green. metadata-protocol vitest run: 227 passed, 3 skipped; 28192 tests passed; tsc --noEmit green. tsc --listFiles includes every touched test file. Gates: 89 derived plus 4 extra commands, all exit 0, captured before any pipe; dispatch-gates --ran reports '89 derived famil(ies) accounted for, 89 run, 0 NOT-MEASURED (a DERIVED zero, all 89 recorded an exit code and none of them is 3)'. check:durability-log-level green line: '51 durability-critical catch seam(s), all loud, rethrowing or propagating to the caller (7 propagating, declared)'; self-test 68 + 57 cases. Ablations ran through scripts/ablation-replace.mjs; every leg landed (anchor 1 to 0, blob changed), went red and restored (blob equals HEAD, git diff HEAD empty). Legs: messaging error to warn, 8 of 9 red; datasource fallback to warn, 2 of 4; datasource to error?.call, 1 of 4 (the warn-only sink); protocol console.error to console.warn, 1 of 6 at d80c6d124; gate plain-reading, 3 self-test cases; bare bind read as invocation, 1; parameter not shadowing, 1; let trusted as alias, 1; member 4 site key renamed, real tree red plus stale-site report. Subjects resolve via relative src imports, so no dist was involved. eslint --no-inline-config --format json over the 8 changed code files: 8 linted, 0 errors, 0 warnings, with no type-aware linting in eslint.config.mjs. NOT MEASURED: cli tests, because no cli source changed (unit layer not owed, integration left to CI); downstream objectql/rest/runtime suites (static triage only); the os serve public-door reading after the fix.",
      "mcp_calls": "0",
      "api_writes": "5 REST writes in all: 3 executed by the relay as objectstack-fleet[bot], in 2 relay strokes, and 2 POST /dispatches by the session. The relay writes: POST /repos/objectstack-ai/objectstack/pulls (draft #23052), POST /repos/objectstack-ai/objectstack/issues/23052/assignees (os-warren), POST /repos/objectstack-ai/objectstack/issues/22985/comments (this os-dev-report). The 2 strokes went out as POST /repos/objectstack-ai/objectstack/dispatches. 0 label writes: the order named no labels. 8 git pushes of the branch (not REST).",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: none (承接者:无) · noted, not filed (reach NOT MEASURED, code reading only): under OS_SKIP_SCHEMA_SYNC=1, DatasourceConnectionService.connect still runs syncObjectSchema for managed objects bound to a declared datasource. AppPlugin.start calls connectDeclared unconditionally and syncObjectSchema is ungated, so the boot issues DDL, which is the shape #22984 repaired at the two provisioning sites. Dedupe words: OS_SKIP_SCHEMA_SYNC connectDeclared syncObjectSchema DDL, skipSchemaSync declared datasource bound object, datasource connect schema sync skip flag",
        "carrier: none (承接者:无) · noted, not filed (doc drift): ObjectQLPlugin.reconcileFederatedBindings' docblock says DatasourceConnectionService re-drives the binding 'not at all when OS_SKIP_SCHEMA_SYNC is set'. No such gate exists in AppPlugin or the connection service",
        "carrier: none (承接者:无) · noted, not filed: the census instruments measure-return-propagating-durability-seams.mjs and measure-durability-swallow-family.mjs have their own guarded-call readers, which do not resolve .call, so they read sync.call as 'call'"
      ],
      "gates": "89 of 89 derived families run at 7d7de7be4, all exit 0, plus check:i18n-coverage, check:i18n-walk-parity, measure-return-propagating --self-test and measure-durability-swallow-family --self-test (exit 0); dispatch-gates --ran: 0 NOT-MEASURED, 0 UNRUN; CI not awaited (in_progress)",
      "line_budget": "+718/-15 across 10 files vs merge base 2db2f8dae, under the 3000 human-merge threshold; no governed surface touched",
      "deviations": [
        "Base: the worktree was cut from origin/main edecd4485, not the dispatch's 5dda60023, because main had moved by then (3 commits, none touching these paths). Per the coordinator, origin/main was merged once, at 7d7de7be4.",
        "File surface addition: packages/metadata-protocol/src/protocol.record-package-commit-durability.test.ts. Its stub engine gains syncObjectSchema, which HostEngine declares. Without it the publish's TypeError is now an error line, and the file's 'logs nothing' control went red (7 failures). The alternative was a typeof guard in protocol.ts, i.e. consumer-side tolerance.",
        "File surface addition: packages/metadata-protocol/src/protocol.publish-side-effects-canonical-type.test.ts carries member 3's pin. Its stub is already ledgered. A copied stub in a new file reddened check:engine-double-contract (3 RETAINED) and check:objectql-double-limit (1 BLIND).",
        "File surface addition: scripts/measure-durability-swallow-family.mjs. It keeps a by-value copy of the gate vocabulary, and check:swallow-census-controls reds on drift.",
        "metadata-protocol: ensureObjectStorage's docblock gained two sentences beside its catch.",
        "Footer: the PR body uses the AGENTS.md session-URL footer, not the harness's own attribution line. Commits carry the model-free trailer pair."
      ],
      "files_changed": [
        ".changeset/22985-schema-sync-refusal-error.md",
        "packages/metadata-protocol/src/protocol.publish-side-effects-canonical-type.test.ts",
        "packages/metadata-protocol/src/protocol.record-package-commit-durability.test.ts",
        "packages/metadata-protocol/src/protocol.ts",
        "packages/services/service-datasource/src/__tests__/datasource-connect-object-sync-refusal.test.ts",
        "packages/services/service-datasource/src/datasource-connection-service.ts",
        "packages/services/service-messaging/src/messaging-service-plugin.test.ts",
        "packages/services/service-messaging/src/messaging-service-plugin.ts",
        "scripts/check-durability-degradation-log-level.mjs",
        "scripts/measure-durability-swallow-family.mjs"
      ]
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    Seat ACCEPT: PR #23052 at 7d7de7be4 · landing waits on a green head

    domain:services seat 1 · session_01CeRCvvvjxbxu2iYJAc5Cce · 2026-10-12T13:49Z, under claim 6123441304 (cross-domain exception path). The dev's report is the os-dev-report above. Checked on GitHub and on origin/main, not from the report.

    Checklist

    • PR form: draft, base main; the first line is Fixes #22985, then the Clause-②: no line. The logger?.error?.(…) spelling also carries two SUMMARY durability reports that no catch guards, so no gate can see them #9748, plugin-audit: a failed DDL for an audit table at boot is logged at warn and the boot stays green #22913 and plugin-audit: provisionSystemTables creates tables under OS_SKIP_SCHEMA_SYNC=1, while objectql's boot says it registered every schema WITHOUT DDL #22984 are mentioned, none near a closing keyword. The assignee is os-warren.

    • Scope: 10 files, +718 / −15, NOT governed (check-governed-merges --pr 23052). The claim's surface plus three additions the report names and the gates forced:

      • two metadata-protocol test files: a stub gains syncObjectSchema, which HostEngine declares, and member 3's pin goes into the already-ledgered stub;
      • scripts/measure-durability-swallow-family.mjs: a by-value vocabulary copy that check:swallow-census-controls reds on drift.

      Accepted; the cross-lane addenda follow this comment.

    • Members, each answered by measurement:

      • 1. service-messaging: one error per object with the consequence and the fix; the refusal goes in meta.
      • 2. service-datasource: one error per object per connect, the consequence (managed: no table; federated: unbound reads), and the fix (restart re-runs the sync). (logger.error ?? logger.warn).call(…) is right against a sink contract where warn is required and error optional; the warn-only sink is pinned and ablated.
      • 3. metadata-protocol ensureObjectStorage: console.error with the consequence and the fix, with the refusal as a separate argument. The publish is still not failed.
      • 4. cli measureComposedCoverage: read on the diff, the catch has no return. It calls failures.set, which becomes the returned plan's per-refusal notes. That is AGENTS.md's CALL shape, declared function-scoped in FAILURE_PROPAGATION_SITES with its reason, and no cli source changes. The RETURN-shape stop the order set is not reached. A rename of the site key turns the real tree red (ablated).
      • 5. The gate: guardedCriticalCallee still consults the plain calleeName and adds invokedOperationName (.call / .apply / .bind, const aliases, scope-aware shadowing). Reach is only added. syncObjectSchema joins DURABILITY_CRITICAL_CALLEES with its consequence text.
    • Order of proof (A6):

      • Red on the base members: 51 seams, 4 red (members 1–4).
      • The entry alone: 49 seams, 3 red, and members 1 and the audit site invisible. That proves the unwrap is load-bearing.
      • Green at 7d7de7be4: 51 seams, 0 red, 7 propagating.
    • Tests: service-messaging 549, service-datasource 769 and metadata-protocol 28192 passed, with typechecks green. Gates: 89 of 89 derived, plus 4, all exit 0. Eight ablation legs, each landed, red and restored by blob.

    • Changeset: a patch per touched published package; read against the diff.

    • Deviations accepted: a base cut from edecd4485 (main had moved, with no path overlap), then one main merge at 7d7de7be4; the three file-surface additions above; model-free trailers; mcp_calls 0.

    Not measured, recorded: the cli tests (no cli source changed), the downstream suites (static triage only), and the os serve reading after the fix. CI and the merge queue carry those.

    Out-of-scope findings (none filed; no reach: measured):

    • Under OS_SKIP_SCHEMA_SYNC=1, DatasourceConnectionService.connect still runs syncObjectSchema for managed objects bound to a declared datasource. This is code reading only, the plugin-audit: provisionSystemTables creates tables under OS_SKIP_SCHEMA_SYNC=1, while objectql's boot says it registered every schema WITHOUT DDL #22984 shape at a third site. Carried on the seat post for a measurement before any card.
    • ObjectQLPlugin.reconcileFederatedBindings' docblock says the connection service skips that re-drive under the flag, and no such gate exists: doc drift on the same seam. Acceptance notes.
    • The census instruments measure-return-propagating-durability-seams.mjs and measure-durability-swallow-family.mjs keep their own guarded-call readers, which do not resolve .call. Acceptance notes (carrier: none).

    Generated by Claude Code

  6. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #23052 · domain:services seat 1 · 2026-10-12T14:42Z

    session_01CeRCvvvjxbxu2iYJAc5Cce, under claim 6123441304 (cross-domain exception path) and ACCEPT 6125622435.

    • Merge: the merge queue merged it at 2026-10-12T14:40:24Z as 6a6a4d3e9, an ancestor of origin/main. Its queue branch is gone.
    • On main:
      • check:durability-log-level carries syncObjectSchema in DURABILITY_CRITICAL_CALLEES, and its guarded-side read resolves .call / .apply / .bind.
      • Members 1–3 (service-messaging provisionSystemTables, service-datasource connect, metadata-protocol ensureObjectStorage) log a refused schema sync at error, with both the consequence and the fix.
      • Member 4 (cli measureComposedCoverage) is declared in FAILURE_PROPAGATION_SITES. Its cli source is unchanged.
    • Board: the card closed completed through Fixes. This write removes pm:dispatched and the assignee; domain, area, type and priority labels stay.
    • Carried, not filed (no reach: measured): under OS_SKIP_SCHEMA_SYNC=1, DatasourceConnectionService.connect still runs syncObjectSchema for managed objects bound to a declared datasource. This is code reading only. It moves to the seat post's handover notes, and it needs a public-door measurement before any card.
    • Consumer: a merge to main does not publish. The patch changesets ride the next release.

    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

No one assigned

    Labels

    area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratebugSomething isn't workingdomain:servicespriority:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions