Skip to content

Package install: POST /api/v1/packages installs a package with no protocol range unchecked and says so only in the server log (the install-local door now answers it) #30412

Closed as not planned

Description

@objectstack-fleet

domain:server#5 · os-bill · session_01Utp6CYVV6fcAQrNTDQRvPb

Filed from PR #30409 (card #30326, server half), the dev's acceptance note "not covered" and the seat ACCEPT 6655279987. ⛔ Not a claim. Triage sets the grade.

Business reason. The compatibility check exists so an app written for another platform version does not break a customer's deployment. After PR #30409 the marketplace install door (POST /api/v1/marketplace/install-local) tells the installer when the check was skipped. The package install door POST /api/v1/packages still does not: the administrator who installs a package with no engines.protocol there reads only success, and only the server log says it was not checked.

Reader who acts. domain:server, packages/runtime/src/domains/packages.ts (~L1750, assertProtocolCompat(manifest), its verdict dropped).

What to do

Answer the same optional notice the install-local door answers (protocolCheckSkipped: reason, packageId, protocolVersion, message, plus requiredRange / rangeSource for unparsed-range), read from the verdict assertProtocolCompat now returns (ProtocolCompatAdmitted, PR #30409), never a second reading of the range. Omit it for a checked range. One spelling with the install-local door: share the mapping (protocolCheckSkippedOf) rather than restating it. Pin a no-range install, an unparsed range and a satisfied-range control.

Serial after PR #30409 merges. If the answer is a published packages/spec schema, report it rather than deciding in the PR (#30326's own rule). Dedupe: packages install protocolCheckSkipped, POST /api/v1/packages engines.protocol: none. New.

Activity

  1. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: audit · p3 kept · domain:server · 2026-11-05T11:04Z

    ⛔ Not a claim.


    Generated by Claude Code

  2. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 5, 2026
  3. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#6 · os-bill · session_01Utp6CXCS98uhsFQYk64epE

    Claim: PM loop round 1
    Session: session_01Utp6CXCS98uhsFQYk64epE (seat) · dev = project card thread session_01Utp6CaqrWFKRoka7Myh79f
    Account: os-bill
    Branch: claude/project-thread-xmwfp7 (harness-pinned thread branch; platform exception to claude/issue-<n>-<slug>)
    Worktree: objectstack-issue-30412
    Domain: domain:server
    Seat: domain:server#6
    File surface: packages/runtime/src/domains/packages.ts and the shared protocolCheckSkipped mapping's home from PR #30409, with tests; content/docs/** only for sentences this PR makes false (not releases/**); one changeset. ⛔ packages/spec/**, ui/**, skills/**, docs/adr/**, .claude/**, AGENTS.md (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: default tier (opus; dispatch-gates --tier: no path-derived mandate), effort: high
    Clause-②: yes (widening) — the package install door gains the optional protocolCheckSkipped notice the install-local door already answers (precedent PR #30409); no new key. The seat reviews the declaration at TIER_DEFAULT
    Metadata-protocol: none
    Responsibility: packages/runtime/src/domains/packages.ts drops the assertProtocolCompat verdict, so a no-range install reads only success | the install-local door already answers the notice (PR #30409); this door does not | every administrator installing through POST /api/v1/packages
    Thread-read: 6656374504
    Premise: holds (seat read on c5800818: assertProtocolCompat(manifest) at L1750, verdict unused)
    Serial constraints cleared: PR #30409 (#30326 server half) merged; seat 5 yielded the card to this seat (coordinator relay 12:25Z)

    Order: project file server-seat6/dispatch-30412.md, delivered to the card thread.

  4. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#6 · os-bill · session_01Utp6CXCS98uhsFQYk64epE
    os-dev-report

    Card thread session_01Utp6CaqrWFKRoka7Myh79f · branch claude/project-thread-xmwfp7 (no commits) · Claim 6657995090 · no PR · read on origin/main 989ae42

    Status: needs_decision. The order's stop line fired: this door's answer is a published packages/spec contract, so the notice cannot be reused here without a spec change. Four readings, all on 989ae42:

    1. The shared mapping cannot be imported from runtime. protocolCheckSkippedOf and ProtocolCheckSkipped are module-private in packages/cloud-connection/src/marketplace-install-local-plugin.ts (L733-783). @objectstack/cloud-connection depends on @objectstack/runtime (its package.json), so runtime cannot call it. One spelling for both doors means moving it down to @objectstack/metadata-core protocol-handshake.ts, beside ProtocolCompatAdmitted.
    2. L1750 is the fallback arm only. packages/runtime/src/domains/packages.ts L1750 runs only when no protocol service resolves. The composed door calls protocolSvc.installPackage (packages/metadata-protocol/src/protocol-package-install.ts L274), which runs the handshake itself, drops the verdict and returns the spec type InstallPackageResponse (packages/spec/src/kernel/package-registry.zod.ts L404: package, message, dependencyResolution). Bringing the verdict out without a second reading of the range needs either a key on that spec schema or a non-spec side channel in metadata-protocol.
    3. The answer is bound to a published contract. PackageApiContracts.installPackage (packages/spec/src/api/package-api-assembled.zod.ts L285-290) names output: PackageInstallResponseSchema (packages/spec/src/api/package-api.zod.ts L527: data = package, dependencyResolution, namespaceConflicts, message). The wire answers data = the bare InstalledPackage row instead (pinned in packages/client/src/packages-write-envelope.test.ts L159-176: expect(installed).toEqual(registry.getPackage(MANIFEST.id))). The install-local answer has no spec schema, so triage 6656374504's "adds no new protocol key" holds there and not here.
    4. The door's measured UI caller would see a false alarm. The Console's New package dialog (ui/packages/app-shell/src/views/metadata-admin/PackageFormDialog.tsx L595-610) POSTs /api/v1/packages with { id, name, version, type, … } and no engines.protocol (the create form has no protocol field: grep -n "engines\|protocol" on that file is zero hits). Every package created in Studio is a no-range install, so the notice ("may not work on this platform version") would fire on every create. The Console's Import package file flow goes to /api/v1/marketplace/install-local (PackagesPage.tsx L1559), which already answers the notice.

    Governing text: dispatch order "If the shape cannot be reused without a packages/spec change, stop and report needs_decision"; card #30412 "If the answer is a published packages/spec schema, report it rather than deciding in the PR"; AGENTS.md Prime Directive #12 (contract-first).

    Options

    A. Spec first, then this card. A spec card declares an optional protocolCheckSkipped on the install answer (one shared schema the install-local answer can also name) and settles where it sits, given that the declared data.package and the wire's bare row already disagree. Then this card moves the mapping to metadata-core, has the metadata-protocol primitive return the verdict, and spreads it on both arms in runtime. It also needs a rule that a package created in Studio does not get the warning (reading 4). Cost: two cards, about M (spec) plus M (server, four packages: metadata-core, metadata-protocol, runtime, cloud-connection).

    B. Undeclared key on the bare row, no spec change. Put protocolCheckSkipped on data beside the row's keys, as install-local does. Cost: about M across the same four packages. It breaks the order's stop line and Prime Directive #12: the client's InstalledPackage type and the spec contract would both say nothing about a key the wire sends. The client pin above goes red for a no-range manifest and would have to change. The Studio false alarm stays.

    C. Do not answer it on this door; record why on #30412 (recommended). The door's real UI caller creates a package on this platform, where "no range" is the normal case, not a risk. Third-party package files reach the install-local door, which already tells the installer. The server warn line stays as it is. Reopen only if someone names a real third-party installer that uses POST /api/v1/packages (the SDK's client.packages.install is the only other caller found in this repo). Cost: zero code.

    Four axes

    • Real business need: the measured UI caller is New package (no range by construction), and imports already get the notice. A warns only if the Studio exemption is also built; B warns falsely on every create. C matches what is measured.
    • Long-term soundness: A is the only contract-first way to add the notice. B is the consumer-side drift Prime Directive Add comprehensive test suite for Zod schema validation #12 forbids. C adds nothing and leaves no debt.
    • Keeping AI from writing it wrong: B teaches that an undeclared answer key is fine, which is exactly the drift PackageApiContracts exists to stop. A keeps declared equal to served. C leaves nothing to get wrong.
    • No scope creep at startup stage: A adds a protocol key and two cards for a door whose measured caller does not need it. C is the narrow answer.

    Recommendation: C. If the maintainer wants the notice on this door anyway, A, with the Studio exemption ruled in the same answer. B is not recommended.

    {
      "issue": 30412,
      "status": "needs_decision",
      "branch": "claude/project-thread-xmwfp7",
      "pr": null,
      "session": "session_01Utp6CaqrWFKRoka7Myh79f",
      "premise_still_valid": false,
      "summary": "Stopped before code on the order's spec stop line. The POST /api/v1/packages answer is bound to the published PackageApiContracts.installPackage output PackageInstallResponseSchema, so the notice cannot be reused without a packages/spec change. Also measured: protocolCheckSkippedOf is module-private in cloud-connection, which depends on runtime; L1750 is only the no-protocol fallback, and the composed arm drops the verdict inside metadata-protocol installPackage, which returns spec InstallPackageResponse; the Console New package dialog POSTs this door with no engines.protocol, so the notice would fire on every Studio create. No worktree, no commits, no PR.",
      "tests": "none run: needs_decision before code (read-only measurement on origin/main 989ae42d)",
      "gates": "none run: no diff",
      "dogfood_battery": "n/a: no PR",
      "mcp_calls": "3 (all reads): issue_read get #30412, issue_read get_comments #30412 twice; no write tool",
      "api_writes": "1: this comment via post-stamped (POST /repos/objectstack-ai/objectstack/issues/30412/comments)",
      "open_questions": [
        {
          "question": "Should POST /api/v1/packages tell the installer that the protocol check was skipped, given that its answer is a published spec contract and its measured UI caller (Console New package) is a no-range install by construction?",
          "options": [
            "A: spec card first (declare an optional protocolCheckSkipped on the install answer and settle data.package vs the bare row), then this card across metadata-core, metadata-protocol, runtime and cloud-connection, with a Studio-create exemption",
            "B: undeclared key on the bare row, no spec change (breaks the stop line, Prime Directive #12 and the client pin; false alarm on every Studio create)",
            "C: do not answer it on this door; record why on #30412; imports already get the notice through install-local"
          ],
          "recommendation": "C, because the door's measured caller creates packages on this platform, imports already carry the notice, and A adds a protocol key and two cards with no measured need; A if the maintainer wants it anyway"
        }
      ],
      "security_findings": "none",
      "out_of_scope_findings": [
        "class: b · reach: POST /api/v1/packages answers data = the bare InstalledPackage row (pinned in packages/client/src/packages-write-envelope.test.ts L159-176) while the published PackageApiContracts.installPackage output PackageInstallResponseSchema declares data.package · Seam: spec:PackageApiContracts.installPackage.output → runtime:packages/runtime/src/domains/packages.ts deps.success(pkg) · dedupe words: PackageInstallResponseSchema bare row, installPackage contract data.package"
      ]
    }

    Report taken 2026-11-05T12:33Z.

  5. added
    needs-user-decisionBlocked on a maintainer decision — do not dispatch; decision cards only, never on a PR
    and removed
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    on Nov 5, 2026
  6. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#6 · os-bill · session_01Utp6CXCS98uhsFQYk64epE

    Release: session_01Utp6CXCS98uhsFQYk64epE · reason: seat clock-out · next: needs-user-decision
    Resume-from: none

    The server lane closes this seat on the maintainer's word 「当前任务处理完,合并后只留一个 server 席位」 (2026-11-05T16:41Z); the assignee is cleared and pm:dispatched removed 2026-11-05T18:51Z. The card waits on the maintainer's letter (decision analysis 6658092417; the seat recommended C). No code was written on the card branch. Since then #30464 landed through PR #30593: PackageInstallResponseSchema.data is the bare InstalledPackage row, and domain:spec#2 notes (mailbox 6666252458, contract review 6664812981) that InstalledPackageSchema is a non-strict z.object, so any member this card adds on data (for example protocolCheckSkipped) is stripped by PackageApiContracts.installPackage.output unless spec declares it: a route that adds a member needs a spec change too. On the ruling, domain:server#2 claims it afresh.

  7. removed
    needs-user-decisionBlocked on a maintainer decision — do not dispatch; decision cards only, never on a PR
    on Nov 5, 2026
  8. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-bill · session_01Utp6Cbvcmv8YWFhFJwzjKL

    Ruling confirmed: letter C · executed by domain:server#2 (seat 6's items came to this seat at its clock-out, Release 6666447042).

    Maintainer: 「同意」 to the recommendation, in the decision thread, 2026-11-05T21:30Z.

    What C means. The POST /api/v1/packages answer stays as it is; the server's warn line for an install with no protocol range stays. No code, no PR. The card closes as not planned.

    Why, each read on main (66a8822b; seat 6's report 6658092417, all four readings hold):

    1. The answer is a published contract, PackageInstallResponseSchema. Since PR fix(spec): PackageInstallResponseSchema declares the bare installed row the install door serves #30593 its data is the bare, non-strict row, so a key it does not declare reaches no consumer that reads the schema: adding a notice there (B) would ship text nobody is promised to see.
    2. The only Studio flow that writes through this door is "new package", which by construction carries no protocol range, so a notice would fire on every create and be wrong every time.
    3. A third-party package file goes through "import package" → install-local, which already answers the unchecked range (PR feat(cloud-connection,metadata-core): an install the protocol handshake admitted unchecked says so in its answer #30409).

    Reopen when someone names a real third-party installer that installs a foreign package through POST /api/v1/packages. Then take A: a domain:spec card first to declare an optional protocolCheckSkipped on the response, ruled together with a Studio "new package" exemption.

    #30464 landed in PR #30593 and is not this card.

    State change: needs-user-decision removed; closed as not planned 2026-11-05T21:31Z.

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

    domain:serverDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestpriority:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions