Skip to content

release: v4.0.0 published an EMPTY app to the staging marketplace (20 KB, no objects) — publish-marketplace posts the composed packages[] artifact that cloud's ingest reads flat; flatten at publish + guard, ship 4.0.1 (maintainer ruling 2026-10-10) #2053

Closed

Description

@hotlong

What happened (measured, 2026-10-10)

v4.0.0 was tagged at 6162c43f and pushed at 14:15Z. Publish to marketplace (staging) (run 38058869162) succeeded with ✓ published version 4.0.0. But the version cloud stored is an empty app:

sys_package_version.manifest_json (staging control plane) 3.1.0 4.0.0
size 1,251,460 B 20,471 B
objects / views / pages / dashboards / reports 18 / 14 / 8 / 5 / 10 absent
flows / actions / hooks 29 / 31 / 39 absent
permissions / sharingRules / positions / data / datasets / skills 7 / 10 / 12 / 19 / 9 / 6 absent
what is left — id, name, version, type, category, description, docs, i18n, translations

Cause. Since objectstack.config.ts became composeStacks([serviceStack, appStack], { manifest: 'preserve' }), objectstack build writes a composed artifact. Built locally at 6162c43f, dist/objectstack.json is 2.45 MB with top level { manifest, packages: [ {manifest:{…25 keys}}, … ] (2), i18n, docs }: every collection now lives under packages[]. scripts/publish-marketplace.mjs posts that bundle as is to POST /api/v1/cloud/packages/:id/versions. Cloud's ingest and installer at its current pins read only the flat shape that 3.x published, so packages[] was dropped silently. Every gate is green because the POST answered 201.

Containment already done:

  • Staging's 4.0.0 listing is withdrawn (POST /cloud/package-versions/pkgv_7c3f2f07…/withdraw → listing_status: draft, 14:19Z). The staging catalog's latest is back to 3.1.0.
  • Production was never published; publish-production.yml is manual. ⛔ Do not dispatch it for 4.0.0.
  • The GitHub Release v4.0.0 exists. That is harmless.

Ruling

The maintainer, in chat on 2026-10-10 (session 64cb68a6-01c7-4dc6-acc6-ccb459c90ef2), chose 「hotcrm 发布时压平」: flatten at publish time in this repo and ship 4.0.1. Cloud learning the composed shape is a separate, later card, not this one.

Do

  1. Flatten in scripts/publish-marketplace.mjs. When bundle.packages is an array, flatten it into the flat shape 3.1.0 published before POSTing:
    • Top level: the app package's manifest (id: app.objectstack.hotcrm) gives id/name/version/type/category/description.
    • Collections: every collection array (objects, views, pages, apps, dashboards, reports, flows, actions, hooks, permissions, sharingRules, positions, data, datasets, mappings, skills, requires, …) is the concatenation across all packages, de-duplicated by name or id where one would collide. Fail loudly on a real name collision.
    • Kept: i18n, docs, translations.
  2. A guard that cannot be green while empty. The script die()s before any HTTP call if the flattened bundle has 0 objects, or carries fewer objects than the build reports.
    • Add a unit test over a fixture of the composed shape: the flat output must hold the union of both packages' collections.
    • Add a negative control: the composed shape without flattening trips the guard.
  3. Prove it on the real artifact. On dist/objectstack.json from pnpm build at the release commit, the flattened bundle has 18 objects and the 3.1.0-like key set.
    • Record a DRY_RUN=1 run's key/count summary in the PR.
    • Diff the key set against what 3.1.0 stored: no key may be missing unless 4.0 removed it on purpose.
  4. Release 4.0.1 through the normal flow:
    • changeset patch → changeset:version, plus the five doc surfaces test/docs-declared-versions.test.ts pins and the manifest version in src/sales/index.ts;
    • merge queue → tag v4.0.1;
    • staging publishes automatically.

