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. 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.

  3. 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.

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