You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
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
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)
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
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.
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.
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.
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.
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.
{
"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."
]
}
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.
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.
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.
{
"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."
]
}
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 main45478585), not taken from the report:
File set: 13 files, exactly the claim's surface:
both changesets consumed;
package.json4.0.0 → 4.0.1, written by changeset:version;
src/sales/index.tsversion: '4.0.1';
src/service/index.tsversion: '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 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.
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.
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.
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.
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:
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.
Activity
objectstack-fleet commentedon Oct 10, 2026
Claim: PM loop round R78
Session:
session_012zh91QzFgePbkmuHnugLN3Account:
os-zhuang(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-2053-publish-flattenWorktree:
hotcrm-issue-2053Domain:
repo:hotcrm(single-lane repo, nodomain:*taxonomy)Seat:
repo:hotcrm#1File surface:
scripts/publish-marketplace.mjs(flatten the composedpackages[]artifact, plus the empty-bundle guard before any HTTP call), a new test of it undertest/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(governedPathsInover 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
objectstack-fleet commentedon Oct 10, 2026
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
objectstack-fleet commentedon Oct 10, 2026
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 head6c78cfa1is green.repo:hotcrmseat,session_012zh91QzFgePbkmuHnugLN3, 2026-10-10T15:05Z.Re-measured by the seat in its own scratch clone of
6c78cfa1, not taken from the report:Part of #2053, thenClause-②: no;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': patchchangeset.src/sales/apps/crm.app.tsis untouched.@objectstack/spec,@objectstack/core,@objectstack/objectql) are directdependenciesinpackage.json, sopublish-staging.yml'spnpm install --frozen-lockfileresolves them.pnpm build(Data: 18 Objects 358 Fields), thenDRY_RUN=1 node scripts/publish-marketplace.mjsprintsbundle (flattened from 2 packages): … objects 18 … requires 8, the same line as the PR body.v3.1.0release asset and read itsdist/objectstack.jsonas data.emailTemplates(76).manifesthas the identical 9 keys.crm_case,crm_knowledge, My Cases and SLA, as 3.1.0's did. The module's contributions are folded in, not dropped.assertBundleCarriesEveryObject(artifact, artifact)throwsBundleError … 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:manifestobject, as 3.1.0 posted;log_callis legitimately on five objects;navigationContributionsis folded.AGENTS.mdScope 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 apackages[]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
objectstack-fleet commentedon Oct 10, 2026
Landed: PR #2054 MERGED as
d7decd43onmain(squash, single parent6162c43f). The seat landed it through the merge queue after 9 of 9 checks went green on head6c78cfa1: ready and queued at 15:13:26Z, merged at 15:13:44Z.repo:hotcrmseat,session_012zh91QzFgePbkmuHnugLN3, 2026-10-10T15:14Z.Two readings:
merged;git diff 6162c43f d7decd43is byte-identical to the reviewed head's diff: 5 files, +506 / −4.What now holds on
main:scripts/publish-marketplace.mjsflattens 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:objectstack servemounts — every hosted install shows a broken logo (404); inline the icon as a data URI (maintainer ruling 2026-10-10) #2051 (PR fix(branding): inline the app icon as a data: URI so hosted installs show the logo and favicon #2055, ACCEPTed, its CI running);v4.0.1;Generated by Claude Code
objectstack-fleet commentedon Oct 10, 2026
Claim: PM loop round R79 (#2053 round 2: the 4.0.1 version PR)
Session:
session_012zh91QzFgePbkmuHnugLN3Account:
os-zhuang(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-2053-release-4-0-1Worktree:
hotcrm-issue-2053r2Domain:
repo:hotcrm(single-lane repo, nodomain:*taxonomy)Seat:
repo:hotcrm#1File surface:
package.jsonandCHANGELOG.md(written bychangeset version);.changeset/*(the two patch changesets consumed; a first-paragraph text edit to2053-publish-flat-bundle.mdonly);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(governedPathsInover 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 wholeThread-read: none
Serial constraints cleared: round 1 landed as
d7decd43(PR #2054), and #2051 as45478585(PR #2055). Nothing else is in flight; this PR stays last until it lands.Generated by Claude Code
objectstack-fleet commentedon Oct 10, 2026
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
objectstack-fleet commentedon Oct 10, 2026
Verdict: os-dev report
6099195039(round 2), PR #2056. ACCEPT, Tier S (not governed; +47 / −35 over 13 files, percheck-governed-merges.mjs): the seat lands it through the merge queue once every check on head979d0ef5is green.repo:hotcrmseat,session_012zh91QzFgePbkmuHnugLN3, 2026-10-10T15:41Z.Checked in the seat's own scratch clone of
979d0ef5(basemain45478585), not taken from the report:File set: 13 files, exactly the claim's surface:
package.json4.0.0→4.0.1, written bychangeset:version;src/sales/index.tsversion: '4.0.1';src/service/index.tsversion: '4.0.1'anddependencies: { 'app.objectstack.hotcrm': '^4.0.1' };docs/ARCHITECTURE.md/docs/RELEASE_STRATEGY.mdversion rows, anddocs/STATUS.md's sentence andHotCRM v4.0.1transcript, each a pure version swap;CHANGELOG.md: a new## 4.0.1/### Patch Changessection above## 4.0.0. Its stamps are4547858(fix(branding): inline the app icon as a data: URI so hosted installs show the logo and favicon #2055) andd7decd4(fix(publish): flatten the composed artifact before posting it to the marketplace (#2053) #2054), both onmain. Sentences checked:scripts/backfill-contact-mailing-address.tsbeforeos 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), thenDRY_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, soreleaseNotesForsends 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 Changesetre-ranskipped.Out-of-scope observation from the report, accepted as dormant:
releaseNotesFor's lookahead ends in\Z, which a JavaScript regex reads as a literalZ. 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:
v4.0.1on the merge commit, which startsrelease.ymlandpublish-staging.yml;Generated by Claude Code
objectstack-fleet commentedon Oct 10, 2026
Landed: PR #2056 MERGED as
1d7148bfonmain(squash, single parent45478585).main'spackage.jsonreads4.0.1.repo:hotcrmseat,session_012zh91QzFgePbkmuHnugLN3, 2026-10-10T16:29Z.Two readings:
merged;git diff 45478585 1d7148bfis byte-identical to the reviewed head979d0ef5's diff: 13 files, +47 / −35.The seat's miss: every check on
979d0ef5was green by 15:45:45Z, but the seat queued the PR only at 16:28Z. The earlier failedCheck Changesetrun (from beforeskip-changesetwas 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):That push starts
release.ymlandpublish-staging.yml. The seat then reads both runs and checks that the publish log'sbundle (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
objectstack-fleet commentedon Oct 10, 2026
Released: HotCRM 4.0.1 is tagged and published to the staging marketplace, this time with the whole app in the bundle.
repo:hotcrmseat,session_012zh91QzFgePbkmuHnugLN3, 2026-10-10T18:28Z.The tag:
v4.0.1is an annotated tag (0007f97a) on1d7148bf, the merge commit of PR #2056. The maintainer pushed it; both runs recordos-zhuangas the actor.release.yml: run 38072931450,success(17:43:59Z → 17:44:29Z, head1d7148bf).publish-staging.yml: run 38072931457, job 114273910845,success. Read from the job log:✓ Build complete,Data: 18 Objects 358 Fields,Artifact: dist/objectstack.json (2343.6 KB);What only the cloud can show (the card's acceptance, the maintainer session's): this container is refused
cloud.objectos.app. These remain open:sys_package_versionrow for 4.0.1: its size, its 18 objects, and whether it keptemailTemplates;approvedand the catalog latest4.0.1;objectstack servemounts — every hosted install shows a broken logo (404); inline the icon as a data URI (maintainer ruling 2026-10-10) #2051);This card stays open for that.
Also seen in the log: the same 8
approval-approvers-may-resolve-emptywarnings, now the subject of decision card #2057.Generated by Claude Code
hotlong commentedon Oct 11, 2026
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 at1d7148bf) was published by the tag push. The control plane storespkgv_35366dd1…:mailing_addresspresent and the logo inlined;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:version 4.0.1;crm_*objects and 32 flows, and the contact object carriesmailing_address;objectstack servemounts — every hosted install shows a broken logo (404); inline the icon as a data URI (maintainer ruling 2026-10-10) #2051 is fixed).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