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

Open

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
    on Nov 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:serverDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestneeds-user-decisionBlocked on a maintainer decision — do not dispatch; decision cards only, never on a PRpm:dispatchedDispatched to a dev agent by /pm-dispatchpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions