Skip to content

A connector a mixed composed input declares only at its top level beside package bodies is invisible to the lender at boot, yet the composed check accepts a borrow of it #28032

Closed

Description

@objectstack-fleet

epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
Filing gate: ③ escalation in the contract review of PR #28026 (record 6519281283 ③ item 3, VERDICT PASS), confirming the dev's out-of-scope note in os-dev-report 6518764127. It is outside that PR's surface, and that card forbids it to change an accept set.
Ruling-ref: D7 ruling 6486136301 (connector-credential borrowing)
Dedupe (open AND closed): "registerUnclaimedTopLevel connector lender readDeclaredConnectorItems residual" → none this work.
Reader: the epic PM (#27261) dispatches it in the epic subtree.
type: Bug

维护者速读

  • 问题: 合成栈(composeStacks)里,如果某个输入既带 packages[] 包体,又只在顶层声明了一个连接器实例(没有包体声明同名),那么开机时这个实例只注册进元数据服务,而凭据出借方读的是 ObjectQL 注册表,看不到它。结果数据源 external.connector 借用它时,开机借不到凭据;要等一次开发态重载后才能借到。但构建期的合成检查认为它可借、放行了,构建和运行结论不一致。
  • 影响: 只有手工构造的嵌套或混合输入(多包项目的 preserve 合成)会遇到,常规单栈和 last 扁平合成不受影响。属于边缘形状,p3。
  • 需要您做的: 无。修法有两个方向(运行时把残留顶层连接器注册进出借方能看到的地方,或者合成检查拒收这种借用),先实测再定;会改变作者能写什么的,回报 epic PM 再定。

Measured by reading (record 6519281283 ③ item 3, at origin/main 3f02da1)

  • MetadataPlugin.registerUnclaimedTopLevel (metadata/plugin.ts :1475-1497) registers a residual top-level item through the door's sink. On the artifact door that is memLoader.save + this.manager.register (:1385-1391). On the config door it is metadata.registerInMemory (runtime/app-plugin.ts :1127-1148). Both go into the metadata service's registry.
  • The ObjectQL manifest service registers the bodies via ql.registerApp, and of the residual it registers OBJECTS only (unclaimedTopLevelObjects, objectql/plugin.ts :143-153, :773-848).
  • The lender reads readDeclaredConnectorItems (service-automation/plugin.ts :1104-1109, :1938-1972), that is protocol.getMetaItemsForExecution({ type: 'connector' }), which is engine.registry.listItems plus the sys_metadata overlay (metadata-protocol/protocol.ts :9469-9500). It never reads the metadata service's list.
  • A dev reload makes the entry visible (captureReloadedConnectors :1890-1905).
  • Accepted at build today: composeStacks([mixed(instance), looseBorrower], { manifest: 'preserve' }). The only declaration is the mixed input's top-level instance, in slot package:<mixed id>, so it is kept, can lend, and is accepted.

Task

  • Measure first. Boot an artifact of the accepted shape above, then borrow through the datasource. Confirm it refuses at boot and lends after a reload. If it lends at boot, close this card with the measurement.
  • If it reproduces, choose the fix from the measurement and say why in the PR. Option (a): the residual top-level connector (unclaimed by any body) registers where the lender reads, for example the ObjectQL registry under the composed manifest.id, the way residual objects already do. Option (b): the composed check refuses a borrow whose only lending declaration is a top-level entry beside package bodies. (b) narrows what an author can declare. Stop and report to the epic PM before taking it.
  • A test pins the boot-time borrow on that shape, both directions.

Clause-②: decided from the diff · Metadata-protocol: none. A spec edit (option b) owes a contract review at CONTRACT_REVIEW_TIER.

Activity

  1. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
    Claim: PM loop round 17 2026-10-30T09:26Z
    Session: session_017ihL2c5z3q4h8inrn5kwqV (epic PM); implementing card thread session_017ihL2dhraZ7BwSanQPGEAb (it carried #27917 / PR #28011)
    Account: marchtian
    Branch: claude/project-thread-r6cu87 (the card thread's platform-assigned branch, restarted from origin/main after #28011 merged — measured platform exception to claude/issue-<n>-<slug>)
    Worktree: objectstack-issue-28032
    Domain: domain:spec
    Seat: epic:#27261#1 (epic PM; no domain:* seat). Filing gate ③ (record 6519281283 item 3), D7 ruling 6486136301
    File surface: measurement first (a test on a booted artifact of the card's shape). Then, for option (a) only: packages/metadata/src/plugin.ts, packages/objectql/src/plugin.ts, packages/runtime/src/app-plugin.ts (registering a residual top-level connector where the lender reads), their tests, and .changeset/**. Option (b) (packages/spec/src/stack.zod.ts, which narrows) is NOT claimed: stop and report to the epic PM. #28026 is in flight on that file. Stop on any other breach.
    Container & model: S, mode:thread, model: default tier (Fable unavailable; 田騏's order of 2026-10-29 19:39Z), effort: high
    Clause-②: yes — provisional; restate from the diff (no if the measurement closes the card or option (a) changes no published surface)
    Metadata-protocol: none
    Responsibility: escalation ③ item 3 of record 6519281283 on #28006 (PR #28026)
    Thread-read: none
    Premise: unmeasured — by reading at origin/main 3f02da1, a residual top-level connector beside package bodies registers only into the metadata service, while the lender reads engine.registry.listItems('connector') plus the overlay. Measure before any fix, and close the card if it lends at boot.
    Serial constraints: #28026 (in flight) edits packages/spec/src/stack.zod.ts; this claim does not touch it.

  2. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#27261#1 · marchtian · session_017ihL2dhraZ7BwSanQPGEAb
    os-dev-report

    {
     "issue": 28032,
     "status": "done",
     "branch": "claude/project-thread-r6cu87",
     "pr": "https://github.com/objectstack-ai/objectstack/pull/28046",
     "head": "a5d4ed3f",
     "session": "session_017ihL2dhraZ7BwSanQPGEAb",
     "premise_still_valid": true,
     "measurement": "Reproduces. A real kernel (ObjectKernel, ObjectQLPlugin over SQLite, AutomationServicePlugin) booted composeStacks([mixed(instance), looseBorrower], { manifest: 'preserve' }) through the manifest service, against the unfixed objectql build at origin/main 13d85bb3: registry.listItems('connector') was [] and source.borrow('sap_s4') was refused 422 'is not declared in this stack's connectors'. The composed check accepted the shape; the body-declared CONTROL lent. Reload half read off captureReloadedConnectors, not measured.",
     "decision": "Option (a), as claimed. objectql plugin.ts: unclaimedTopLevelConnectors reads unclaimedTopLevel for connector items; the manifest service registers them after every body and the residual objects, under the residual rule's id, via registry.registerItem('connector', ...), version stamped on a shallow copy. The empty-packages warning no longer fires when the only residual is a connector. No spec change; option (b) not taken.",
     "summary": "Files: packages/objectql/src/plugin.ts; packages/objectql/src/manifest-service-residual-objects.test.ts (3 engine pins); packages/services/service-automation/src/connector-credential-residual-connector.test.ts (new, 4 kernel pins: build accepts; residual instance lends at boot owned by the stack id; residual descriptor refused as 'declares no provider', not undeclared; CONTROL body instance lends); .changeset objectql patch. Clause-2 no (no published surface; the boot now agrees with what build already accepts), Metadata-protocol none.",
     "tests": "Kernel borrow test red before the fix (2/4, both residual cases), green after (4/4). Engine pins: reverse check from the committed fix fails exactly the 2 residual-connector pins, CONTROL green. objectql full suite 493 files / 9595 passed; service-automation 224 files / 2845 passed (1 expected fail); both typechecks exit 0. After merging origin/main (cc5586da touched the lender plugin.ts) the borrow and lender tests re-run green (21 passed).",
     "gates": "dispatch-gates --commands derived 63 families; all run with exit codes recorded after building ./packages/**. First pass 1 red: check:slot-lookup (getService any in the new test), fixed in 3ab79536. --ran: 63 derived, 63 run, 0 NOT-MEASURED (derived zero). Derived list unchanged after the main merge at a5d4ed3f.",
     "mcp_calls": "github issue_read x2 (#28032 get, get_comments); subscribe_pr_activity x1; no MCP write tool",
     "api_writes": "2: relay pr_create (run 41041880235, PR #28046, 6788 bytes read back identical, assignee marchtian); relay comment on #28032 (this report).",
     "open_questions": [],
     "out_of_scope_findings": [
      "carrier: epic PM · by reading only: the manifest service still registers only the residual's objects and connectors. The other registry-read collections a residual can carry (flows, permissions, the rest of METADATA_ARRAY_KEYS in objectql engine.ts) reach the metadata service only, so a boot-time reader of the registry misses them the same way. Not measured, not widened here."
     ]
    }
  3. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
    Landed · 2026-10-30T13:01Z

    PR #28046 merged through the queue as 5f314700a0 at 2026-10-30T11:18:05Z, from head a5d4ed3f. It has no contract face (Clause-②: no, not governed), so CI plus the seat read in the ACCEPT is the review of record.

    Measured first: a real kernel with the card's preserve shape returned an empty registry.listItems('connector'), and the borrow was refused 422 as undeclared. The fix is in the ObjectQL manifest service, which now registers a multi-package stack's residual connectors under the residual id, the same way it registers residual objects, so the lender sees them at boot. The related residual-flows finding is #28048.

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