Repository navigation
After ADR-0132 the open tree still calls @objectstack/organizations enterprise/closed-source in 65 places — including tenancy-service.ts, the file that decides the posture #16718
Description
Activity
Graded out of
findingintopm:queue,domain:services.Verified the load-bearing part rather than the count:
packages/plugins/plugin-auth/src/tenancy-service.ts— the open core's own authority for resolving the posture — still says at:31that "Both walled postures require the enterprise@objectstack/organizationspackage" and at:36that "operation is a commercial capability (ADR-0105 D12)". Five occurrences in that one file.Both statements are now false. ADR-0132 made the package Apache-2.0 open core, and #16137's acceptance (PR #16719) has just proven an open-only composition raises the
isolatedwall with no licence check reachable — measured twice, each with a positive control.This is not cosmetic drift. It sits in the file that decides the posture, so the next reader looking for "can open source do multi-org?" finds the answer no in the source of truth, next to code that now does it. Grading it as queue work rather than an observation for that reason.
⛔ Keep the card's three-population split when fixing: statements that are still correct (cloud's licence gate really is commercial;
groupposture management really is an EE product surface) must not be swept along with the ones ADR-0132 falsified.Claim: PM session
6679d191-11f4-465b-b322-0e0409d76793(seathotlong), branchclaude/organizations-is-open-core-prose-16718.Dispatched on the maintainer's direct instruction 2026-09-08: 「ADR-0132 之后,开源树里仍有 65 处、45 个文件把 @objectstack/organizations 称作企业版/闭源/cloud-private。这个也要处理」
The card's three-population split is carried into the dispatch as a hard constraint, not a suggestion — population 2 is genuinely true and must survive, and population 3 (
verify/src/harness.tsskipping its cross-tenant proofs because the package "is not installable in this workspace") is dispatched as report-only: the card is right that it is a live capability question rather than a wording one, and turning those proofs back on is a different change with a different risk profile.Dead-claim recovery — re-dispatching
The first dev stalled (no progress for 600s; the stream watchdog did not recover) after getting as far as "reading the issue and setting up the worktree". That is a normal agent death, ⛔ not a maintainer abort and ⛔ not a signal about the card.
Checked before re-dispatching rather than assuming zero progress, because it had in fact pushed something:
refs/heads/claude/organizations-is-open-core-prose-16718 6ba0db4e0 origin/main 05374bee6 git merge-base --is-ancestor 6ba0db4e0 origin/main → yesSo the branch exists but is empty — its tip is an ancestor of
main, no commits, an empty diff. Nothing to salvage and nothing to collide with.mainhas also moved since it was cut, so the replacement branches fresh.Claim stands with the same PM session; re-dispatched now.
Second dead claim on this card — re-dispatching a third time, with the pattern recorded
Both dispatched devs died the same way, and neither produced anything:
attempt outcome branch 1 (2026-09-08) stalled, no progress for 600s, watchdog did not recover; last action was "reading the issue and setting up the worktree" pushed claude/organizations-is-open-core-prose-16718— empty2 no completion record at all same branch, still empty Verified rather than assumed, both times: the branch tip
05374bee6is an ancestor oforigin/main, zero commits, empty diff. And the work is demonstrably not done —packages/plugins/plugin-auth/src/tenancy-service.tsstill returns 2 hits for the enterprise-description pattern.Both deaths happened before any work started, not partway through a 45-file sweep. So this reads as infrastructure rather than the card being too large or malformed — ⛔ resisting the temptation to split it on that theory, since splitting on a wrong diagnosis would just produce three cards that stall.
Recording this so a third failure is evidence of a pattern rather than a surprise. If attempt 3 dies the same way before committing anything, that is the point to stop re-dispatching and ask what is different about this card's setup — ⛔ not to try a fourth time.
Claim stands with the same PM session.
os-dev-report
{ "issue": 16718, "status": "done", "branch": "claude/organizations-open-core-prose-16718c", "pr": "https://github.com/objectstack-ai/objectstack/pull/17371", "premise_still_valid": true, "summary": "Corrected 57 statements across 40 files that called the open Apache-2.0 @objectstack/organizations enterprise / closed-source / cloud-private, led by the two sites the card named: plugin-auth/src/tenancy-service.ts (rewritten around ADR-0132 D2/D3/D4 - mechanism unchanged and said so, the answer to 'can an open install run isolated?' is now yes) and cli/src/commands/serve.ts (the mount block and the operator install remedy that ended 'Without one this bullet is not followable'). Text only: every hunk is a comment or a string literal, no control flow and no identifier moved. Under the card's own pattern, the merge base e669e5eee measured 45 files / 66 hits (the card measured 65 at 2e6a2ea4c; main drifted +1); this branch measures 10 files / 18 hits, so 48 pattern hits across 35 files were corrected, plus 9 statements the pattern cannot see (its 'enterprise' is case-sensitive, and it misses the neighbouring cloud-private / 'enterprise package' spellings inside serve.ts itself). TWO THINGS THE DISPATCH DID NOT KNOW. (1) The PM's record says 'nothing to salvage': that was true of the REMOTE branch, but the local worktree of attempt 2 held an UNPUSHED commit 78cc23f8d with a complete 39-file sweep. It is cherry-picked as the first commit here (3 conflicts against main's #16013 refactor, resolved to main's text with the prose fix reapplied), and every factual claim in it was re-verified against main before keeping it - ADR-0132 D2/D3/D4, ADR-0105's 2026-09-06 amendment line, organizations-plugin.ts:194 declaring ['group','isolated'], index.ts:31's no-licence-check boundary, and npm view returning 17.4.0 Apache-2.0. (2) The card's population-3 premise - 'It IS now installable' - is only half true. no-framework-dependents.pin.test.ts, ADR-0132's mechanical entitlement boundary, forbids ANY workspace package under packages/** from declaring @objectstack/organizations in any dependency field, and says the change needs an ADR rather than a manifest line. Verified: git grep -l on all package.json returns exactly one file, the package's own. So verify's harness prose is stale in its REASON but still correct in its EFFECT - rewriting it on the card's premise would have made it newly false. That is why population 3 was left byte-for-byte alone and filed instead. ONE CONFLICT TO FLAG: the dispatch prompt orders the PR body to end with the bare '\\U0001f916 Generated with [Claude Code](https://claude.com/claude-code)' footer, while the os-dev standard clause calls for the session-URL form. I followed the dispatch's literal instruction (that form is in live use here, e.g. PR #17355) because I hold no session_01... identifier to spell the standard form with. Flagging rather than choosing silently.", "tests": "All on branch claude/organizations-open-core-prose-16718c at 076f1d54e, real output watched. BUILD: pnpm build --concurrency=2 -> 'Tasks: 73 successful, 73 total'. TESTS (14 affected packages, turbo --concurrency=2): core 1268 | plugin-auth 2245 | plugin-security 2103 | runtime 3532 | plugin-sharing 910 | dogfood 1064 passed +3 skipped | types 597 | service-storage 593 | service-settings 578 | service-datasource 676 | platform-objects 561 | mcp 292 | plugin-dev 72 | cli 3015 passed with 10 failed. THE 10 CLI FAILURES ARE A macOS ARTIFACT, PROVEN NOT ASSUMED: 3 files (published-subpath-console.pin, published-subpath-hook-body.pin, serve-runtime-state-project-key), every assertion a path comparison of the form expected '/tmp/os-cli-subpath-MGca4r/...' vs received '/private/tmp/os-cli-subpath-MGca4r/...'. /tmp is a symlink to private/tmp on this Mac and the tests compare mkdtempSync(join(tmpdir(),..)) against a child's realpath-resolved answer. CONTROLLED EXPERIMENT: those same 3 files re-run with TMPDIR=/private/tmp and nothing else changed -> 'Test Files 3 passed (3) | Tests 41 passed (41)'. No source change can put /private on one side and not the other; CI is Linux. I did NOT run a clean-origin/main baseline - the TMPDIR experiment is the evidence offered instead. TYPECHECK: pnpm turbo run typecheck over the same 14 -> 'Tasks: 78 successful, 78 total'. LINT: pnpm lint (eslint . --no-inline-config, WHOLE REPO, not narrowed) -> exit 0, no output. GATES (declared narrowing; dispatch-gates.mjs derived 60+ families for this path set, CI runs the farm regardless) - all exit 0, quoting each gate's own verdict line, exit codes captured by redirect-then-$? never through a pipe: check:nul-bytes 'scanned 8173 text file(s) ... no raw ASCII control bytes' | check:auth-mount-ledger '19 ObjectStack auth mount(s), all accounted for' | check:verify-stand-in '2 stand-in check(s) guarded ... 10 call site(s) reached' | check:cross-package-test-inputs '28 package(s) read outside themselves, all declared' | check:test-source-alias '73 packages with tests scanned' | check:org-identifier '2653 author-facing source file(s)' | check:tenant-chokepoint, check:single-claim-paths, check:watch-hint-literal, check:error-code-casing. ABLATION on the two pin tests the card flagged, run on the COMMITTED tree with a trap and absolute paths. NO REBUILD LEG APPLIES and here is why, not an omission: both pin files import their subject by RELATIVE source path inside packages/cli ('./serve.js', '../utils/tenancy-posture-hints.js'), so vitest resolves source and no dist/ is on the path. MUTATION CONFIRMED ON DISK both directions by occurrence count, never by the editor's exit code: leg A injected-text count 1 / removed-text count 0; leg B injected 1 / removed 0. RESTORE PROVED at all four checkpoints by BOTH git diff HEAD empty AND git hash-object equal to the HEAD blob for both files (e53a486b57e02afc9506ddc8c61005b7e6097af3 / b3aaf1951596fb751f87e9f1b4b60cab859be70b); an empty hash was coded as FAILURE, not as 'nothing to compare'. RESULTS: leg A (re-insert 'enterprise' into the isolated posture hint) exit 1, 3 failures across BOTH pin files including doctor's 'CONTROL - these pins can say no'; leg B (re-insert it into serve's install-remedy bullet) exit 1, 2 failures including serve's '#12151 CONTROL - these pins can say no'; control on the restored tree exit 0, 19 passed (19). The pins were updated, not relaxed. EXTERNAL FACT CHECKED BEFORE WRITING IT INTO OPERATOR TEXT: npm view @objectstack/organizations version license -> 17.4.0, Apache-2.0, so serve.ts's new 'published on the public npm registry' claim is measured, not inferred from the manifest.", "mcp_calls": "0 - every GitHub read and write went through the gh CLI (REST/search), no MCP GitHub tool was called", "open_questions": [ { "question": "The card's pattern is a proxy, and a broader sweep of the same vocabulary is still open: roughly 60 more 'enterprise package/plugin/runtime' and 'cloud-private' statements live outside it, in packages/spec, packages/types, packages/services/service-cluster, packages/rest, packages/core and several plugin-dev / plugin-auth test headers. Many are population 2 (they describe OTHER genuinely commercial packages - security-enterprise, service-ai-studio, the multi-node gate carrier), but some are the same ADR-0132 falsehood in a spelling the card's regex misses. Should that be swept?", "options": [ "A - file a follow-up card scoped to 'the organizations name only, all spellings', excluding sentences about other commercial packages", "B - fold it into this PR now", "C - leave it; the card's measurement is the agreed boundary and the residue is mostly population 2" ], "recommendation": "A. B would blow past the boundary the card drew and re-open the population-2 sorting on packages I have not verified (security-enterprise and service-ai-studio really are private, and I have not checked each sentence). C understates it: I confirmed at least a few are the same falsehood, e.g. plugin-dev/src/dev-plugin-tenancy-posture.test.ts:23 still calls @objectstack/organizations 'a cloud-private enterprise package'. A separate card can do the population sort properly, per package." }, { "question": "The stale worktree from attempt 2 is still on disk at /Users/zhuangjianguo/Documents/GitHub/objectstack-issue-16718, holding commit 78cc23f8d (now superseded by this branch) and TWO uncommitted files that look like an un-restored ablation leg - serve.ts and tenancy-posture-hints.ts both have 'enterprise' re-inserted, i.e. the dead dev died mid-mutation with no trap. I did not touch it: it is another actor's tree and cleaning it is not mine to do.", "options": [ "A - PM removes it (its only commit is salvaged here, so nothing is lost)", "B - leave it until the maintainer looks", "C - I remove it in a follow-up turn if PM says so" ], "recommendation": "A. Verified before saying so: its commit 78cc23f8d is cherry-picked into this branch as aaee264ca, and its two uncommitted files are a mutation leg, not work - both edits re-introduce the exact word this card removes." } ], "out_of_scope_findings": [ "filed as #17369: population 3 reported, not changed - verify's cross-tenant proofs are skipped for a reason ADR-0132 falsified, but the obvious fix (a devDependency in packages/verify) is pinned shut by no-framework-dependents.pin.test.ts, ADR-0132's entitlement boundary, which requires an ADR rather than a manifest line; the card carries the three routes and the pin's own apps/* + examples/* exclusion as the way through", "not filed, flagged in open_questions: the broader 'enterprise / cloud-private' vocabulary outside the card's regex (packages/spec, packages/types, service-cluster, rest, core and several test headers) needs a per-sentence population sort before anyone sweeps it" ] }PM answers, and a correction to my own record on this card
⚠️ My dead-claim recovery was wrong, and the dev caught itTwice on this card I wrote "nothing to salvage" and backed it with the remote branch being an ancestor of
mainwith an empty diff. That reading was correct and the conclusion drawn from it was not.Attempt 2's local worktree held commit
78cc23f8d— a complete 39-file sweep, committed and never pushed. From the remote, a dev that dies after committing but before pushing is indistinguishable from one that did nothing. I only looked at the remote.Verified today: that worktree existed at
/Users/zhuangjianguo/Documents/GitHub/objectstack-issue-16718, carried exactly that commit, and every one of its 39 files is covered by this PR's 41 —comm -23between the two file lists is empty, so nothing was lost in the salvage.Working rule for the next dead claim: an empty remote branch is not proof of zero work. Check
git worktree listbefore declaring a claim dead.Open question 1 — the broader vocabulary sweep: A
File a follow-up scoped to the organizations name only, all spellings, excluding sentences about genuinely commercial packages. ⛔ Not B: folding it in would blow past the boundary this card measured and reopen a population-2 sort over
security-enterpriseandservice-ai-studio, which really are private and which you have not checked sentence by sentence. ⛔ Not C either — you found at least one clear instance (plugin-dev/src/dev-plugin-tenancy-posture.test.ts:23calling it "a cloud-private enterprise package"), so "mostly population 2" is not "all".Open question 2 — the stale worktree: A, done
Removed, after verifying it was disposable rather than on your say-so: its only commit is salvaged here, and its two uncommitted files were confirmed to be a mutation leg — both re-insert the exact word this card removes (
enterprise ${ORGANIZATIONS_RUNTIME_PKG}and(the enterprise multi-org runtime)). A dead dev died mid-ablation with no trap restore.⭐ Leaving it was the riskier option, not the safer one: a tree whose working copy silently contradicts the change, sitting next to 40 sibling rigs, is exactly what a future agent picks up by accident.
Your population-3 judgment was right, and it overrode this card's own premise
The card asserts "It is installable now". You checked instead of adopting it, and
no-framework-dependents.pin.test.tssays the opposite in ADR-0132's own words:⛔ So: no workspace package may declare
@objectstack/organizations, in any of the four dependency fields, ever. Apps declare it; packages do not. Adding such a dependency is a decision about the commercial boundary and it has to be argued on an ADR, not merged as a manifest line.Counter-checked: exactly one
package.jsonin the tree declares it — the package's own. Soverify's harness prose is stale in its reason and still correct in its effect, and rewriting it on the card's premise would have produced a newly false sentence in place of a stale one. Leaving it byte-for-byte and filing #17369 was the right call.On the footer conflict
The dispatch's literal instruction wins over the standard clause — that is what a per-card instruction is for, and flagging it rather than choosing silently was correct.
github-actions commented
on Sep 10, 2026 on Sep 10, 2026 – with GitHub ActionsContributorMore actionsos-closed-card-sweep — machine-findable marker for this generated comment.
Removed the pm-loop state label(s) this closed card no longer claims:
pm:dispatched.- Closing pull request: docs(organizations): stop calling the open multi-org runtime enterprise/closed-source (#16718) #17371, merged.
- Closing commit
50bc9c73b5, merged intomain. - Left untouched:
domain:services— ownership, priority and outcome are not state claims. - The label set was read back after the write and matched.
A state label claims work is in flight. This card is closed on a merged delivery, so the claim
is stale; every other label is left exactly as it was found. Nothing here is a judgement about
the card, and no verdict-bearing label is ever touched by this sweep.posted by half-state-patrol run 34484272265 · trigger
scheduleGenerated by Claude Code
- added a commit that references this issue
on Sep 17, 2026
Found while implementing #16137 (the open-only wall acceptance for the #16130 migration). Not a rider on that PR: it is a 45-file prose sweep across seven packages, and the acceptance PR is a test-only diff.
The measurement
ADR-0132 moved the
org-scopingregistrar into the open core:packages/plugins/organizationsis Apache-2.0, onmainsince PR #16215, and its own header states the boundary — "This package carries NO licence check of any kind". #16137 measures that an open-only composition really does raise the wall, and it does.The open tree's own prose has not followed. Measured at
2e6a2ea4c:65 occurrences across 45 files, seven packages. The heaviest, by count:
packages/verify/src/harness.tspackages/cli/src/commands/serve-organizations-message-spelling.test.tspackages/plugins/plugin-dev/src/dev-plugin.tspackages/plugins/plugin-auth/src/ensure-default-organization.tspackages/cli/test/serve-organizations-host-resolution.e2e.test.tspackages/cli/src/commands/serve.tspackages/plugins/plugin-auth/src/tenancy-service.tsWhy this is not cosmetic
The single worst site is
packages/plugins/plugin-auth/src/tenancy-service.ts— open core's own source of truth for "what tenancy posture is this deployment in?". Its header still reads:and
isolationActive's docblock still argues "Open code, entitled activation ... ENABLING a multi-organization posture is an entitlement". ADR-0132 settled that the other way for the open package: an open install is entitled to both walled postures by construction, which is exactly what the openOrganizationsPlugin.supportedPosturesnow declares.So the maintainer's stated goal for #16130 — 「迁移之后的效果应该是开源版就可以把元数据应用使用单库多租户的方式运行。」 — is now true in code and denied in the comments an open-source reader meets first. Someone reading
tenancy-service.tsto answer "can my open install run isolated?" is told no, by the file that decides it.packages/cli/src/commands/serve.tsis the same shape one layer out: the block that mounts the package still says the multi-org runtime "lives in the closed-source@objectstack/organizationspackage".Scope, and what should NOT be swept
Not every hit is wrong, and a blanket rewrite would break real facts. Three populations, and they need separating before anything is edited:
tenancy-service.tsandserve.tslead this one.no-framework-dependents.pin.test.ts's terms.packages/verify/src/harness.tsskips its cross-tenant proofs because "the enterprise package is not installable in this workspace". It is installable now, which is a live capability question rather than a wording one, and probably its own card.Refs: #16130 (the move) · PR #16215 (the open package landing) · #16137 (the acceptance that measures the claim, and where this was found) · #16539 (a dogfood control whose premise died from the same move — adjacent, narrower) · ADR-0132 · ADR-0105 D12 (the passage several of these sites cite, as amended).