While migrating CreateNoteModal to use @bytelyst/ui Input/Select/Textarea
(which internally call React.useId), Vitest tests failed with:
TypeError: Cannot read properties of null (reading 'useId')
Root cause: the web package pins react@19.2.0 but @bytelyst/ui declared
react: '^19.0.0' as a peer, so pnpm resolved 19.2.6 for it from the
common-platform side. Two React copies coexisted (19.2.0 and 19.2.6),
the @bytelyst/ui components linked against one and react-dom test-rendered
against the other, and useId failed because the dispatcher belonged to
a different React instance than the consumer.
Fix: declare pnpm.overrides in the workspace root so the entire monorepo
resolves to a single react@19.2.0 / react-dom@19.2.0 pair. Verified via
'pnpm why react' (all transitive references now point at 19.2.0) and the
on-disk symlinks (web/node_modules/@bytelyst/ui/node_modules/react and
common-plat/packages/ui/node_modules/react both link to
.pnpm/react@19.2.0).
Three mechanical lint warnings in the web package are resolved with
zero behavior change:
- web/src/app/(app)/notes/[noteId]/page.tsx — rename onTagsAccepted
callback param to '_tags' to match the no-unused-vars allowlist
(the param is intentionally unused; we trigger a re-save regardless).
- web/src/lib/feedback-client.ts — drop the unused PRODUCT_ID import.
- web/src/lib/notes-client.ts — delete the dead toWorkspaceSummary()
helper. Workspace summaries are produced by listWorkspaceSummaries()
on the backend response now; the local helper had no callers.
Web lint goes from 23 → 20 warnings. Remaining 20 are React-compiler
advisories about setState-in-effect patterns; those require careful
per-component refactoring (useReducer, derive-from-props, or
startTransition) and are tracked under Sprint D / Q1 tech debt rather
than fixed mechanically.
The audit script silently passed on hosts without ripgrep installed
because 'rg -n ...' would fail, '|| true' swallowed the failure,
'matches' would be empty, and report() would print 'ok: no matches'.
This hid genuine UI drift from local 'pnpm run audit:ui' runs.
Changes:
- Detect ripgrep availability at startup and emit a stderr note when
falling back.
- Add a grep-based fallback that translates rg '--glob !path' exclusions
into 'grep --exclude=<basename>' so caller-side exclusions (e.g. the
@bytelyst/ui adapter file at Primitives.tsx) still apply.
- Guard the optional 'extra_excludes' array expansion against 'set -u'
when no exclusions are configured.
Result: on this host (no rg) the audit now correctly reports
2 categories with matches — raw interactive controls and legacy global
surface classes — instead of the false 'all green' it produced before.
'pnpm run audit:ui:strict' exits non-zero when matches remain, ready to
wire into CI once UI5–UI8 finish migrating the remaining call sites.
Sprint C / UI5 — migrate the highest-leverage user-facing forms off the
legacy 'input-shell' / inline-style pattern onto the @bytelyst/ui Input,
Textarea, and AlertBanner primitives via the local Primitives.tsx adapter.
Adapter additions (web/src/components/ui/Primitives.tsx):
- Re-export AlertBanner, FormSection, and FieldGrid from @bytelyst/ui so
product code never imports from the underlying package directly.
Migrated screens:
- web/src/app/(auth)/login/page.tsx
- web/src/app/(auth)/register/page.tsx
- web/src/app/(auth)/forgot-password/page.tsx
- web/src/components/CreateWorkspaceModal.tsx
Each migration replaces the ad-hoc 'input-shell' inputs and manual
label/error/success divs with the Input (label + hint props), Textarea,
and AlertBanner (tone='error'|'success') primitives. Inline style blocks
are replaced with Tailwind utility classes that read from the existing
--nl-* CSS custom properties so the visual tokens remain unchanged.
The 3 auth pages alone remove 9 input-shell call sites; the
CreateWorkspaceModal removes 2 more.
Verified:
- pnpm --filter @notelett/web run typecheck: passes
- pnpm --filter @notelett/web run test: 96/96 pass
- pnpm run verify: end-to-end green (backend 380/380, web 96/96, mobile 97/97)
Sprint B — closes audit item B7 (doc consolidation).
- docs/AGENT_TASK_ROADMAP.md, docs/ARCHITECTURE_REVIEW_AND_REUSE_ROADMAP.md,
docs/GAP_ANALYSIS.md were each self-marked as historical snapshots
but kept polluting the top of docs/. Moved them under docs/archive/
in the previous commit; this commit:
- Adds docs/archive/README.md explaining what's archived vs active
- Repoints cross-doc links in docs/IMPLEMENTATION_TRACKER.md,
docs/WEB_AI_FAST_ROADMAP.md, and docs/roadmaps/*.md to the new
archive paths
- Fixes relative links inside the archived files themselves so
historical readers can still navigate back to active docs
- AGENTS.md §1.1 refreshed: reflects the May 22 re-verified state
(382/96/97 tests), links the two new runbooks, and points readers
away from docs/archive/ as a work source.
Sprint B — closes audit items B4 and B5.
- docs/runbooks/MEK_ROTATION.md: step-by-step procedure for rotating
the field-encrypt master key in Azure Key Vault, including pre-flight
checks, rewrapAllDeks usage, verification queries, rollback, and lost-MEK
recovery. Replaces the previous gap where MEK rotation had no
documented operator path.
- docs/runbooks/SECRET_MANAGEMENT.md: inventory of every secret consumed
by NoteLett with its production source (AKV), two production-grade
patterns (workload identity vs K8s CSI), the compose-host pattern,
rotation flow per secret type, verification commands, and red-flag
triage.
Both docs cross-link each other and call out concrete open items
(automation, dual-JWT support, audit-log emission) for later sprints
rather than overstating current capabilities.
Sprint B — closes audit items B6 (event-bus completeness) and B3
(public-share revocation regression).
Event bus:
- note-tasks/repository.ts createNoteTask now emits task.created with
taskId, noteId, workspaceId, userId, title
- workspaces/repository.ts createWorkspace now emits workspace.created
with workspaceId, userId, name
The event-bus already declared these event types (event-bus.ts) and
webhook subscribers can target them, but they were never emitted —
making the contract dead. Emissions follow the same .catch(() => {})
pattern used by note.created/updated/deleted in notes/repository.ts so
a subscriber failure cannot break the create flow.
Regression tests:
- note-tasks/repository.test.ts and workspaces/repository.test.ts
exercise the emission paths end-to-end through the in-memory
datastore.
- note-shares/repository.integration.test.ts adds a 5-test integration
suite for the public-share revocation path: token resolves before
revocation; token returns null after deleteShare (hard delete);
expired token returns null; cross-product token rejected;
listSharesForNote does not include revoked shares.
Verified:
- pnpm --filter @notelett/backend run test: 380/380 (was 373, +7 new)
- pnpm run verify end-to-end green
- Commit previously untracked docs/NEXT_SPRINT_ROADMAP.md with refreshed
May 22 status; mark Sprint 1 (backend build) and Sprint 2 (lint) as
resolved by Sprint A workspace-path fix
- Add post-Sprint-A re-verification section to
docs/PRODUCTION_READINESS_HANDOFF_ROADMAP.md documenting the
workspace-path regression and the re-verified gates
- Update README quick-start to reference the canonical common-platform
checkout path with BYTELYST_COMMON_PLAT_ROOT override note
Restores green build after the May 12 Docker/UI regression.
Root cause: pnpm-workspace.yaml referenced a sibling path
(../learning_ai/learning_ai_common_plat/...) that did not exist on
dev/CI hosts. .pnpmfile.cjs fell back to ../learning_ai_common_plat for
some packages but missed others, so @bytelyst/ui was pulled from a
stale Gitea 0.1.0 tarball with zero exports (breaking web typecheck +
26 tests) and @bytelyst/monitoring was never linked into node_modules
(breaking backend typecheck + 2 test suites).
Changes:
- pnpm-workspace.yaml now references ../learning_ai_common_plat/packages/* directly
- .pnpmfile.cjs swaps DEFAULT/LEGACY common-plat roots so the canonical
path is the default and the older nested path is the fallback
- scripts/docker-prep.sh, scripts/local-smoke.sh, scripts/release-guard-audit.sh
follow the same canonical-first / legacy-fallback pattern
- .github/workflows/ci.yml symlinks directly to ../learning_ai_common_plat
- pnpm-lock.yaml regenerated with @bytelyst/ui@0.1.9 and
@bytelyst/monitoring@0.1.5 linked to the local common-plat checkout
Verified:
- pnpm run verify: backend 373/373, web 96/96, mobile 97/97
- pnpm run audit:release-guards: passes
- backend, web, mobile lint all exit 0 (advisory warnings retained)
- Fixed NEXT_PUBLIC_NOTES_API_URL to use public API endpoint
- Updated docker-compose.yml environment format to proper YAML
- Updated Dockerfiles to remove Gitea secrets and use .docker-deps
- Added docker-prep.sh script for dependency packaging
- Changed NODE_ENV back to development for compatibility with memory DB
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The base image approach is too complex for the current pnpm workspace structure.
Products cannot easily use the base image's workspace because pnpm expects all
workspace packages to be present during install. Reverting to the proven
docker-prep.sh tarball approach for now.
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The base image only includes production dependencies, so we need to install
all dependencies (including devDependencies) in the builder stage to have
TypeScript and Next.js available for building.
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Update Dockerfiles to use bytelyst-common-base-backend and bytelyst-common-base-web
images instead of installing @bytelyst/* packages via tarballs.
Benefits:
- Smaller final images (~50MB vs ~250MB)
- Faster builds (base image cached)
- Consistent package versions across products
- No need for docker-prep.sh tarball packing
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>