Repository navigation
Commit f624f27
feat(app-shell,console): Studio reaches package-less flows at /studio/~org/automations (objectui#11553) (#11565)
Fixes #11553
Clause-②: yes
Implements the ruling triage recorded on
objectstack-ai/objectstack#21332 (comment `5946433961`): **the Studio
arm, not the package arm.** The clone stays package-less (ADR-0126
§7.1). Nothing changes on the objectstack side. Studio gains a surface
for package-less, org-owned flows.
Dev run: `session_01FjqrwXPfSMkSfkKYDSRkN2` (`mode:subagent` of the PM
seat), branch `claude/issue-11553-studio-packageless-flows`, cut from
`origin/main` `d0c0c7fe9`.
## What failed, measured on `origin/main` before the change
- Routing: `studioRoutes` declared only `/studio`, `/studio/:packageId`
and `/studio/:packageId/:tab`, so every pillar was keyed by a package.
- The Automations rail called `loadPackageSurfaces(client, 'flow',
packageId)`, which reads `list('flow', { packageId })` and `listDrafts({
packageId, type })`. The registry's package filter keeps only items
whose `_packageId` equals that package, so a flow with no package is in
no rail.
- The home (`BuilderLanding`) listed packages only. On the stock
showcase that means "No writable packages yet" plus the read-only
showcase package.
- The deep link: the rail's list effect did `setCurrent((c) => c ??
deepLinked ?? items[0] ?? null)`. A `?surface=flow:NAME` link naming a
flow the rail did not hold therefore opened the first flow, and the URL
mirror rewrote `?surface=` to that flow. That is the "redirects to
another flow" from the QA run.
## The change
**One reserved scope, `/studio/~org/automations`, served by the same
`StudioDesignSurface` with no package under it.** There is no second
builder.
- `studioScope.ts` (new) holds:
- the segment `~org`;
- the path builder `studioOrgScopePath`;
- the package-less predicates. They read the spec's `_packageId` on
served items (from `MetadataProtectionFields`, which `FlowSchema`
spreads) and `packageId` on `_drafts` headers. The DB-authored
`sys_metadata` pseudo-package also reads as "no package", the same
reading `studioPackageId` in `utils/appRoute.ts` gives it.
- `~` is outside the spec's `MANIFEST_ID_PATTERN` and outside this app's
`PACKAGE_ID_RE` and `sanitizePackageId`, so the segment cannot equal a
package id. A test pins this against both rules.
- `packageSurfaces.ts`: new `loadPackageLessSurfaces`. It does an
unscoped `list('flow')` and `listDrafts({ type })`, keeps only rows and
headers bound to no package, and merges them through the existing
published-wins merge.
- `AutomationsPillar`: `packageId: string | null`. With `null` it lists
package-less flows, and drafts save with no `package=`. The scope has no
"New" flow (authoring stays package-first).
- Deep links: a `?surface=flow:` link that names a flow the rail does
not hold no longer opens the first flow. From a package's pillar, a
named package-less flow is found and opened in
`/studio/~org/automations?surface=flow:NAME`, once per mount with
replace navigation. A flow found nowhere is reported on the canvas ("The
link names flow “NAME”, which is not here.").
- `StudioDesignSurface`, in the package-less scope:
- Writable by derivation. `readOnly` is `packageId !== null &&
pkgWritable === false`, so a read-only answer left over from the package
shown before the switch cannot lock the scope. The server write gate
stays the authority.
- Offers its one pillar (another pillar's URL lands on it), no Create
app, and no package copilot.
- The header count and the pending-changes sheet cover package-less
**flow** drafts only.
- Publish promotes each of those drafts by reference through
`publishDraft`. `POST /packages/:id/publish-drafts` cannot reach a draft
bound to no package; this is the same door Home's publish-all uses for
package-less drafts. It is not all-or-nothing, and failures are named
per draft.
- `PackageSwitcher` and `BuilderLanding` each get an entry,
"Organization flows". On the home it sits under its own "Not in a
package" heading, shown whether or not any writable package exists.
- `DraftChangesPanel` gets an optional `include` predicate. The scope
passes its own filter, so the sheet reviews exactly what that scope's
Publish ships. Package hosts pass nothing, and their behaviour is
unchanged.
- The console's `studioRoutes` get the bare `/studio/~org` leg, which
lands on `automations`. The pillar URL itself is served by the generic
`:packageId/:tab` route.
- i18n: seven new keys in `metadata-admin/i18n.ts` (en and zh), added
beside `engine.studio.landing.installedHeading`. No key is moved or
reordered.
- Docs: `packages/app-shell/README.md` and
`content/docs/guide/console.md`. Changeset:
`.changeset/11553-studio-package-less-flows.md` (`@object-ui/app-shell`
minor, `@object-ui/console` patch).
**Landing versus the claim's file surface.** Everything sits in the
claimed surface except three files in the same package, each needed by
it:
- `packages/app-shell/src/preview/DraftChangesPanel.tsx`: the `include`
predicate, so the sheet does not review drafts the scope's Publish never
ships;
- `packages/app-shell/src/index.ts`: exports the segment, pillar and
path builder for the console route;
- the two docs files above, which AGENTS.md commandment #2 requires.
## The ruling's pins
| Pin | Where |
|---|---|
| A package-less flow is listed and opens editable |
`StudioDesignSurface.packageLessFlows-11553.test.tsx`: "lists the clone
and none of the packaged flows"; "opens it editable, and an edit saves a
draft bound to no package" |
| A packaged flow on a read-only package stays read-only | the same
file, "lists the package’s flows, not the clone, and offers no edit";
objectui#11124's
`StudioDesignSurface.automationsReadOnly-11124.test.tsx` stays green,
unchanged |
| The deep link to a package-less flow opens that flow, not another |
"in the package-less scope: the named flow, though another is listed
first"; "the measured link, through the showcase pillar: lands on the
package-less scope, the clone open and editable" |
Further pins: `studioScope.test.ts`, `packageSurfaces.test.ts`
(`loadPackageLessSurfaces`), `BuilderLanding.orgScope-11553.test.tsx`
(the entry on the measured shape, with no writable package) and
`StudioRoute.test.tsx` (the `~org` leg, behind the same entry gate).
## Verification (final head `4831c5a`)
- Build: `pnpm --workspace-concurrency=2 --filter
'@object-ui/app-shell^...' build` exited 0 (29 packages). The plugin
dists the console needs were built for its type-check.
- `pnpm --filter @object-ui/app-shell type-check`: exit 0. This covers
both `tsc --noEmit` and `tsconfig.test.json`.
- `pnpm --filter @object-ui/console type-check`: exit 0, after
rebuilding app-shell.
- `pnpm exec vitest run packages/app-shell/src/views/studio-design/`: 88
files, 521 tests passed.
- `pnpm exec vitest run packages/app-shell/src/preview/
apps/console/src/components/`: 50 files, 562 tests passed.
- `eslint` on the 13 changed TS/TSX files: exit 0, 0 errors. Warnings
per file equal the base `d0c0c7fe9` count for each pre-existing file
(StudioDesignSurface 17, DraftChangesPanel 2, StudioRoute 1,
BuilderLanding 0).
- Repo checks, all exit 0:
- `check:i18n-keys`, `check:i18n-drift`, `check:i18n-dead-keys`,
`check:i18n-designer-parity`
- `check:control-bytes`, `check:new-line-citations`,
`check:changeset-claims`, `check:pending-changeset-literals`
- `check:spec-symbols`, `check:readme-exports`,
`check:unreferenced-sources`, `check:test-path-roots`
- `check:vi-mock-specifiers`, `check:vi-mock-inherit`,
`check:vi-mock-override-shape`
- `check:metadata-write-doors`, `check:handler-key-reads`,
`check:self-import`, `check:phantom-deps`, `check:esm-specifiers`
- `check:doc-fences`, `check:doc-types`
- `check-changeset-presence`, `check-changeset-no-major`,
`check-changeset-overwrite`, `check-changeset-fixed`, `check-doc-links`
**Ablations.** Each mutation was made on committed code through
`ablation-replace.mjs` in WRAP mode. Each anchor hit once, the blob
changed, and the restore was proven with blob equal to HEAD and an empty
`git diff HEAD`. The tests import `./StudioDesignSurface` from source,
so no build was needed.
| Ablation | Result |
|---|---|
| A1: the deep-link fallback put back to `?? items[0]` | 1 red: "a link
naming a flow no rail holds opens no other flow" |
| A2: the package-less redirect removed | 1 red: "the measured link,
through the showcase pillar" |
| A3: the package-less loader swapped for the package loader | 9 red |
| A4: the package-less publish arm disabled | 1 red: "publishes each
package-less flow draft by reference" |
| A5: the header count unfiltered | 1 red: "counts only package-less
flow drafts" |
| A6: the sheet's `include` dropped | 1 red |
| A7: `readOnly` put back to `pkgWritable === false` (at `4831c5a`) | 2
red, including "from that read-only package, the switcher’s entry opens
the package-less scope editable" |
Lint was narrowed to the changed files. The repo-wide `pnpm lint` and
the full test shards are left to CI. No browser check was run.
## Acceptance notes
- **Not this card:** objectui#11546 (a read-only canvas opens no node
inspector) remains open; this change does not touch it.
- **The Setup page's clone result does not link to the copy in Studio.**
Setup › Packaged automation says "Edit the copy in Studio" after a
clone. The copy is now reachable from Studio's home and switcher, and
`studioOrgScopePath({ type: 'flow', name })` could produce a direct
link, but none is added here (that file is outside this card). Noted,
not filed; carrier: none.
- **The same deep-link fallback lives in two sibling rails.** The
Interfaces rail (`cur ?? deepLinked ?? firstLeaf`) and the Data rail (`c
?? deepLinked ?? items[0]`) still open their first item when a
`?surface=` link names one they do not hold. That is the same mechanism
this card removed for flows, but it was not measured through a public
entry here. Noted, not filed; carrier: none.
- **A host that mounts `StudioDesignSurface` with its own `aiSlot`**
(the cloud edition) and reads `:packageId` itself would see `~org` as a
package id in this scope. Not measured: there is no such host in this
repository.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01FjqrwXPfSMkSfkKYDSRkN2)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent b0bf413 commit f624f27
16 files changed
Lines changed: 1109 additions & 42 deletions
File tree
- .changeset
- apps/console/src/components
- content/docs/guide
- packages/app-shell
- src
- preview
- views
- metadata-admin
- studio-design
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
336 | 336 | | |
337 | 337 | | |
338 | 338 | | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
339 | 358 | | |
340 | 359 | | |
341 | 360 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
40 | 40 | | |
41 | 41 | | |
42 | 42 | | |
| 43 | + | |
| 44 | + | |
43 | 45 | | |
44 | 46 | | |
45 | 47 | | |
| |||
102 | 104 | | |
103 | 105 | | |
104 | 106 | | |
105 | | - | |
| 107 | + | |
| 108 | + | |
106 | 109 | | |
107 | 110 | | |
108 | 111 | | |
| |||
150 | 153 | | |
151 | 154 | | |
152 | 155 | | |
| 156 | + | |
153 | 157 | | |
154 | 158 | | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
155 | 167 | | |
156 | 168 | | |
157 | 169 | | |
158 | 170 | | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
159 | 175 | | |
160 | 176 | | |
161 | 177 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
29 | 29 | | |
30 | 30 | | |
31 | 31 | | |
| 32 | + | |
32 | 33 | | |
33 | 34 | | |
34 | 35 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
374 | 374 | | |
375 | 375 | | |
376 | 376 | | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
377 | 393 | | |
378 | 394 | | |
379 | 395 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
451 | 451 | | |
452 | 452 | | |
453 | 453 | | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
454 | 462 | | |
455 | 463 | | |
456 | 464 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
337 | 337 | | |
338 | 338 | | |
339 | 339 | | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
340 | 348 | | |
341 | 349 | | |
342 | 350 | | |
| |||
351 | 359 | | |
352 | 360 | | |
353 | 361 | | |
| 362 | + | |
354 | 363 | | |
355 | 364 | | |
356 | 365 | | |
| |||
386 | 395 | | |
387 | 396 | | |
388 | 397 | | |
389 | | - | |
| 398 | + | |
| 399 | + | |
390 | 400 | | |
391 | 401 | | |
392 | 402 | | |
| |||
428 | 438 | | |
429 | 439 | | |
430 | 440 | | |
431 | | - | |
| 441 | + | |
432 | 442 | | |
433 | 443 | | |
434 | 444 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2776 | 2776 | | |
2777 | 2777 | | |
2778 | 2778 | | |
| 2779 | + | |
| 2780 | + | |
| 2781 | + | |
| 2782 | + | |
| 2783 | + | |
| 2784 | + | |
| 2785 | + | |
| 2786 | + | |
2779 | 2787 | | |
2780 | 2788 | | |
2781 | 2789 | | |
| |||
5649 | 5657 | | |
5650 | 5658 | | |
5651 | 5659 | | |
| 5660 | + | |
| 5661 | + | |
| 5662 | + | |
| 5663 | + | |
| 5664 | + | |
| 5665 | + | |
| 5666 | + | |
| 5667 | + | |
5652 | 5668 | | |
5653 | 5669 | | |
5654 | 5670 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
12 | | - | |
13 | | - | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
14 | 15 | | |
15 | 16 | | |
16 | 17 | | |
17 | 18 | | |
18 | 19 | | |
19 | | - | |
| 20 | + | |
20 | 21 | | |
21 | 22 | | |
22 | 23 | | |
23 | 24 | | |
24 | 25 | | |
| 26 | + | |
25 | 27 | | |
26 | 28 | | |
27 | 29 | | |
| |||
203 | 205 | | |
204 | 206 | | |
205 | 207 | | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
206 | 234 | | |
207 | 235 | | |
208 | 236 | | |
| |||
0 commit comments