Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
de912b0
feat(tools): data-less 'tools' source endpoint + source-attached cont…
Rod-Christensen Jul 27, 2026
150a999
feat(deploy): teams-as-environments backend — org artifact registry, …
Rod-Christensen Jul 27, 2026
6b1e84b
feat(run-log): team-scoped deploy continua — teammates can watch and …
Rod-Christensen Jul 27, 2026
fdd6c62
feat(run-log): drop runKind from the rrext_log wire — the scope IS th…
Rod-Christensen Jul 27, 2026
b087a9c
feat(deploy): list-API envelope on list/versions/history — history pa…
Rod-Christensen Jul 27, 2026
38036e0
feat(dev-team): default team becomes the profile-assigned development…
Rod-Christensen Jul 27, 2026
9190450
fix(ai-tests): make the ai suite warning-free
Rod-Christensen Jul 27, 2026
186203b
feat(sdk): migrate both client SDKs to teams-as-environments deploy +…
Rod-Christensen Jul 27, 2026
e4db646
feat(ui-arch): deploy surface completion + the Provider/View/Panel/Pa…
Rod-Christensen Jul 28, 2026
63c5df6
feat(deploy): vocabulary split, per-source run settings, honest outco…
Rod-Christensen Jul 29, 2026
46aa257
feat(deploy-2): owner-scoped task identity, scoped monitors, push-dri…
Rod-Christensen Jul 31, 2026
ccc11df
fix(ci): repair the deploy-2 CI failures — stale typecheck debt + the…
Rod-Christensen Jul 31, 2026
5439505
fix(review): address CodeRabbit findings on the deploy-2 capstone (#1…
Rod-Christensen Jul 31, 2026
84c6c68
fix(review): close out the remaining CodeRabbit findings on #1764
Rod-Christensen Jul 31, 2026
b814930
fix(review): third pass — the review-body findings outside the inline…
Rod-Christensen Jul 31, 2026
1c4397a
fix(review): fourth pass — CodeRabbit findings on the third-pass commit
Rod-Christensen Jul 31, 2026
1b7cbeb
fix(review): restore the three reset-lost review items (docs + verb d…
Rod-Christensen Jul 31, 2026
e705092
fix(review): fifth pass — snapshot diagnostics, trigger contract, pro…
Rod-Christensen Jul 31, 2026
7711c13
fix(review): sixth pass — 28 CodeRabbit findings across scheduler, de…
Rod-Christensen Jul 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 63 additions & 63 deletions apps/shell-ui/scripts/freeze-shell-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,11 @@
// (no flag) full freeze — appends the next version + re-accumulates the contract.
// --check CI mode — nonzero exit if the live surface differs from the newest
// frozen version (no freeze committed).
// --regen reapply the accumulated barrels + conformance over the EXISTING
// versions without appending one (used when the mechanism, not the
// surface, changes).
// --regen RESET to a fresh v0 — drops every frozen version and re-mints v0
// from the CURRENT live surface (pre-1.0 policy: breaking surface
// changes collapse the history; no external consumers hold floors
// yet). CI pairs it with `git diff --exit-code`, so a committed
// contract that disagrees with the live surface fails the diff.
// =============================================================================

const path = require('path');
Expand Down Expand Up @@ -209,8 +211,11 @@ function cleanup() {
/**
* Step 1 — type-check shared-ui and the shell-ui contract program. A frozen
* contract must never be generated from a broken tree.
*
* @param {string} [failureNote] Extra recovery instruction printed on failure
* (regen mode passes one because its destructive reset has already run).
*/
function preCheck() {
function preCheck(failureNote) {
log('Pre-check: tsc --noEmit (shared-ui + shell-ui contract)...');
for (const [label, tsconfig] of [
['shared-ui', SHARED_UI_TSCONFIG],
Expand All @@ -220,6 +225,7 @@ function preCheck() {
if (r.code !== 0) {
console.error(`[shell:freeze] Pre-check FAILED for ${label}:`);
console.error(r.output);
if (failureNote) console.error(failureNote);
cleanup();
process.exit(1);
}
Expand All @@ -238,18 +244,7 @@ function preCheck() {
function generateCandidate() {
fs.mkdirSync(TMP_DIR, { recursive: true });
const candidatePath = path.join(TMP_DIR, 'candidate.d.ts');
const r = runNodeBin(
DBG.binPath,
[
'--project', path.relative(SRC_DIR, CONTRACT_TSCONFIG),
'--no-check',
'--no-banner',
'--export-referenced-types=false',
'-o', candidatePath,
'api.ts',
],
SRC_DIR,
);
const r = runNodeBin(DBG.binPath, ['--project', path.relative(SRC_DIR, CONTRACT_TSCONFIG), '--no-check', '--no-banner', '--export-referenced-types=false', '-o', candidatePath, 'api.ts'], SRC_DIR);
if (r.code !== 0 || !fs.existsSync(candidatePath)) {
console.error('[shell:freeze] dts-bundle-generator FAILED:');
console.error(r.output);
Expand Down Expand Up @@ -385,18 +380,7 @@ function writeApiVersion(n) {

/** Build the provenance header stamped onto each frozen version file. */
function versionHeader(next) {
return [
MIT_HEADER,
'',
'// =============================================================================',
`// FROZEN shell-api contract — ShellApiV${next} — never edit by hand`,
'// =============================================================================',
`// Generated: ${new Date().toISOString()}`,
`// Source commit: ${gitCommit()}`,
`// Generator: dts-bundle-generator@${DBG.version}`,
'// Produced by: ./builder shell:freeze',
'// =============================================================================',
].join('\n');
return [MIT_HEADER, '', '// =============================================================================', `// FROZEN shell-api contract — ShellApiV${next} — never edit by hand`, '// =============================================================================', `// Generated: ${new Date().toISOString()}`, `// Source commit: ${gitCommit()}`, `// Generator: dts-bundle-generator@${DBG.version}`, '// Produced by: ./builder shell:freeze', '// ============================================================================='].join('\n');
}

/**
Expand All @@ -407,15 +391,7 @@ function versionHeader(next) {
*/
function writeVersion(next, candidateBody) {
fs.mkdirSync(VERSIONS_DIR, { recursive: true });
const content = [
versionHeader(next),
'',
BEGIN,
candidateBody.trim(),
END,
`export type ShellApiV${next} = ShellApiShape;`,
'',
].join('\n');
const content = [versionHeader(next), '', BEGIN, candidateBody.trim(), END, `export type ShellApiV${next} = ShellApiShape;`, ''].join('\n');
fs.writeFileSync(path.join(VERSIONS_DIR, `v${next}.d.ts`), content);
}

Expand All @@ -425,13 +401,7 @@ function writeVersion(next, candidateBody) {
* @param {number} maxN - The newest (just-frozen) version number.
*/
function regenerateBarrels(maxN) {
const generatedNote = [
'',
'// =============================================================================',
'// GENERATED by `./builder shell:freeze` — do not edit by hand.',
'// =============================================================================',
'',
].join('\n');
const generatedNote = ['', '// =============================================================================', '// GENERATED by `./builder shell:freeze` — do not edit by hand.', '// =============================================================================', ''].join('\n');

// latest.ts re-exports the newest version's full surface. `export type *`
// (not `export *`) keeps this a pure type re-export: shell-api is a types-only
Expand All @@ -458,7 +428,7 @@ function regenerateBarrels(maxN) {
' *',
' * Accumulation is NOT an intersection of versions: that reduces nominal members',
' * (enums like ConnectionState) to `never`. The "nothing ever frozen can be',
" * removed\" guarantee is the per-version floors in shell-ui's",
' * removed" guarantee is the per-version floors in shell-ui\'s',
' * contract-check.generated.ts, which assert the live surface still satisfies',
' * EACH frozen version separately.',
' */',
Expand Down Expand Up @@ -549,7 +519,7 @@ function generateConformance(maxN) {
lines.push('// =============================================================================');
lines.push('// GENERATED by `./builder shell:freeze` — do not edit by hand.');
lines.push('//');
lines.push('// Enforces the accumulated shell-api contract under shell-ui\'s own tsc:');
lines.push("// Enforces the accumulated shell-api contract under shell-ui's own tsc:");
lines.push('// - a per-version VALUE floor: the live surface must satisfy EACH frozen');
lines.push('// version separately (per-version, not intersected — intersecting the');
lines.push('// snapshots reduces nominal members like enums to `never`).');
Expand Down Expand Up @@ -592,22 +562,54 @@ function main() {
const checkMode = process.argv.includes('--check');
const regenMode = process.argv.includes('--regen');

// --regen: reapply the accumulated barrels + conformance over the EXISTING
// versions without freezing a new one. Used when the contract MECHANISM
// changes (not the surface) — no candidate is generated, no version written.
// --regen: RESET the contract to a single fresh v0 frozen from the CURRENT
// live surface. Pre-1.0 policy: while the platform iterates, intentional
// breaking surface changes are absorbed by collapsing the accumulated
// history back to one v0 (no external consumers hold the old floors yet).
// CI still pairs this with `git diff --exit-code`: a committed freeze that
// disagrees with the live surface — including a hand-edited floor or
// version file — regenerates differently and fails the diff.
//
// ORDER MATTERS: the old floors/barrels must be stubbed out BEFORE the tsc
// pre-check — after a breaking surface change they are precisely what no
// longer compiles. Every stubbed file is regenerated at the end; an
// interrupted run is repaired by simply re-running shell:regen.
if (regenMode) {
preCheck();
const { prev } = determineVersions();
if (prev < 0) {
log('No frozen versions to regenerate.');
return;
// Step R1 — drop every frozen version; v0 is re-minted below.
if (fs.existsSync(VERSIONS_DIR)) {
for (const f of fs.readdirSync(VERSIONS_DIR)) {
if (/^v\d+\.d\.ts$/.test(f)) fs.rmSync(path.join(VERSIONS_DIR, f), { force: true });
}
}
regenerateBarrels(prev);
generateConformance(prev);
// Also re-stamp apiver.ts so a freeze interrupted between conformance and
// the version stamp is fully repairable with a single --regen run.
writeApiVersion(prev);
log(`Regenerated index.ts + contract-check.generated.ts + apiver.ts from v0..v${prev} (no new version).`);

// Step R2 — stub the generated contract files so the pre-check compiles
// against the LIVE surface only (no stale floors, no missing ./versions
// imports). All three are rewritten for real by the regeneration below.
const stubNote = ['// =============================================================================', '// RESET STUB — transient state inside `./builder shell:regen`; regenerated', '// into the real file before the run completes. If you are reading this in a', '// committed tree, a reset was interrupted: re-run `./builder shell:regen`.', '// ============================================================================='].join('\n');
fs.writeFileSync(CONTRACT_CHECK, `${MIT_HEADER}\n\n${stubNote}\nexport {};\n`);
fs.writeFileSync(INDEX_TS, `${MIT_HEADER}\n\n${stubNote}\n// Vacuous alias: contract-hold.ts still assigns the live surface to\n// ShellApiLatest during the pre-check; unknown accepts anything.\nexport type ShellApiLatest = unknown;\n`);
fs.writeFileSync(LATEST_TS, `${MIT_HEADER}\n\n${stubNote}\nexport {};\n`);

// Step R3 — the normal freeze pipeline, pinned to version 0. A pre-check
// failure HERE lands after the destructive R1/R2 steps: the frozen
// versions are gone and three generated files are stubs, and re-running
// shell:regen fails at this same point until the live surface compiles —
// so the failure note names git restore as the way back.
preCheck('[shell:freeze] The reset already dropped the frozen versions and stubbed the generated files. ' + 'Fix the reported type errors and re-run `./builder shell:regen`, or return to the committed ' + 'contract with: git restore packages/shell-api apps/shell-ui/src/contract-check.generated.ts');
const candidateBody = generateCandidate();
writeVersion(0, candidateBody);
try {
regenerateBarrels(0);
generateConformance(0);
writeApiVersion(0);
} catch (err) {
// Roll the snapshot back so a rerun regenerates EVERYTHING instead
// of seeing a half-written v0 as the baseline.
fs.rmSync(path.join(VERSIONS_DIR, 'v0.d.ts'), { force: true });
throw err;
}
cleanup();
log('Recreated the v0 freeze from the live surface (contract history reset to v0).');
return;
}

Expand All @@ -630,9 +632,7 @@ function main() {
} else if (!hasActionableChange(prev, candidateBody)) {
log(`Up to date with v${prev} (no actionable change).`);
} else {
console.error(
`[shell:freeze --check] Shell API has new exports not in v${prev}. Run \`./builder shell:freeze\`.`,
);
console.error(`[shell:freeze --check] Shell API has new exports not in v${prev}. Run \`./builder shell:freeze\`.`);
cleanup();
process.exit(1);
}
Expand Down
13 changes: 7 additions & 6 deletions apps/shell-ui/scripts/tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -266,14 +266,15 @@ const shellContractModule = {
}),
},
{
// Integrity check: regenerate the barrels + per-version conformance
// floors from the immutable versions/*.d.ts WITHOUT freezing a new
// version. CI runs this and then `git diff --exit-code` on the generated
// files — a nonzero diff means a floor was hand-edited/dropped to launder
// a removed export past the tsc floors. Nothing is written otherwise.
// RESET to a fresh v0: drops every frozen version and re-mints v0
// from the CURRENT live surface (pre-1.0 policy — intentional
// breaking changes collapse the contract history). CI pairs this
// with `git diff --exit-code` on the generated files: a committed
// contract that disagrees with the live surface — including a
// hand-edited floor or version file — fails the diff.
name: 'shell:regen',
action: () => ({
description: 'Regenerate shell-api barrels + floors from frozen versions (no new version)',
description: 'Recreate the v0 shell-api freeze from the live surface (contract reset)',
steps: ['client-typescript:build', 'shell:regen-run'],
}),
},
Expand Down
68 changes: 11 additions & 57 deletions apps/shell-ui/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ import { usePolling } from './hooks/usePolling';
import { useDashboardData } from './hooks/useDashboardData';
import { useConnectionStatus } from './hooks/useConnectionStatus';
import { useShellApiConfig } from './connection/ShellApiConfigContext';
import { useShellEvents } from './views/useShellEvents';
import { useIframeBridge } from './hooks/useIframeBridge';
import { useAppComponent } from './lib/useAppComponent';
import { useSidebarContent } from './components/layout/HostChromeContext';

Expand Down Expand Up @@ -106,33 +106,14 @@ import ConfirmDialog from './components/layout/ConfirmDialog';
import { PopupRow } from 'shared/components/PopupRow';

// Shell-owned overlay pages
import AccountPage from './views/account/AccountPage';
import SettingsPage from './views/settings/SettingsPage';
import AccountProvider from './providers/AccountProvider';
import SettingsProvider from './providers/SettingsProvider';

// Icons — a DELIBERATE subset of ./icons/BoxIcon: only the glyphs shell chrome
// itself renders are part of the frozen contract. Apps needing other icons take
// them from 'shared' (the full set), which is not contract-bound; every name
// added here is frozen forever, so the surface grows only on demonstrated need.
import {
BxPlus,
BxEditAlt,
BxTrash,
BxDesktop,
BxGridAlt,
BxCog,
BxListUl,
BxStop,
BxPlay,
BxHome,
BxNote,
BxComponent,
BxUser,
BxRocket,
BxLockOpen,
BxPurchaseTag,
BxChevronRight,
BxFolderOpen,
} from './icons/BoxIcon';
import { BxPlus, BxEditAlt, BxTrash, BxDesktop, BxGridAlt, BxCog, BxListUl, BxStop, BxPlay, BxHome, BxNote, BxComponent, BxUser, BxRocket, BxLockOpen, BxPurchaseTag, BxChevronRight, BxFolderOpen } from './icons/BoxIcon';

// =============================================================================
// TYPE RE-EXPORTS — standalone types apps import from 'shell-ui'
Expand All @@ -149,24 +130,7 @@ import {
// =============================================================================

// Shell component prop contracts + workspace/config types
export type {
ShellAppProps,
ShellSidebarProps,
AppDescriptor,
AppManifestEntry,
ShellConfig,
ShellApiConfig,
WorkspacePrefs,
WorkspaceState,
AppWorkspaceState,
SettingValue,
SettingSchema,
AppConfiguration,
ShellBrandingConfig,
ShellThemeConfig,
ShellThemeOption,
ShellAccountConfig,
} from './workspace/types';
export type { ShellAppProps, ShellSidebarProps, AppDescriptor, AppManifestEntry, ShellConfig, ShellApiConfig, WorkspacePrefs, WorkspaceState, AppWorkspaceState, SettingValue, SettingSchema, AppConfiguration, ShellBrandingConfig, ShellThemeConfig, ShellThemeOption, ShellAccountConfig } from './workspace/types';

// Top-level shell + sidebar component prop types
export type { ShellProps } from './components/layout/Shell';
Expand Down Expand Up @@ -196,28 +160,18 @@ export type { ShellConnectionEventMap as ShellEventMap } from 'shared';
export type { ConnectionStatus, ConnectionMode, IAuthProvider } from 'shared';

// Iframe protocol message types
export type { ShellToIframeMsg, IframeToShellMsg, ShellInitMsg } from './views/ShellIframeProtocol';
export type { ShellToIframeMsg, IframeToShellMsg, ShellInitMsg } from './hooks/iframeBridgeProtocol';

// Document component library standalone types.
// `Documents` itself is captured as a constructor via `shellApi.Documents`;
// these are its standalone helper/model types that apps import directly.
export type {
Editor,
WorkspaceBinding,
Document,
EditorGroup,
SplitOrientation,
DocumentsState,
LayoutNode,
LayoutLeaf,
LayoutSplit,
} from './lib/Documents';
export type { Editor, WorkspaceBinding, Document, EditorGroup, SplitOrientation, DocumentsState, LayoutNode, LayoutLeaf, LayoutSplit } from './lib/Documents';
export type { DocTabsProps } from './lib/DocTabs';
export type { DocSplitLayoutProps } from './lib/DocSplitLayout';
export type { DocExplorerProps, DocExplorerConfig, DocEntry, DocEntryChild, DocEntryStatus } from './lib/DocExplorer';
export type { IVirtualFileSystem } from 'shared/modules/explorer/types';

// ViewMenu declaration types (consumed by shared-ui's PageViewControl/SidebarMenu)
// ViewMenu declaration types (consumed by shared-ui's TabControl/SidebarMenu)
export type { DashboardData } from './hooks/useDashboardData';
export type { ViewMenu, ViewMenuEntry } from 'shared';

Expand All @@ -243,7 +197,7 @@ export const shellApi = {
useWorkspace,
useClient,
useShellEvent,
useShellEvents,
useIframeBridge,
useSubscriptions,
usePolling,
useDashboardData,
Expand Down Expand Up @@ -286,8 +240,8 @@ export const shellApi = {
PopupRow,

// Shell-owned overlay pages
AccountPage,
SettingsPage,
AccountProvider,
SettingsProvider,

// Icons
BxPlus,
Expand Down
Loading
Loading