Release 0.2.21; track the StartOS version files

Cut version 0.2.21 (current = v_0_2_21) with the adjudicated DO-item fixes.
Also commits the previously-untracked v_0_2_* version files that
versions/index.ts imports (ROADMAP item D2), so a fresh checkout builds
without dangling imports. The missing v_0_2_2 is intentional (versions are
an unordered set).

Built and verified: ten31portal_x86_64.s9pk packs cleanly at 0.2.21:0,
arch x86_64, SDK 0.4.0.
This commit is contained in:
Jonathan Kirkwood
2026-07-01 13:39:56 -05:00
parent 8247c28243
commit 7fc78d7058
23 changed files with 308 additions and 3 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "ten31portal-startos", "name": "ten31portal-startos",
"version": "0.2.20", "version": "0.2.21",
"private": true, "private": true,
"scripts": { "scripts": {
"build": "npm run check && rm -rf ./javascript && ncc build startos/index.ts -o ./javascript", "build": "npm run check && rm -rf ./javascript && ncc build startos/index.ts -o ./javascript",
+23 -2
View File
@@ -1,2 +1,23 @@
export { v_0_1_0 as current } from './v_0_1_0' export { v_0_2_21 as current } from './v_0_2_21'
export const other = [] import { v_0_1_0 } from './v_0_1_0'
import { v_0_2_0 } from './v_0_2_0'
import { v_0_2_1 } from './v_0_2_1'
import { v_0_2_3 } from './v_0_2_3'
import { v_0_2_4 } from './v_0_2_4'
import { v_0_2_5 } from './v_0_2_5'
import { v_0_2_6 } from './v_0_2_6'
import { v_0_2_7 } from './v_0_2_7'
import { v_0_2_8 } from './v_0_2_8'
import { v_0_2_9 } from './v_0_2_9'
import { v_0_2_10 } from './v_0_2_10'
import { v_0_2_11 } from './v_0_2_11'
import { v_0_2_12 } from './v_0_2_12'
import { v_0_2_13 } from './v_0_2_13'
import { v_0_2_14 } from './v_0_2_14'
import { v_0_2_15 } from './v_0_2_15'
import { v_0_2_16 } from './v_0_2_16'
import { v_0_2_17 } from './v_0_2_17'
import { v_0_2_18 } from './v_0_2_18'
import { v_0_2_19 } from './v_0_2_19'
import { v_0_2_20 } from './v_0_2_20'
export const other = [v_0_1_0, v_0_2_0, v_0_2_1, v_0_2_3, v_0_2_4, v_0_2_5, v_0_2_6, v_0_2_7, v_0_2_8, v_0_2_9, v_0_2_10, v_0_2_11, v_0_2_12, v_0_2_13, v_0_2_14, v_0_2_15, v_0_2_16, v_0_2_17, v_0_2_18, v_0_2_19, v_0_2_20]
@@ -0,0 +1,16 @@
import { VersionInfo } from '@start9labs/start-sdk'
export const v_0_2_0 = VersionInfo.of({
version: '0.2.0:0',
releaseNotes: {
en_US:
'Investor and fund-administrator accounts. Per-entity access control, built-in document storage (statements, K-1s), and per-investor capital-account statements. Admin screens to create accounts, grant entity access, upload documents, and enter capital-account figures.',
},
migrations: {
// New tables (entity_access, documents, capital_account_statements) and the
// users.username column are applied by Alembic on app startup. No StartOS-level
// data migration is required.
up: async ({ effects }) => {},
down: async ({ effects }) => {},
},
})
@@ -0,0 +1,15 @@
import { VersionInfo } from '@start9labs/start-sdk'
export const v_0_2_1 = VersionInfo.of({
version: '0.2.1:0',
releaseNotes: {
en_US:
'Owner access grid (accounts by fund/SPV, click to grant or revoke access) and a capital-account importer that reads a fund-administrator spreadsheet, matches or creates investor accounts on review, grants entity access, and loads each investor\'s capital-account balance.',
},
migrations: {
// No schema changes beyond 0.2.0; new features reuse existing tables. Alembic
// remains the source of truth for the database schema on app startup.
up: async ({ effects }) => {},
down: async ({ effects }) => {},
},
})
@@ -0,0 +1,14 @@
import { VersionInfo } from '@start9labs/start-sdk'
export const v_0_2_10 = VersionInfo.of({
version: '0.2.10:0',
releaseNotes: {
en_US:
'One unified Import: a single eNAV upload loads fund holdings/NAV and members + capital (commitment, paid-in, distributions, current value). Investor view now shows commitment and DPI alongside current value. Menu cleanup: one Import item, Audit Log moved to the bottom.',
},
migrations: {
// Adds capital_account_statements.commitment_cents; applied by Alembic on startup.
up: async ({ effects }) => {},
down: async ({ effects }) => {},
},
})
@@ -0,0 +1,14 @@
import { VersionInfo } from '@start9labs/start-sdk'
export const v_0_2_11 = VersionInfo.of({
version: '0.2.11:0',
releaseNotes: {
en_US:
'Linked investor logins: an investor who invests under several names (e.g. an IRA and a trust, or through different Partners) can be given one sign-on that shows every name\'s investments together, labeled by name. Set it in Users → Manage → Login. Also: sortable columns on the Entities view.',
},
migrations: {
// Adds users.primary_account_id; applied by Alembic on startup.
up: async ({ effects }) => {},
down: async ({ effects }) => {},
},
})
@@ -0,0 +1,13 @@
import { VersionInfo } from '@start9labs/start-sdk'
export const v_0_2_12 = VersionInfo.of({
version: '0.2.12:0',
releaseNotes: {
en_US:
'Clearnet performance: gzip compression on all responses (the ~320KB app bundle drops to ~90KB over the wire) and long-lived browser caching of content-hashed assets, so repeat visits are near-instant. No data changes.',
},
migrations: {
up: async ({ effects }) => {},
down: async ({ effects }) => {},
},
})
@@ -0,0 +1,13 @@
import { VersionInfo } from '@start9labs/start-sdk'
export const v_0_2_13 = VersionInfo.of({
version: '0.2.13:0',
releaseNotes: {
en_US:
'Re-importing a NAV now updates that quarter in place instead of adding a second round (no more doubled totals on the Entities view). New Ten31 Portal branding and logo.',
},
migrations: {
up: async ({ effects }) => {},
down: async ({ effects }) => {},
},
})
@@ -0,0 +1,14 @@
import { VersionInfo } from '@start9labs/start-sdk'
export const v_0_2_14 = VersionInfo.of({
version: '0.2.14:0',
releaseNotes: {
en_US:
'User administration from StartOS Actions: Create User, Reset Password, List Users, and Delete User. New role layout — Managing Partner (full access incl. sign-off), Operations (full access except sign-off), Fund Admin. The built-in Service Admin can be reset but never deleted.',
},
migrations: {
// Adds users.is_service_admin and flags the bootstrap admin; applied by Alembic on startup.
up: async ({ effects }) => {},
down: async ({ effects }) => {},
},
})
@@ -0,0 +1,13 @@
import { VersionInfo } from '@start9labs/start-sdk'
export const v_0_2_15 = VersionInfo.of({
version: '0.2.15:0',
releaseNotes: {
en_US:
'Fixes doubled "Invested" totals by removing duplicate holdings/positions left by older imports (runs automatically on the next import of a fund, or all at once via the new "Fix Duplicate Holdings" action). Usernames are now editable in Users → Manage. Mobile/PWA: installable to a phone home screen with an app icon, plus a responsive layout with a collapsible menu.',
},
migrations: {
up: async ({ effects }) => {},
down: async ({ effects }) => {},
},
})
@@ -0,0 +1,13 @@
import { VersionInfo } from '@start9labs/start-sdk'
export const v_0_2_16 = VersionInfo.of({
version: '0.2.16:0',
releaseNotes: {
en_US:
'Clear and rebuild a fund: re-import with the new "Replace existing holdings" option to wipe a fund\'s old positions and rebuild from the file — for when a source change (e.g. Carta → eNAV) renamed every position. Also available as the "Reset Fund Holdings" and "List Funds" actions. Investor capital accounts are never affected.',
},
migrations: {
up: async ({ effects }) => {},
down: async ({ effects }) => {},
},
})
@@ -0,0 +1,13 @@
import { VersionInfo } from '@start9labs/start-sdk'
export const v_0_2_17 = VersionInfo.of({
version: '0.2.17:0',
releaseNotes: {
en_US:
'The Entities view now shows a Committed column (total LP commitments to each fund) alongside Invested (capital deployed into companies) and Last Signed Value.',
},
migrations: {
up: async ({ effects }) => {},
down: async ({ effects }) => {},
},
})
@@ -0,0 +1,13 @@
import { VersionInfo } from '@start9labs/start-sdk'
export const v_0_2_18 = VersionInfo.of({
version: '0.2.18:0',
releaseNotes: {
en_US:
'The fund Partners tab now shows each member\'s Committed, Paid-in, and Distributions alongside their capital value, with a total committed for the fund — matching what investors see in their own portal.',
},
migrations: {
up: async ({ effects }) => {},
down: async ({ effects }) => {},
},
})
@@ -0,0 +1,13 @@
import { VersionInfo } from '@start9labs/start-sdk'
export const v_0_2_19 = VersionInfo.of({
version: '0.2.19:0',
releaseNotes: {
en_US:
'Mobile/PWA fixes: the top bar (including Sign out) no longer hides behind the phone status bar, and the Entities view now shows each fund as a card that fits the screen — no more zooming out or sideways scrolling.',
},
migrations: {
up: async ({ effects }) => {},
down: async ({ effects }) => {},
},
})
@@ -0,0 +1,13 @@
import { VersionInfo } from '@start9labs/start-sdk'
export const v_0_2_20 = VersionInfo.of({
version: '0.2.20:0',
releaseNotes: {
en_US:
'Passwords now persist when you edit an investor (the password only changes if you type a new one). Investors can change their own password from the portal, and admins keep the ability to reset it. Every password field has a show/hide eye toggle.',
},
migrations: {
up: async ({ effects }) => {},
down: async ({ effects }) => {},
},
})
@@ -0,0 +1,13 @@
import { VersionInfo } from '@start9labs/start-sdk'
export const v_0_2_21 = VersionInfo.of({
version: '0.2.21:0',
releaseNotes: {
en_US:
'Importing with replace-existing-holdings now asks for confirmation before clearing a fund, and shows step-by-step progress. You can expand an audit log entry to see its full detail. Entities with no investments show a clear empty state. A brief server hiccup shows a retry prompt instead of logging you out. Backend adds document upload size limits, database indexes, and an automated test suite.',
},
migrations: {
up: async ({ effects }) => {},
down: async ({ effects }) => {},
},
})
@@ -0,0 +1,14 @@
import { VersionInfo } from '@start9labs/start-sdk'
export const v_0_2_3 = VersionInfo.of({
version: '0.2.3:0',
releaseNotes: {
en_US:
'Replace the Carta importer with a fund-administrator eNAV importer. Reads the holdings (HLD) sheet into holdings, positions, and a quarter valuation; decrypts password-protected workbooks (password entered on upload); reads the report date from the sheet; accepts XLSX or CSV.',
},
migrations: {
// No schema changes; import is parsing-only. Alembic owns the DB schema on startup.
up: async ({ effects }) => {},
down: async ({ effects }) => {},
},
})
@@ -0,0 +1,14 @@
import { VersionInfo } from '@start9labs/start-sdk'
export const v_0_2_4 = VersionInfo.of({
version: '0.2.4:0',
releaseNotes: {
en_US:
'Import fund members from the eNAV ALLOC SI tab. The capital-account import now reads the investor roster, matches existing members by fund-administrator investor ID (idempotent re-imports), offers to create new members (without a login until you set a password), grants entity access, and loads each member\'s capital balance.',
},
migrations: {
// Adds users.external_investor_id; applied by Alembic on startup.
up: async ({ effects }) => {},
down: async ({ effects }) => {},
},
})
@@ -0,0 +1,14 @@
import { VersionInfo } from '@start9labs/start-sdk'
export const v_0_2_5 = VersionInfo.of({
version: '0.2.5:0',
releaseNotes: {
en_US:
'Fund page Partners and Documents tabs. Partners lists each member with their investor ID, login status (members imported without a password show "No login yet" until you set one), and latest capital value. Documents shows all of the fund\'s files for admins.',
},
migrations: {
// Adds users.login_enabled; applied by Alembic on startup.
up: async ({ effects }) => {},
down: async ({ effects }) => {},
},
})
@@ -0,0 +1,13 @@
import { VersionInfo } from '@start9labs/start-sdk'
export const v_0_2_6 = VersionInfo.of({
version: '0.2.6:0',
releaseNotes: {
en_US:
'Fund page Partners and Documents tabs: Partners lists each member with investor ID, login status (members imported without a password show "No login yet"), and latest capital value; Documents shows all of the fund\'s files for admins.',
},
migrations: {
up: async ({ effects }) => {},
down: async ({ effects }) => {},
},
})
@@ -0,0 +1,13 @@
import { VersionInfo } from '@start9labs/start-sdk'
export const v_0_2_7 = VersionInfo.of({
version: '0.2.7:0',
releaseNotes: {
en_US:
'Fund page Partners and Documents tabs (each partner with their capital value in the fund), plus a sidebar header alignment fix so the top-left borders meet cleanly.',
},
migrations: {
up: async ({ effects }) => {},
down: async ({ effects }) => {},
},
})
@@ -0,0 +1,13 @@
import { VersionInfo } from '@start9labs/start-sdk'
export const v_0_2_8 = VersionInfo.of({
version: '0.2.8:0',
releaseNotes: {
en_US:
'Serve the web app with no-cache on index.html so upgrades show up immediately without a manual browser refresh. Includes the fund Partners/Documents tabs and the sidebar header alignment fix.',
},
migrations: {
up: async ({ effects }) => {},
down: async ({ effects }) => {},
},
})
@@ -0,0 +1,13 @@
import { VersionInfo } from '@start9labs/start-sdk'
export const v_0_2_9 = VersionInfo.of({
version: '0.2.9:0',
releaseNotes: {
en_US:
'Show the running app version in the UI (sidebar / portal header) so updates are verifiable at a glance. Includes the fund Partners/Documents tabs, sidebar alignment fix, and no-cache on the web app.',
},
migrations: {
up: async ({ effects }) => {},
down: async ({ effects }) => {},
},
})