Acceptance (taken by the maintainer's session after the tag; noted here so the dev leaves room for it)

  • The stored version is complete: staging's sys_package_version for 4.0.1 is about 1.2 MB+, with 18 objects; listing approved; catalog latest 4.0.1.
  • Fresh install in the browser: a fresh install on staging renders the app (objects, the executive dashboard, sample data).
  • Upgrade in the browser: a 3.1.0 → 4.0.1 upgrade on a staging environment that already has HotCRM with sample data. This is the major's real test. The five flat mailing_* fields become one mailing_address field, and the hosted installer's schema sync is additive, so what an existing contact's address shows after the upgrade is measured, not assumed.

Related: hotcrm#2051 (the inlined logo), which can ride the same 4.0.1 if it is ready.

Activity

  1. added
    pm:queueReady for the PM dispatch loop
    priority:p1High: required for production / M2
    on Oct 10, 2026
  2. objectstack-fleet commented on Oct 10, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round R78
    Session: session_012zh91QzFgePbkmuHnugLN3
    Account: os-zhuang (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-2053-publish-flatten
    Worktree: hotcrm-issue-2053
    Domain: repo:hotcrm (single-lane repo, no domain:* taxonomy)
    Seat: repo:hotcrm#1
    File surface: scripts/publish-marketplace.mjs (flatten the composed packages[] artifact, plus the empty-bundle guard before any HTTP call), a new test of it under test/ with a composed-shape fixture and the negative control, and one .changeset/2053-*.md ('hotcrm': patch). The 4.0.1 version cut is a second PR after this one lands (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: default (governedPathsIn over the surface: none; expected well under 3,000 changed lines ⇒ Tier S)
    Clause-②: no
    Responsibility: scripts/publish-marketplace.mjs posts the composed packages[] artifact as is | none: cloud's ingest reads only the flat shape 3.x published | every hosted install of 4.0.0 (staging's listing withdrawn 14:19Z; production never published)
    Thread-read: none
    Serial constraints: #2053 is p1 and goes first, on the maintainer's 「2053 插队」 in the seat's chat. #2051 runs beside it on a disjoint surface. The 4.0.1 version PR waits for this PR to land, and for #2051's if it is ready, so 4.0.1 carries both.


    Generated by Claude Code

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

    @objectstack-fleet
    Contributor

    os-dev-report

    {
      "issue": 2053,
      "status": "done",
      "branch": "claude/issue-2053-publish-flatten",
      "pr": "https://github.com/objectstack-ai/hotcrm/pull/2054",
      "session": "session_012zh91QzFgePbkmuHnugLN3 (subagent: the parent seat session, per the Claude-Session line)",
      "premise_still_valid": true,
      "summary": "Draft PR #2054 (head 6c78cfa1, base main, Part of #2053, Clause-② no). A new module, scripts/lib/marketplace-bundle.mjs, flattens the composed artifact and guards it: flattenArtifact, assertBundleCarriesEveryObject, countArtifactObjects, describeBundle and BundleError. scripts/publish-marketplace.mjs imports it and runs both steps straight after reading dist/objectstack.json, before any HTTP call, DRY_RUN included. Its only CLI change is one bundle summary line plus the refusal.\nThe flatten reuses platform pieces rather than writing its own merge rules: package order from resolveArtifactPackageOrder (@objectstack/core, app first); collection keys from COMPOSE_KEY_DISPOSITIONS (@objectstack/spec), concatenated with exact duplicates removed; the module navigationContributions folded into apps[].navigation by SchemaRegistry.applyNavContributions (@objectstack/objectql). Anything a one-package bundle cannot carry is refused by name (BundleError) instead of dropped.\nMeasured on the real artifact at 6162c43f: 18 objects, the same 9 manifest keys as 3.1.0, every key 3.1.0 carried present, one new key (emailTemplates 76, authored by #1949), 1,376,671 B as compact JSON. The before/after numbers and the 3.1.0 key diff are in the PR body.\nFiles changed: scripts/publish-marketplace.mjs (modified); scripts/lib/marketplace-bundle.mjs, test/marketplace-bundle.test.ts, test/fixtures/composed-artifact.json and .changeset/2053-publish-flat-bundle.md (new). The changeset is a hotcrm patch. No changeset:version run, no 4.0.1 cut, src/sales/apps/crm.app.ts untouched.\nDeviations (mechanism assumptions corrected by measurement):\n(1) The 3.1.0 bundle (release asset) posted a manifest object at the top level, not id/name/version/type/category/description. Those are cloud's stored projection, and category comes from objectstack.manifest.json through the upsert. The flat bundle therefore keeps the app manifest object, as 3.1.0 did.\n(2) De-duplicating by name or id alone would die on the real artifact. log_call, log_meeting and schedule_meeting each exist on 5 objects (4 inside the app package alone), and email templates repeat a name once per locale. The identity is scoped the way the platform scopes it: objectName+name for actions (collectComposedActionKeyCollisions), name+locale for email templates (ITEM_KEY_DISCRIMINATORS). Items with no name or id are only de-duplicated when exactly equal, because data legitimately carries two datasets each for crm_event_attendee and crm_campaign_member. Only requires changes: 12 down to 8.\n(3) The module navigationContributions is a manifest key that a flat bundle cannot carry. Dropping it would hide Cases, Knowledge, Service Overview, My Cases and SLA Performance from a hosted install. This flatten gap is fixed by the platform fold above.\n(4) The concatenation order is the platform registration order (app first), not the artifact packages[] order (service first), so seed rows replay as a packaged boot replays them.\n(5) Beyond the fixture cases, two cases run on the composition HotCRM actually builds (objectstack.config.ts), so a future package that cannot be flattened fails on its PR rather than at a tag.\nOther notes: (a) AGENTS.md Scope rule 3 (no platform tooling here) is in tension with this publish-side flatten; I executed the maintainer ruling 「hotcrm 发布时压平」 and did not re-decide it. (b) The harness attribution reminder named a model (Co-Authored-By Claude Opus 5.5) and a different PR footer form. Both are reported, not imitated: commit trailers are model-free and the PR ends with the session-URL footer. (c) The PR carries the ci/cd label from labeler.yml (path-based), not from me. (d) CI was in_progress at report time and I did not wait for it. (e) The worktree was removed (node_modules first, no --force).",
      "tests": "Gate: OS_VERIFY_LOCK_SLOT=hotcrm-2053 bash /home/user/objectstack/scripts/pm/os-verify-lock.sh -c 'pnpm verify' at HEAD 6c78cfa1 (git rev-parse --short HEAD). Exit 0, \"os-verify-lock: VERDICT command-exit 0 · held the lock 474s\".\nSteps inside verify: validate \"✓ Validation passed\"; typecheck tsc --noEmit clean; lint \"18 suggestion(s)\", no errors; lint:i18n-gate \"✓ i18n lint gate: 0 i18n/missing-* issues\"; hygiene \"✓ source hygiene clean\"; hygiene:tokens \"✓ source token ratchet clean\"; build \"✓ Build complete\", Artifact 2340.9 KB; test \"Test Files 176 passed (176) · Tests 3633 passed | 1 skipped (3634)\".\nNew file alone: pnpm exec vitest run --maxWorkers=2 test/marketplace-bundle.test.ts gave \"Tests 11 passed (11)\", VERDICT command-exit 0.\nBefore, at 6162c43f: DRY_RUN=1 node scripts/publish-marketplace.mjs exited 0 and printed \"✓ published version 4.0.0\". The bundle top-level keys were manifest, packages, i18n, docs; bundle.objects was absent; the objects lived under packages[] (service 3, app 15).\nAfter: DRY_RUN=1 pnpm publish:marketplace exited 0 and printed \"bundle (flattened from 2 packages): manifest {9 keys} · i18n {3 keys} · docs 4 · permissions 7 · objects 18 · data 20 · translations 1 · apps 1 · views 14 · pages 8 · dashboards 5 · reports 10 · datasets 9 · actions 31 · flows 32 · emailTemplates 76 · positions 12 · sharingRules 10 · skills 6 · hooks 44 · mappings 3 · requires 8\".\nAblation, CLI negative control (ablation-replace, from committed 6c78cfa1, anchor x1 to x0, blob ebe323ad to a753188e): replacing the flatten with bundle = artifact made the dry run print \"✗ refusing to publish: the bundle to post carries 0 objects (top-level keys: manifest, packages, i18n, docs); the build carries 18.\" with INNER-EXIT=1 and no [dry-run] POST line. Restored blob ebe323ad equals HEAD; git diff HEAD empty.\nAblation, navigation fold (under the lock): removing the applyNavContributions line gave \"Tests 2 failed | 9 passed (11)\": the fixture fold case and the real-composition contributed-items case. VERDICT command-exit 1. Restored blob ecb89816 equals HEAD; git diff HEAD empty.\nChangeset: pnpm changeset status --since=origin/main exited 0 with \"hotcrm patch\".\nControl-byte self-scan (grep -naP) over the 5 changed files: no matches.\nShallow check: git rev-parse --is-shallow-repository returned false.\nDeclared to CI, not run locally: test:coverage, e2e, codeql, link-check, docs-app.\nNOT MEASURED: the cloud-side acceptance (stored row size and object count, fresh install, 3.1.0 to 4.0.1 upgrade). Reason: it runs after the 4.0.1 tag, in the maintainer session.",
      "mcp_calls": "0",
      "api_writes": "3 relay writes, each one repository_dispatch to objectstack-ai/objectstack executed as objectstack-fleet[bot]: (1) pr_create, POST /repos/objectstack-ai/hotcrm/pulls (draft #2054; read-back 11473 B sent, 11473 B stored, identical); (2) label-write --assign os-zhuang, POST /repos/objectstack-ai/hotcrm/issues/2054/assignees (read-back matches); (3) post-stamped, POST /repos/objectstack-ai/hotcrm/issues/2053/comments (this report). Not counted as REST: git push -u origin claude/issue-2053-publish-flatten (twice: the empty probe, then 6c78cfa1). REST reads: issue 2053 and its comments, PR 2054.",
      "open_questions": [],
      "out_of_scope_findings": [
        "class: a · reach: public door POST /api/v1/cloud/packages/:id/versions answered 201 to the composed bundle and stored an app with no objects (staging run 38058869162, stored row 20,471 B; measured on the card) · evidence: the cloud ingest silently drops a top-level packages[] instead of refusing it · dedupe words: marketplace ingest, composed packages[], empty app, 201, sys_package_version manifest_json · carrier: the later cloud card the maintainer named in the #2053 ruling (cloud learns the composed shape) · noted, not filed.",
        "observation · reach: none at a public door. During pnpm test the log shows 1659 crm_campaign_member and 158 crm_campaign SeedLoader ERROR lines (in_progress campaigns refused for missing start_date/end_date, written as cel daysAgo/daysFromNow), but a real objectstack dev boot on port 4938 on a fresh DB logged 0 SeedLoader errors, so this is harness-only · carrier: none · noted, not filed.",
        "acceptance note (not a finding): emailTemplates is the one key the 3.x shape never carried; whether cloud stores it is read off sys_package_version.manifest_json in the post-tag acceptance · carrier: the maintainer acceptance session.",
        "acceptance note (not a finding): docs/RELEASE_STRATEGY.md §Marketplace Publish does not yet describe the new summary line or the refusal; nothing in it is false · carrier: none · noted in the PR Acceptance notes only."
      ]
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 10, 2026

    @objectstack-fleet
    Contributor

    Verdict: os-dev report on this card, PR #2054. ACCEPT, Tier S (not governed; +506 / −4 over 5 files, per check-governed-merges.mjs): the seat lands it through the merge queue once every check on head 6c78cfa1 is green. repo:hotcrm seat, session_012zh91QzFgePbkmuHnugLN3, 2026-10-10T15:05Z.

    Re-measured by the seat in its own scratch clone of 6c78cfa1, not taken from the report:

    • PR shape:
      • first line Part of #2053, then Clause-②: no;
      • 5 files: scripts/publish-marketplace.mjs (+33 / −4: the import, the flatten + guard before any HTTP call, one summary line), scripts/lib/marketplace-bundle.mjs (new), test/marketplace-bundle.test.ts (new), test/fixtures/composed-artifact.json (new), and a 'hotcrm': patch changeset.
      • No version cut. src/sales/apps/crm.app.ts is untouched.
      • The three platform imports (@objectstack/spec, @objectstack/core, @objectstack/objectql) are direct dependencies in package.json, so publish-staging.yml's pnpm install --frozen-lockfile resolves them.
    • The dry run on the real artifact: pnpm build (Data: 18 Objects 358 Fields), then DRY_RUN=1 node scripts/publish-marketplace.mjs prints bundle (flattened from 2 packages): … objects 18 … requires 8, the same line as the PR body.
    • Against 3.1.0: the seat downloaded the v3.1.0 release asset and read its dist/objectstack.json as data.
      • Every top-level key 3.1.0 carried is in the flat bundle. The one new key is emailTemplates (76).
      • manifest has the identical 9 keys.
      • The 18 object names are the identical set, and equal the union of both packages' objects.
      • The flat app's navigation carries crm_case, crm_knowledge, My Cases and SLA, as 3.1.0's did. The module's contributions are folded in, not dropped.
    • The guard: assertBundleCarriesEveryObject(artifact, artifact) throws BundleError … carries 0 objects … the build carries 18. The flat bundle minus one object throws … carries 17 objects, but the build carries 18.
    • pnpm exec vitest run test/marketplace-bundle.test.ts: 11 passed.

    The design, accepted: the flatten reuses the platform's package order, collection list and navigation fold, and its only local rule (item identity scoped by objectName / locale) exists to refuse. The report's deviations from the card are measured and right:

    • the top level keeps a manifest object, as 3.1.0 posted;
    • identity is scoped, because log_call is legitimately on five objects;
    • navigationContributions is folded.

    AGENTS.md Scope rule 3 tension: executed under the maintainer's ruling 「hotcrm 发布时压平」, so not re-decided.

    Not measured, left to the post-tag acceptance on staging: the stored row and its object count, a fresh install, the 3.1.0 → 4.0.1 upgrade, and whether cloud keeps emailTemplates. Cloud's silent acceptance of a packages[] bundle is the later cloud card the ruling names; it is not filed here.

    Next: after this lands, the 4.0.1 version PR carries this and #2051 if #2051 has landed by then. The maintainer pushes v4.0.1.


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 10, 2026

    @objectstack-fleet
    Contributor

    Landed: PR #2054 MERGED as d7decd43 on main (squash, single parent 6162c43f). The seat landed it through the merge queue after 9 of 9 checks went green on head 6c78cfa1: ready and queued at 15:13:26Z, merged at 15:13:44Z. repo:hotcrm seat, session_012zh91QzFgePbkmuHnugLN3, 2026-10-10T15:14Z.

    Two readings:

    • the PR object reads merged;
    • git diff 6162c43f d7decd43 is byte-identical to the reviewed head's diff: 5 files, +506 / −4.

    What now holds on main: scripts/publish-marketplace.mjs flattens the composed artifact before posting. Before any HTTP call it refuses a bundle that carries fewer objects than the build: 0, or any partial count. It is not released yet: 4.0.0's tag predates it.

    This card stays open and pm:dispatched:


    Generated by Claude Code

  7. objectstack-fleet commented on Oct 10, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round R79 (#2053 round 2: the 4.0.1 version PR)
    Session: session_012zh91QzFgePbkmuHnugLN3
    Account: os-zhuang (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-2053-release-4-0-1
    Worktree: hotcrm-issue-2053r2
    Domain: repo:hotcrm (single-lane repo, no domain:* taxonomy)
    Seat: repo:hotcrm#1
    File surface: package.json and CHANGELOG.md (written by changeset version); .changeset/* (the two patch changesets consumed; a first-paragraph text edit to 2053-publish-flat-bundle.md only); src/sales/index.ts; src/service/index.ts; the version-bound docs (README.md, docs/ARCHITECTURE.md, docs/RELEASE_STRATEGY.md, docs/STATUS.md, content/docs/whats-new*.mdx) (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: default (governedPathsIn over the surface: none; a two-patch cut, expected far under 3,000 changed lines ⇒ Tier S)
    Clause-②: no
    Responsibility: 4.0.0 is withdrawn from staging and was never on production; main carries the fix (PR #2054) and the logo (PR #2055) unreleased | none | every hosted install waiting on the first 4.x that installs whole
    Thread-read: none
    Serial constraints cleared: round 1 landed as d7decd43 (PR #2054), and #2051 as 45478585 (PR #2055). Nothing else is in flight; this PR stays last until it lands.


    Generated by Claude Code

  8. objectstack-fleet commented on Oct 10, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    {
      "issue": 2053,
      "status": "done",
      "branch": "claude/issue-2053-release-4-0-1",
      "pr": "https://github.com/objectstack-ai/hotcrm/pull/2056",
      "session": "session_012zh91QzFgePbkmuHnugLN3 (subagent: the parent seat session, per the Claude-Session line)",
      "premise_still_valid": true,
      "summary": "Draft PR #2056 (head 979d0ef5, base main, first lines \"Part of #2053\" then \"Clause-②: no\"; +47 / −35 over 13 files, no governed path, Tier S) cuts 4.0.1 through docs/RELEASE_STRATEGY.md § Version Sources and § Release Checklist. No code change.\nMeasured first, on 45478585: changeset status --verbose read \"hotcrm -> 4.0.1\" from the two patch changesets 2051-inline-logo.md and 2053-publish-flat-bundle.md, as the dispatch expected.\nStep 1 (commit 0c95ca46): the first paragraph of .changeset/2053-publish-flat-bundle.md now says that 4.0.1 is the first 4.x release that installs whole, that 4.0.0's marketplace publish carried an empty app and was withdrawn, and that the upgrade from 3.x still carries 4.0.0's one-time step: convert the contact mailing addresses with scripts/backfill-contact-mailing-address.ts before os migrate apply --allow-destructive, with the 4.0.0 entry of CHANGELOG.md holding the commands. The old text is now the second paragraph. Text only: the bump stays patch, and status read 4.0.1 again after the edit.\nStep 2 (commit f0fd6695): pnpm changeset:version (exit 0) consumed both changesets. It wrote package.json 4.0.0 -> 4.0.1 and the ## 4.0.1 section of CHANGELOG.md (+16, 0 removed). Each entry is stamped with the main commit that added its changeset: 4547858 (#2055) and d7decd4 (#2054). Both are ancestors of origin/main (merge-base --is-ancestor exit 0, on a full clone).\nStep 3 (commit 325437c1): src/sales/index.ts version, and src/service/index.ts version plus dependencies[app.objectstack.hotcrm] ^4.0.1. Also the README badge, the docs/ARCHITECTURE.md and docs/RELEASE_STRATEGY.md version rows, and docs/STATUS.md's version sentence and validate transcript. The transcript was re-read from a real pnpm validate: HotCRM v4.0.1, every figure unchanged.\nStep 4 (commit 979d0ef5): in all three whats-new locales, the latest-release heading and opening sentence read 4.0.1, and the \"versions between v1.0 and\" note follows them, as #2052 moved it. One short paragraph above \"What 4.0.0 adds\" says that 4.0.1 is the first 4.x that installs from the marketplace (4.0.0's publish there was empty and withdrawn), so a 3.x deployment upgrades straight to it, and that the sidebar logo and favicon now show on hosted installs. \"What 4.0.0 adds\" and its pre-upgrade checks are kept.\nListing notes (step 1's placement question): changesets orders the one-line 2051 entry first. In the 893-char notes, the 2053 entry starts at char 109 and its conversion sentence at char 297; the backfill script name is at 393 and os migrate apply --allow-destructive at 446. All of it is far inside the 8,000-char cap.\nFiles changed: package.json, CHANGELOG.md, .changeset/2051-inline-logo.md and .changeset/2053-publish-flat-bundle.md (both consumed; the second edited first), src/sales/index.ts, src/service/index.ts, README.md, docs/ARCHITECTURE.md, docs/RELEASE_STRATEGY.md, docs/STATUS.md, content/docs/whats-new.mdx, .zh-Hans.mdx and .zh-Hant.mdx. That is exactly the predicted surface.\nDeviations and notes:\n(a) The harness attribution reminder named a model (Co-Authored-By: Claude Opus 5.5) and a different PR footer. Both are reported, not imitated: commit trailers are model-free, and the PR ends with the session-URL footer.\n(b) The first commit was amended once, locally and before any push, to drop a card-reference line from its message (commit messages carry no card reference). There was no force-push.\n(c) pnpm verify ran under the lock via nohup, followed by one foreground tail --pid block until it exited (497 s held). This kept it clear of the roughly 10-minute foreground cap; it was a blocking wait, not a background wake-up.\n(d) Labels documentation and dependencies come from labeler.yml (path-based), not from me. Check Changeset concluded failure, as expected: it waits on the skip-changeset label the seat applies. I added no label.\n(e) CI was in_progress at report time (Build and Test, Typecheck and Build, Playwright, CodeQL); Quality Checks and link-check were green. I did not wait for it.\n(f) The worktree was removed: node_modules first, then git worktree remove without --force, exit 0.",
      "tests": "Gate: OS_VERIFY_LOCK_SLOT=hotcrm-2053r2 bash /home/user/objectstack/scripts/pm/os-verify-lock.sh -c 'pnpm verify' at HEAD 979d0ef5 (git rev-parse --short HEAD; equals the PR head and the pushed branch tip; origin/main still 45478585, so no merge needed). Exit 0: \"os-verify-lock: VERDICT command-exit 0 · held the lock 497s (8m17s) · waited 0s\".\nSteps inside verify: validate \"✓ Validation passed\", \"HotCRM v4.0.1\", \"Data: 18 Objects  358 Fields\"; typecheck tsc --noEmit clean; lint \"18 suggestion(s)\", no errors; lint:i18n-gate \"✓ i18n lint gate: 0 `i18n/missing-*` issues\"; hygiene \"✓ source hygiene clean\" (includes \"✓ no raw control bytes in first-party files\"); hygiene:tokens \"✓ source token ratchet clean\"; build \"✓ Build complete\", \"Artifact: dist/objectstack.json (2343.6 KB)\"; test \"Test Files  177 passed (177)\", \"Tests  3635 passed | 1 skipped (3636)\".\nEarlier, before the STATUS.md edit: pnpm validate under the lock gave VERDICT command-exit 0 and the transcript copied into docs/STATUS.md. Then, at 979d0ef5: vitest run --maxWorkers=2 over docs-declared-versions, docs-metadata-counts, docs-object-term-consistency and docs-retired-personas gave \"Test Files  4 passed (4)\", \"Tests  61 passed (61)\", VERDICT command-exit 0.\nBuild + dry run at 979d0ef5: pnpm build under the lock gave VERDICT command-exit 0, \"Data: 18 Objects  358 Fields\", \"Artifact: dist/objectstack.json (2343.6 KB)\". Then DRY_RUN=1 pnpm publish:marketplace exited 0 and printed:\n\"── HotCRM (app.objectstack.hotcrm) @ 4.0.1\"\n\"bundle (flattened from 2 packages): manifest {9 keys} · i18n {3 keys} · docs 4 · permissions 7 · objects 18 · data 20 · translations 1 · apps 1 · views 14 · pages 8 · dashboards 5 · reports 10 · datasets 9 · actions 31 · flows 32 · emailTemplates 76 · positions 12 · sharingRules 10 · skills 6 · hooks 44 · mappings 3 · requires 8\" (identical to round 1)\n\"release notes: 893 chars\" and \"✓ published version 4.0.1\".\nNotes offsets: the script's own releaseNotesFor text, evaluated against the cut CHANGELOG.md, returned length=893, which equals the dry run. Offsets: the 2051 entry at 40; \"**4.0.1 is the first 4.x release that installs whole.**\" at 109; \"That upgrade still carries 4.0.0's one-time step\" at 297; scripts/backfill-contact-mailing-address.ts at 393; os migrate apply --allow-destructive at 446; \"The 4.0.0 entry of `CHANGELOG.md` holds the commands.\" at 487.\nVersion reading: pnpm exec changeset status --verbose at 45478585 and after the text edit, both \"hotcrm -> 4.0.1\" from the two patch changesets. pnpm changeset:version exit 0.\nControl-byte self-scan (grep -naP) over the changed files and the PR body: no matches (exit 1). git rev-parse --is-shallow-repository: false.\nPR body read-back: the relay reported \"6038 byte(s) sent, 6038 stored — identical\". A REST re-read diffed equal apart from jq's trailing newline.\nDeclared to CI, not run locally: test:coverage, e2e (Playwright), codeql, link-check, docs-app.\nNOT MEASURED: the cloud-side acceptance (the stored row and its object count, fresh install, the 3.1.0 -> 4.0.1 upgrade, the listing notes as cloud renders them). Reason: it runs after the v4.0.1 tag, in the maintainer's session.",
      "mcp_calls": "0",
      "api_writes": "3 relay writes, each one repository_dispatch to objectstack-ai/objectstack executed as objectstack-fleet[bot]:\n(1) pr_create, POST /repos/objectstack-ai/hotcrm/pulls (draft #2056; run 38064313363 success; read-back 6038 B sent, 6038 B stored, identical);\n(2) label-write --assign os-zhuang, POST /repos/objectstack-ai/hotcrm/issues/2056/assignees (run 38064351471 success; the read-back matches: assignees os-zhuang, labels unchanged);\n(3) post-stamped, POST /repos/objectstack-ai/hotcrm/issues/2053/comments (this report).\nNot counted as REST writes: git push to origin claude/issue-2053-release-4-0-1, three times (the empty probe, then f0fd6695, then 979d0ef5; none forced).\nREST reads: issue 2053 and its comments, PR 2056 (twice), and the check runs of the PR head (the first try used a mistyped short sha and got 422; the second read 9 runs).",
      "open_questions": [],
      "out_of_scope_findings": [
        "observation (dormant, not filed) · reach: none. No release has a Major Changes body containing a capital Z: the 4.0.0 and 3.0.0 major bodies have none and come out whole (notes 2,531 and 3,938 chars), and 4.0.1 has no major. Evidence: in scripts/publish-marketplace.mjs releaseNotesFor, the major-section lookahead ends in a backslash-Z escape. In a JavaScript regex that is a literal capital Z, not end of input, so a future major body containing one would be cut at that letter. Dedupe words: releaseNotesFor, Major Changes, lookahead, backslash-Z, release notes truncated, publish-marketplace. Carrier: none (承接者:无). Noted in the PR Acceptance notes only.",
        "observation (not a finding) · the pnpm test log again shows harness-only SeedLoader errors (crm_campaign_member, \"Seed loading completed with 47 dropped record(s)\"). Round 1 measured these as absent on a real dev boot. Not re-measured · carrier: none · noted, not filed."
      ]
    }

    Generated by Claude Code

  9. objectstack-fleet commented on Oct 10, 2026

    @objectstack-fleet
    Contributor

    Verdict: os-dev report 6099195039 (round 2), PR #2056. ACCEPT, Tier S (not governed; +47 / −35 over 13 files, per check-governed-merges.mjs): the seat lands it through the merge queue once every check on head 979d0ef5 is green. repo:hotcrm seat, session_012zh91QzFgePbkmuHnugLN3, 2026-10-10T15:41Z.

    Checked in the seat's own scratch clone of 979d0ef5 (base main 45478585), not taken from the report:

    • File set: 13 files, exactly the claim's surface:

      • both changesets consumed;
      • package.json 4.0.0 → 4.0.1, written by changeset:version;
      • src/sales/index.ts version: '4.0.1';
      • src/service/index.ts version: '4.0.1' and dependencies: { 'app.objectstack.hotcrm': '^4.0.1' };
      • the README badge, the docs/ARCHITECTURE.md / docs/RELEASE_STRATEGY.md version rows, and docs/STATUS.md's sentence and HotCRM v4.0.1 transcript, each a pure version swap;
      • the three whats-new locales.
    • CHANGELOG.md: a new ## 4.0.1 / ### Patch Changes section above ## 4.0.0. Its stamps are 4547858 (fix(branding): inline the app icon as a data: URI so hosted installs show the logo and favicon #2055) and d7decd4 (fix(publish): flatten the composed artifact before posting it to the marketplace (#2053) #2054), both on main. Sentences checked:

      • "The sidebar logo and favicon now show on hosted installs."
      • "4.0.1 is the first 4.x release that installs whole. HotCRM 4.0.0's marketplace publish carried an empty app and was withdrawn, so an installer upgrades from 3.x straight to 4.0.1. That upgrade still carries 4.0.0's one-time step: convert the contact mailing addresses with scripts/backfill-contact-mailing-address.ts before os migrate apply --allow-destructive."
    • whats-new (en): v4.0.1 — Latest release, plus one paragraph above "What 4.0.0 adds". It says 4.0.1 is the first 4.x that installs from the marketplace, that a 3.x deployment upgrades straight to it with the 4.0.0 checks applying, and that the logo and favicon now show. "What 4.0.0 adds" and its checks are kept.

    • Dry run: pnpm build (Data: 18 Objects 358 Fields), then DRY_RUN=1 node scripts/publish-marketplace.mjs:

      • ── HotCRM (app.objectstack.hotcrm) @ 4.0.1;
      • bundle (flattened from 2 packages): … objects 18 …, identical to round 1;
      • release notes: 893 chars.

      4.0.1 has no ### Major Changes, so releaseNotesFor sends the whole section (lines 162–163), and it contains the conversion sentence above. The listing of the first installable 4.x therefore carries 4.0.0's upgrade step, which keeps ruling A's intent.

    • Labels: the seat applied skip-changeset, as chore(release): 3.1.0 #1903 and chore(release): 4.0.0 #2052 carried it. Check Changeset re-ran skipped.

    Out-of-scope observation from the report, accepted as dormant: releaseNotesFor's lookahead ends in \Z, which a JavaScript regex reads as a literal Z. A future major body containing a capital Z would be cut there. No release is affected: 4.0.0 and 3.0.0 come out whole, and 4.0.1 has no major. Noted, not filed in this act.

    Next:

    • this lands through the queue;
    • the maintainer pushes v4.0.1 on the merge commit, which starts release.yml and publish-staging.yml;
    • the seat reads both runs, the bundle line's object count included;
    • the maintainer's session takes the card's staging acceptance.

    Generated by Claude Code

  10. objectstack-fleet commented on Oct 10, 2026

    @objectstack-fleet
    Contributor

    Landed: PR #2056 MERGED as 1d7148bf on main (squash, single parent 45478585). main's package.json reads 4.0.1. repo:hotcrm seat, session_012zh91QzFgePbkmuHnugLN3, 2026-10-10T16:29Z.

    Two readings:

    • the PR object reads merged;
    • git diff 45478585 1d7148bf is byte-identical to the reviewed head 979d0ef5's diff: 13 files, +47 / −35.

    The seat's miss: every check on 979d0ef5 was green by 15:45:45Z, but the seat queued the PR only at 16:28Z. The earlier failed Check Changeset run (from before skip-changeset was applied) kept the PR's all-green suite event from firing, and the seat waited on that event instead of re-reading the checks. That cost about 40 minutes.

    Next: the tag is the maintainer's git push, because the seat's tag push is refused (HTTP 403):

    git fetch origin
    git tag -a v4.0.1 1d7148bf2d3a9890812786d9924da1a0843f6bbe -m "v4.0.1" -m "The first 4.x that installs whole: 4.0.0's marketplace publish carried an empty app (#2053). Also inlines the sidebar logo and favicon (#2051). Upgrading from 3.x still runs 4.0.0's one-time contact-address conversion first (CHANGELOG.md)."
    git push origin v4.0.1
    

    That push starts release.yml and publish-staging.yml. The seat then reads both runs and checks that the publish log's bundle (flattened from 2 packages): … objects 18 … line is present. The card's staging acceptance (the stored row, a fresh install, the 3.1.0 → 4.0.1 upgrade, the logo) is the maintainer session's, as the card says.


    Generated by Claude Code

  11. objectstack-fleet commented on Oct 10, 2026

    @objectstack-fleet
    Contributor

    Released: HotCRM 4.0.1 is tagged and published to the staging marketplace, this time with the whole app in the bundle. repo:hotcrm seat, session_012zh91QzFgePbkmuHnugLN3, 2026-10-10T18:28Z.

    The tag: v4.0.1 is an annotated tag (0007f97a) on 1d7148bf, the merge commit of PR #2056. The maintainer pushed it; both runs record os-zhuang as the actor.

    release.yml: run 38072931450, success (17:43:59Z → 17:44:29Z, head 1d7148bf).

    publish-staging.yml: run 38072931457, job 114273910845, success. Read from the job log:

    • the build: ✓ Build complete, Data: 18 Objects 358 Fields, Artifact: dist/objectstack.json (2343.6 KB);
    • the publish, with the line that 4.0.0's run lacked:
      ── HotCRM (app.objectstack.hotcrm) @ 4.0.1
        bundle (flattened from 2 packages): manifest {9 keys} · i18n {3 keys} · docs 4 · permissions 7 · objects 18 · data 20 · translations 1 · apps 1 · views 14 · pages 8 · dashboards 5 · reports 10 · datasets 9 · actions 31 · flows 32 · emailTemplates 76 · positions 12 · sharingRules 10 · skills 6 · hooks 44 · mappings 3 · requires 8
        ✓ patched sys_package id=pkg_7d6823ee-b9c8-4da4-842d-9e66e9fc6218
        release notes: 893 chars
        ✓ published version 4.0.1
      
    • The bundle line is identical to the dry runs the seat measured on PR fix(publish): flatten the composed artifact before posting it to the marketplace (#2053) #2054 and PR chore(release): 4.0.1 #2056. The 893-char notes carry the contact-address conversion step.

    What only the cloud can show (the card's acceptance, the maintainer session's): this container is refused cloud.objectos.app. These remain open:

    This card stays open for that.

    Also seen in the log: the same 8 approval-approvers-may-resolve-empty warnings, now the subject of decision card #2057.


    Generated by Claude Code

  12. hotlong commented on Oct 11, 2026

    @hotlong
    ContributorAuthor

    Accepted on staging: 4.0.1 publishes a complete app, and both a fresh catalog read and an in-place upgrade serve it

    The listing. v4.0.1 (tag at 1d7148bf) was published by the tag push. The control plane stores pkgv_35366dd1…:

    • 1,326,623 bytes;
    • 18 objects, 14 views, 32 flows and 20 seed datasets;
    • mailing_address present and the logo inlined;
    • listing approved, and the catalog's latest version is 4.0.1.

    4.0.0 (pkgv_7c3f2f07…, 20 KB, no objects) stays withdrawn.

    The upgrade. On the staging environment os-4s63kc, HotCRM 3.1.0 was upgraded to 4.0.1 from the console marketplace (「更新」) on 2026-10-11:

    One finding, filed separately: #2062. On a hosted environment, a contact's legacy address does not carry over to mailing_address, and the hosted customer has no way to run the documented conversion. That should be decided before 4.0.x is published to the production marketplace.

    Closing as completed. Production publish stays the maintainer's manual dispatch.


    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

Labels

priority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions