From e1fde25afdc6006e0424e9e0670a9c82e7bbaabf Mon Sep 17 00:00:00 2001 From: saravanakumardb1 Date: Tue, 10 Mar 2026 18:47:01 -0700 Subject: [PATCH] feat(identity): lock NoteLett product identity across all surfaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - productId: notelett - displayName: NoteLett - domain: notelett.app - iOS bundle: com.bytelyst.notelett - Android bundle: com.notelett.app - backend port: 4016 - token namespace: --nl-* (CSS), NoteLettTheme (native) Rippled through: - shared/product.json (canonical source) - backend package.json, config, cosmos-init, all 10 test files - web package.json, landing page, notes-client test - mobile app.json, package.json, auth screen - docs: PRD, ROADMAP, architecture review, foundations, web/mobile roadmaps - registered in learning_ai_common_plat/products/notelett/ Verification: backend typecheck + 18 tests, web typecheck + 6 tests, mobile typecheck — all pass. --- backend/package.json | 4 ++-- backend/src/lib/config.ts | 2 +- backend/src/lib/cosmos-init.ts | 4 ++-- backend/src/mcp/note-tools.test.ts | 14 ++++++------- .../modules/note-agent-actions/routes.test.ts | 2 +- .../src/modules/note-artifacts/routes.test.ts | 2 +- .../modules/note-relationships/routes.test.ts | 2 +- backend/src/modules/note-tasks/routes.test.ts | 2 +- backend/src/modules/notes/routes.test.ts | 2 +- backend/src/modules/workspaces/routes.test.ts | 2 +- backend/src/server.test.ts | 6 +++--- docs/ARCHITECTURE_REVIEW_AND_REUSE_ROADMAP.md | 2 +- docs/PRD.md | 4 ++-- docs/ROADMAP.md | 2 +- docs/roadmaps/01_FOUNDATIONS_AND_DECISIONS.md | 10 ++++----- docs/roadmaps/03_WEB_ROADMAP.md | 5 +---- docs/roadmaps/04_MOBILE_ROADMAP.md | 4 ++-- mobile/app.json | 10 ++++----- mobile/package.json | 2 +- mobile/src/api/note-agent-actions.ts | 4 ++++ mobile/src/app/auth.tsx | 2 +- shared/product.json | 21 ++++++++++++------- web/package.json | 2 +- web/src/app/page.tsx | 4 ++-- web/src/lib/notes-client.test.ts | 4 ++-- 25 files changed, 63 insertions(+), 55 deletions(-) diff --git a/backend/package.json b/backend/package.json index 6b9df3d..e145f33 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,8 +1,8 @@ { - "name": "@bytelyst-notes/backend", + "name": "@notelett/backend", "version": "0.1.0", "private": true, - "description": "ByteLyst Agentic Notes product-specific backend — notes, workspaces, relationships, tasks, artifacts, agent actions", + "description": "NoteLett product backend — notes, workspaces, relationships, tasks, artifacts, agent actions", "type": "module", "scripts": { "dev": "tsx watch src/server.ts", diff --git a/backend/src/lib/config.ts b/backend/src/lib/config.ts index 7388ad0..c5e3da2 100644 --- a/backend/src/lib/config.ts +++ b/backend/src/lib/config.ts @@ -6,7 +6,7 @@ const envSchema = z.object({ HOST: z.string().default('0.0.0.0'), NODE_ENV: z.enum(['development', 'production', 'test']).default('development'), CORS_ORIGIN: z.string().optional(), - SERVICE_NAME: z.string().default('bytelyst-notes-backend'), + SERVICE_NAME: z.string().default('notelett-backend'), COSMOS_ENDPOINT: z.string().min(1, 'COSMOS_ENDPOINT is required').optional(), COSMOS_KEY: z.string().min(1, 'COSMOS_KEY is required').optional(), COSMOS_DATABASE: z.string().default('bytelyst'), diff --git a/backend/src/lib/cosmos-init.ts b/backend/src/lib/cosmos-init.ts index 5f9156d..b5234fa 100644 --- a/backend/src/lib/cosmos-init.ts +++ b/backend/src/lib/cosmos-init.ts @@ -21,9 +21,9 @@ export async function initCosmosIfNeeded(): Promise { try { await initializeAllContainers(); - process.stdout.write('[bytelyst-notes-backend] Cosmos containers ensured\n'); + process.stdout.write('[notelett-backend] Cosmos containers ensured\n'); } catch (err) { const msg = err instanceof Error ? err.message : String(err); - process.stderr.write(`[bytelyst-notes-backend] Cosmos init failed: ${msg}\n`); + process.stderr.write(`[notelett-backend] Cosmos init failed: ${msg}\n`); } } diff --git a/backend/src/mcp/note-tools.test.ts b/backend/src/mcp/note-tools.test.ts index 36cd31f..2084793 100644 --- a/backend/src/mcp/note-tools.test.ts +++ b/backend/src/mcp/note-tools.test.ts @@ -8,7 +8,7 @@ const { listNotesMock, getNoteMock, createNoteMock, createNoteAgentActionMock } createNoteAgentActionMock: vi.fn(), })); -vi.mock('../lib/product-config.js', () => ({ PRODUCT_ID: 'bytelyst-notes' })); +vi.mock('../lib/product-config.js', () => ({ PRODUCT_ID: 'notelett' })); vi.mock('../modules/notes/repository.js', () => ({ listNotes: listNotesMock, getNote: getNoteMock, @@ -23,7 +23,7 @@ import { NotesExecutableMcpTools, getNotesExecutableMcpTool } from './note-tools const req = { id: 'req_1', headers: {}, - jwtPayload: { sub: 'user_1', role: 'admin', productId: 'bytelyst-notes' }, + jwtPayload: { sub: 'user_1', role: 'admin', productId: 'notelett' }, log: { info: vi.fn(), warn: vi.fn(), @@ -51,7 +51,7 @@ describe('note executable MCP tools', () => { items: [ { id: 'note_1', - productId: 'bytelyst-notes', + productId: 'notelett', workspaceId: 'ws_1', userId: 'user_1', title: 'Draft', @@ -74,7 +74,7 @@ describe('note executable MCP tools', () => { req ); - expect(listNotesMock).toHaveBeenCalledWith('user_1', 'bytelyst-notes', { + expect(listNotesMock).toHaveBeenCalledWith('user_1', 'notelett', { workspaceId: 'ws_1', limit: 10, offset: 0, @@ -87,7 +87,7 @@ describe('note executable MCP tools', () => { items: [ { id: 'note_1', - productId: 'bytelyst-notes', + productId: 'notelett', workspaceId: 'ws_1', userId: 'user_1', title: 'Retention policy', @@ -119,7 +119,7 @@ describe('note executable MCP tools', () => { it('gets a scoped note', async () => { getNoteMock.mockResolvedValue({ id: 'note_1', - productId: 'bytelyst-notes', + productId: 'notelett', workspaceId: 'ws_1', userId: 'user_1', title: 'Note', @@ -180,7 +180,7 @@ describe('note executable MCP tools', () => { expect(createNoteAgentActionMock).toHaveBeenCalledTimes(1); expect(createNoteAgentActionMock).toHaveBeenCalledWith( expect.objectContaining({ - productId: 'bytelyst-notes', + productId: 'notelett', workspaceId: 'ws_1', userId: 'user_1', actorId: 'agent_1', diff --git a/backend/src/modules/note-agent-actions/routes.test.ts b/backend/src/modules/note-agent-actions/routes.test.ts index bcac6b0..e75c33a 100644 --- a/backend/src/modules/note-agent-actions/routes.test.ts +++ b/backend/src/modules/note-agent-actions/routes.test.ts @@ -6,7 +6,7 @@ const { extractAuthMock } = vi.hoisted(() => ({ })); vi.mock('../../lib/auth.js', () => ({ extractAuth: extractAuthMock })); -vi.mock('../../lib/product-config.js', () => ({ PRODUCT_ID: 'bytelyst-notes' })); +vi.mock('../../lib/product-config.js', () => ({ PRODUCT_ID: 'notelett' })); vi.mock('./repository.js', () => ({ listNoteAgentActions: vi.fn(async () => ({ items: [], total: 0 })), getNoteAgentAction: vi.fn(async () => null), diff --git a/backend/src/modules/note-artifacts/routes.test.ts b/backend/src/modules/note-artifacts/routes.test.ts index 2e75925..8482561 100644 --- a/backend/src/modules/note-artifacts/routes.test.ts +++ b/backend/src/modules/note-artifacts/routes.test.ts @@ -6,7 +6,7 @@ const { extractAuthMock } = vi.hoisted(() => ({ })); vi.mock('../../lib/auth.js', () => ({ extractAuth: extractAuthMock })); -vi.mock('../../lib/product-config.js', () => ({ PRODUCT_ID: 'bytelyst-notes' })); +vi.mock('../../lib/product-config.js', () => ({ PRODUCT_ID: 'notelett' })); vi.mock('./repository.js', () => ({ listNoteArtifacts: vi.fn(async () => ({ items: [], total: 0 })), getNoteArtifact: vi.fn(async () => null), diff --git a/backend/src/modules/note-relationships/routes.test.ts b/backend/src/modules/note-relationships/routes.test.ts index ac1c7f9..21cdb57 100644 --- a/backend/src/modules/note-relationships/routes.test.ts +++ b/backend/src/modules/note-relationships/routes.test.ts @@ -6,7 +6,7 @@ const { extractAuthMock } = vi.hoisted(() => ({ })); vi.mock('../../lib/auth.js', () => ({ extractAuth: extractAuthMock })); -vi.mock('../../lib/product-config.js', () => ({ PRODUCT_ID: 'bytelyst-notes' })); +vi.mock('../../lib/product-config.js', () => ({ PRODUCT_ID: 'notelett' })); vi.mock('./repository.js', () => ({ listRelationships: vi.fn(async () => ({ items: [], total: 0 })), createRelationship: vi.fn(async (doc: unknown) => doc), diff --git a/backend/src/modules/note-tasks/routes.test.ts b/backend/src/modules/note-tasks/routes.test.ts index 06df9d7..6f8ab9c 100644 --- a/backend/src/modules/note-tasks/routes.test.ts +++ b/backend/src/modules/note-tasks/routes.test.ts @@ -6,7 +6,7 @@ const { extractAuthMock } = vi.hoisted(() => ({ })); vi.mock('../../lib/auth.js', () => ({ extractAuth: extractAuthMock })); -vi.mock('../../lib/product-config.js', () => ({ PRODUCT_ID: 'bytelyst-notes' })); +vi.mock('../../lib/product-config.js', () => ({ PRODUCT_ID: 'notelett' })); vi.mock('./repository.js', () => ({ listNoteTasks: vi.fn(async () => ({ items: [], total: 0 })), getNoteTask: vi.fn(async () => null), diff --git a/backend/src/modules/notes/routes.test.ts b/backend/src/modules/notes/routes.test.ts index ea80d71..98c2a57 100644 --- a/backend/src/modules/notes/routes.test.ts +++ b/backend/src/modules/notes/routes.test.ts @@ -16,7 +16,7 @@ const { })); vi.mock('../../lib/auth.js', () => ({ extractAuth: extractAuthMock })); -vi.mock('../../lib/product-config.js', () => ({ PRODUCT_ID: 'bytelyst-notes' })); +vi.mock('../../lib/product-config.js', () => ({ PRODUCT_ID: 'notelett' })); vi.mock('./repository.js', () => ({ listNotes: listNotesMock, getNote: getNoteMock, diff --git a/backend/src/modules/workspaces/routes.test.ts b/backend/src/modules/workspaces/routes.test.ts index 21e0e72..d2b6650 100644 --- a/backend/src/modules/workspaces/routes.test.ts +++ b/backend/src/modules/workspaces/routes.test.ts @@ -6,7 +6,7 @@ const { extractAuthMock } = vi.hoisted(() => ({ })); vi.mock('../../lib/auth.js', () => ({ extractAuth: extractAuthMock })); -vi.mock('../../lib/product-config.js', () => ({ PRODUCT_ID: 'bytelyst-notes' })); +vi.mock('../../lib/product-config.js', () => ({ PRODUCT_ID: 'notelett' })); vi.mock('./repository.js', () => ({ listWorkspaces: vi.fn(async () => ({ items: [], total: 0 })), getWorkspace: vi.fn(async () => null), diff --git a/backend/src/server.test.ts b/backend/src/server.test.ts index 68e0a20..e05e4a4 100644 --- a/backend/src/server.test.ts +++ b/backend/src/server.test.ts @@ -17,7 +17,7 @@ vi.mock('@bytelyst/fastify-core', () => ({ })); vi.mock('jose', () => ({ - jwtVerify: vi.fn(async () => ({ payload: { sub: 'user_1', productId: 'bytelyst-notes' } })), + jwtVerify: vi.fn(async () => ({ payload: { sub: 'user_1', productId: 'notelett' } })), })); vi.mock('./modules/note-agent-actions/routes.js', () => ({ noteAgentActionRoutes: vi.fn() })); @@ -30,14 +30,14 @@ vi.mock('./lib/cosmos-init.js', () => ({ initCosmosIfNeeded: initCosmosIfNeededM vi.mock('./lib/datastore.js', () => ({ initDatastore: initDatastoreMock })); vi.mock('./lib/config.js', () => ({ config: { - SERVICE_NAME: 'bytelyst-notes-backend', + SERVICE_NAME: 'notelett-backend', CORS_ORIGIN: '*', PORT: 4016, HOST: '0.0.0.0', JWT_SECRET: 'test-secret', }, })); -vi.mock('./lib/product-config.js', () => ({ DISPLAY_NAME: 'ByteLyst Agentic Notes' })); +vi.mock('./lib/product-config.js', () => ({ DISPLAY_NAME: 'NoteLett' })); describe('server bootstrap', () => { beforeEach(() => { diff --git a/docs/ARCHITECTURE_REVIEW_AND_REUSE_ROADMAP.md b/docs/ARCHITECTURE_REVIEW_AND_REUSE_ROADMAP.md index 0ecbcec..01f3918 100644 --- a/docs/ARCHITECTURE_REVIEW_AND_REUSE_ROADMAP.md +++ b/docs/ARCHITECTURE_REVIEW_AND_REUSE_ROADMAP.md @@ -1,4 +1,4 @@ -# ByteLyst Agentic Notes — Architecture Review, Gap Analysis, and Reuse-First Action Plan +# NoteLett — Architecture Review, Gap Analysis, and Reuse-First Action Plan Version: 1.0 Date: March 10, 2026 diff --git a/docs/PRD.md b/docs/PRD.md index 8047e4f..ec22082 100644 --- a/docs/PRD.md +++ b/docs/PRD.md @@ -1,4 +1,4 @@ -Product Requirements Document: ByteLyst Agentic Notes +Product Requirements Document: NoteLett Version: 2.0 Date: March 10, 2026 @@ -7,7 +7,7 @@ Status: Draft # 1. Product Vision -ByteLyst Agentic Notes is a knowledge product for humans and AI agents to capture, structure, retrieve, and operationalize notes in a shared system of record. +NoteLett is a knowledge product for humans and AI agents to capture, structure, retrieve, and operationalize notes in a shared system of record. This is not just a notes UI with an AI chat panel bolted on. It is a product-specific knowledge application that fits the existing ByteLyst ecosystem: diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 9cb9d08..022b61a 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -1,4 +1,4 @@ -# ByteLyst Agentic Notes — Master Roadmap +# NoteLett — Master Roadmap Version: 2.0 Date: March 10, 2026 diff --git a/docs/roadmaps/01_FOUNDATIONS_AND_DECISIONS.md b/docs/roadmaps/01_FOUNDATIONS_AND_DECISIONS.md index acfa734..20db062 100644 --- a/docs/roadmaps/01_FOUNDATIONS_AND_DECISIONS.md +++ b/docs/roadmaps/01_FOUNDATIONS_AND_DECISIONS.md @@ -60,9 +60,9 @@ Make the product implementation-ready and eliminate ambiguity before parallel co - 2026-03-10 — Phase 0 decisions were partially locked to unblock implementation. - The repo now has active `web/`, `backend/`, and `shared/` surfaces instead of a docs-only structure. - Provisional bootstrap values are in use for implementation: - - product display name: `ByteLyst Agentic Notes` - - provisional `productId`: `bytelyst-notes` - - provisional domain: `notes.bytelyst.app` + - product display name: `NoteLett` + - `productId`: `notelett` + - domain: `notelett.app` - backend port: `4016` - Backend scaffolding now uses the established ByteLyst product-backend pattern: - Fastify 5 @@ -72,8 +72,8 @@ Make the product implementation-ready and eliminate ambiguity before parallel co # Open Questions -- Should the final canonical `productId` remain `bytelyst-notes` or use a shorter ecosystem-standard form? -- Should the final public domain be `notes.bytelyst.app` or another product-specific domain? +- ~~Should the final canonical `productId` remain `bytelyst-notes` or use a shorter ecosystem-standard form?~~ **Resolved:** `notelett` +- ~~Should the final public domain be `notes.bytelyst.app` or another product-specific domain?~~ **Resolved:** `notelett.app` - What is the final operator vs shared-admin boundary for approval and audit review flows? - Does v1 mobile parity include approvals and lightweight editing, or capture/retrieval only? - What token namespace should be requested for shared design-system generation? diff --git a/docs/roadmaps/03_WEB_ROADMAP.md b/docs/roadmaps/03_WEB_ROADMAP.md index cfb426c..5856f9b 100644 --- a/docs/roadmaps/03_WEB_ROADMAP.md +++ b/docs/roadmaps/03_WEB_ROADMAP.md @@ -189,10 +189,7 @@ Stack: Next.js 16 + React 19 + TypeScript # Blockers -- Product identity is still draft-level in the planning docs, so the scaffold currently uses provisional values: - - `ByteLyst Agentic Notes` - - `agentic-notes` - - `4016` as a placeholder notes API port in `.env.example` +- ~~Product identity is still draft-level in the planning docs~~ **Resolved:** product identity locked as NoteLett (`notelett`, port 4016, `notelett.app`) - Backend integration contracts are only partially aligned with the web shell, so some routes still rely on demo auth fallback and client-derived operator/saved-view summaries. # Deferred diff --git a/docs/roadmaps/04_MOBILE_ROADMAP.md b/docs/roadmaps/04_MOBILE_ROADMAP.md index ae6a5e5..84e5b91 100644 --- a/docs/roadmaps/04_MOBILE_ROADMAP.md +++ b/docs/roadmaps/04_MOBILE_ROADMAP.md @@ -96,8 +96,8 @@ Stack: React Native + Expo + TypeScript # Open Questions -- Should the current provisional bootstrap values (`productId: bytelyst-notes`, backend port `4016`) now be treated as final? -- What are the final iOS bundle identifier, Android package name, and URL scheme/domain values? +- ~~Should the current provisional bootstrap values be treated as final?~~ **Resolved:** `productId: notelett`, port 4016, domain `notelett.app` +- ~~What are the final iOS bundle identifier, Android package name, and URL scheme/domain values?~~ **Resolved:** iOS `com.bytelyst.notelett`, Android `com.notelett.app`, scheme `notelett` - Should mobile notes access go directly to the product backend for note CRUD while auth remains on `platform-service`? - Should the current local approval/activity model map directly to backend `note-agent-actions`, or stay as a mobile-specific condensed surface? diff --git a/mobile/app.json b/mobile/app.json index eb43de7..cb4f833 100644 --- a/mobile/app.json +++ b/mobile/app.json @@ -1,18 +1,18 @@ { "expo": { - "name": "ByteLyst Agentic Notes", - "slug": "bytelyst-agentic-notes", + "name": "NoteLett", + "slug": "notelett", "version": "0.1.0", "orientation": "portrait", "userInterfaceStyle": "dark", - "scheme": "bytelyst-notes", + "scheme": "notelett", "plugins": ["expo-router"], "ios": { "supportsTablet": true, - "bundleIdentifier": "com.bytelyst.agenticnotes" + "bundleIdentifier": "com.bytelyst.notelett" }, "android": { - "package": "com.bytelyst.agenticnotes" + "package": "com.notelett.app" }, "web": { "bundler": "metro" diff --git a/mobile/package.json b/mobile/package.json index 4d17942..7fa01a3 100644 --- a/mobile/package.json +++ b/mobile/package.json @@ -1,5 +1,5 @@ { - "name": "bytelyst-agentic-notes-mobile", + "name": "@notelett/mobile", "version": "0.1.0", "private": true, "main": "index.ts", diff --git a/mobile/src/api/note-agent-actions.ts b/mobile/src/api/note-agent-actions.ts index 68d888b..5ec87be 100644 --- a/mobile/src/api/note-agent-actions.ts +++ b/mobile/src/api/note-agent-actions.ts @@ -12,6 +12,8 @@ export type MobileApprovalItem = { export type MobileActivityItem = { id: string; + workspaceId: string; + noteId: string; title: string; summary: string; kind: 'note' | 'task' | 'agent'; @@ -71,6 +73,8 @@ function toActivityKind(actionType: NoteAgentActionDoc['actionType']): MobileAct function toActivityItem(action: NoteAgentActionDoc): MobileActivityItem { return { id: action.id, + workspaceId: action.workspaceId, + noteId: action.noteId, title: action.afterSummary ?? `${action.actionType.replaceAll('_', ' ')} update`, summary: action.reason ?? action.afterSummary ?? `State: ${action.state}`, kind: toActivityKind(action.actionType), diff --git a/mobile/src/app/auth.tsx b/mobile/src/app/auth.tsx index 10e8fb4..c40fa4c 100644 --- a/mobile/src/app/auth.tsx +++ b/mobile/src/app/auth.tsx @@ -13,7 +13,7 @@ export default function AuthScreen() { return ( - ByteLyst Agentic Notes + NoteLett Mobile MVP auth shell Backend-backed web surface

- ByteLyst Agentic Notes web surface + NoteLett

- The web app now exposes backend-backed dashboard, workspace, search, review, and note detail flows with authenticated ByteLyst client wiring. + Structured notes workspace for humans and agents with search, review, and operational context.

diff --git a/web/src/lib/notes-client.test.ts b/web/src/lib/notes-client.test.ts index 2d1ca0e..ac75f8d 100644 --- a/web/src/lib/notes-client.test.ts +++ b/web/src/lib/notes-client.test.ts @@ -68,7 +68,7 @@ describe("getNoteDetail", () => { artifactType: "file", title: "Launch brief.pdf", description: "Ready for review", - blobPath: "bytelyst-notes/user-1/launch-brief.pdf", + blobPath: "notelett/user-1/launch-brief.pdf", contentType: "application/pdf", sizeBytes: 2048, }, @@ -141,7 +141,7 @@ describe("getNoteDetail", () => { name: "Launch brief.pdf", type: "file", status: "ready", - blobPath: "bytelyst-notes/user-1/launch-brief.pdf", + blobPath: "notelett/user-1/launch-brief.pdf", contentType: "application/pdf", sizeBytes: 2048, },