Skip to content

Expand structured logging across client and API usage flows#5

Merged
2witstudios merged 1 commit intomasterfrom
codex/optimize-production-logging-practices
Sep 24, 2025
Merged

Expand structured logging across client and API usage flows#5
2witstudios merged 1 commit intomasterfrom
codex/optimize-production-logging-practices

Conversation

@2witstudios
Copy link
Owner

Summary

  • introduce a browser-safe structured logging helper and shared identifier masking utility for consistent client telemetry
  • replace raw console statements in auth fetch, usage counter, layout store, sidebar, and mention config with masked structured logs
  • harden usage tracking and admin instrumentation to mask identifiers, eliminate console noise, and route events through structured loggers across API routes and AI utilities

Testing

  • pnpm --filter web lint

https://chatgpt.com/codex/tasks/task_e_68d31a1667208320a446d987b320fc63

@2witstudios 2witstudios merged commit 33ee50d into master Sep 24, 2025
@2witstudios 2witstudios deleted the codex/optimize-production-logging-practices branch October 17, 2025 19:57
2witstudios added a commit that referenced this pull request Dec 22, 2025
- Wrap switch case declarations in blocks to prevent variable leakage
- Add isActivityEligibleForRollback() helper for DRY rollback checks
- Export helper for use in history API routes

Addresses PR #118 review issues #5, #13

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2witstudios added a commit that referenced this pull request Dec 23, 2025
Fixes Issues #5 and #7 from PR #118 review:

Context determination (#5):
- Align executeAiUndo context logic with previewAiUndo
- Use 'ai_tool' context for pages (all activities here are AI-generated)
- Simplifies from switch statement to direct assignment with drive check

Transaction atomicity (#7):
- Wrap rollbacks AND message deletion in single transaction
- Pass transaction to executeRollback for atomic operations
- If any operation fails, entire undo is rolled back

This ensures users don't end up in inconsistent states where messages
are deleted but only some changes were reverted.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2witstudios added a commit that referenced this pull request Dec 23, 2025
* feat: add version history and rollback functionality

Implement comprehensive version history browsing and rollback capabilities
for PageSpace, allowing users to restore resources to previous states.

## Schema Changes
- Add 'rollback' operation to activity_operation enum
- Add rollbackFromActivityId and contentFormat fields to activity_logs
- Create retention_policies table for plan-based history retention

## Core Features
- RBAC-based rollback permissions (edit access = rollback access)
- Resource-specific rollback handlers (pages, drives, agents, etc.)
- Plan-based retention limits (7/30/90/unlimited days)
- Rollback creates new activity entry (history never erased)

## API Endpoints
- GET /api/activities/[activityId] - Single activity with rollback eligibility
- POST /api/activities/[activityId]/rollback - Execute rollback
- GET /api/pages/[pageId]/history - Page version history
- GET /api/drives/[driveId]/history - Drive version history (admin)

## UI Components
- VersionHistoryPanel - Slide-out panel with timeline and filters
- VersionHistoryItem - Activity item with "Restore" action
- RollbackConfirmDialog - Confirmation modal with warnings

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: complete rollback handler implementations and fix lint errors

- Fully implement rollbackPermissionChange for grant/revoke/update operations
- Fully implement rollbackMemberChange for add/remove/role change operations
- Fully implement rollbackRoleChange for create/delete/update operations
- Fix lint errors: remove unused imports, fix useEffect dependencies
- Add package exports for @pagespace/lib/permissions and @pagespace/lib/monitoring
- Fix useToast import path to use correct hook location

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat: add undo AI changes feature for conversations

Add ability to undo AI changes from a specific message point:
- Preview what will be affected before undoing
- Two modes: revert conversation only OR revert with all tool changes
- Activity logging for audit compliance

New files:
- ai-undo-service.ts: preview and execute functions
- UndoAiChangesDialog.tsx: confirmation dialog with mode selection
- /api/ai/chat/messages/[messageId]/undo: GET preview, POST execute

Changes:
- Add conversation_undo operations to activity schema
- Add message rollback handler to rollback-service
- Wire undo button to MessageActionButtons and MessageRenderer

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: add missing radio-group UI component

Required for UndoAiChangesDialog mode selection.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: add @radix-ui/react-radio-group dependency

Required for radio-group UI component used in UndoAiChangesDialog.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: restore correct @electron/node-gyp resolution in lockfile

The previous lockfile had a malformed git SSH URL that broke CI:
- Before: git+https://git@github.com:electron/node-gyp.git (broken)
- After: https://codeload.github.com/electron/node-gyp/tar.gz/... (works)

Restored lockfile and re-ran pnpm install to properly resolve dependencies.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: add critical rules to CLAUDE.md and fix retention tier order

- Add CRITICAL rules for package manager (always pnpm, never npm)
- Add CRITICAL rules for database migrations (never manually edit)
- Fix retention days: founder=90, business=unlimited (was swapped)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(db): add missing default and rollback index

- Add DEFAULT now() for retention_policies.updatedAt column
- Add index on activity_logs.rollbackFromActivityId for queries
- Create migration 0027_fix_retention_updated_at.sql

Addresses PR #118 review issues #10, #20

* fix(lib/permissions): add block scoping and eligibility helper

- Wrap switch case declarations in blocks to prevent variable leakage
- Add isActivityEligibleForRollback() helper for DRY rollback checks
- Export helper for use in history API routes

Addresses PR #118 review issues #5, #13

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(lib/monitoring): add rollback fields to ActivityLogInput

- Add contentFormat field for content type tracking
- Add rollbackFromActivityId for rollback chain references
- Pass fields as top-level properties to logActivity

Addresses PR #118 review issue #3

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(api): add authorization and resourceType filtering

- Add permission check before returning activity details
- Implement resourceType query parameter filtering in history routes
- Use shared isActivityEligibleForRollback helper
- Add case-insensitive resource type matching

Addresses PR #118 review issues #6, #7, #13

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(services): improve type safety and add transactions

- Use Drizzle count() instead of .length for efficient queries
- Add operation validation against known valid operations
- Fix unsafe null type assertion in RollbackPreview
- Re-export RollbackContext type for API route usage
- Wrap AI undo mutations in database transaction
- Use switch statement for context determination

Addresses PR #118 review issues #2, #16, #18, #19, #23

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ui): add CSRF protection and improve error handling

- Use post() with CSRF token for rollback requests
- Add error toast for failed preview fetch
- Handle non-JSON error responses gracefully
- Fix non-existent postWithAuth import to use post
- Remove re-throw after toast notification
- Fix redundant ternary for buttonSize

Addresses PR #118 review issues #1, #8, #11, #17, #22, NEW

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(db): address schema issues from PR #118 review

Critical & Major fixes:
- Add missing .defaultNow() to retention_policies.updatedAt
  (fixes schema/migration mismatch that would cause insert failures)
- Add contentFormatEnum for type-safe content format validation
  (prevents invalid values during rollback parsing)
- Add CHECK constraint: retentionDays >= -1 (where -1 = unlimited)
- Add rollback source snapshot fields for audit trail preservation:
  - rollbackSourceOperation: captures source activity type
  - rollbackSourceTimestamp: captures when source change occurred
  - rollbackSourceTitle: captures resource title at time of change

These denormalized fields survive retention policy deletion, ensuring
complete audit trails even when source activities are purged.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(lib): move rollback fields to top-level in activity logger

Fixes Major issue #2 from PR #118 review:
- Move rollbackFromActivityId from metadata to top-level field
- Move contentFormat from metadata to top-level field
- Add rollback source snapshot fields to ActivityLogInput interface
- Update logRollbackActivity to accept and pass snapshot fields

Fields are now stored in dedicated database columns instead of being
nested in the metadata JSONB, enabling proper indexing and querying.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(services): add transaction support to rollback service

Enables atomic rollback operations (Issue #7 from PR #118 review):
- Add optional transaction parameter to executeRollback()
- Update all internal rollback functions to accept database parameter
- Pass rollback source snapshot fields to logRollbackActivity

When a transaction is provided, all database operations use it instead
of the default db connection, enabling atomic rollback + message
deletion in AI undo operations.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(services): improve atomicity and context logic in AI undo

Fixes Issues #5 and #7 from PR #118 review:

Context determination (#5):
- Align executeAiUndo context logic with previewAiUndo
- Use 'ai_tool' context for pages (all activities here are AI-generated)
- Simplifies from switch statement to direct assignment with drive check

Transaction atomicity (#7):
- Wrap rollbacks AND message deletion in single transaction
- Pass transaction to executeRollback for atomic operations
- If any operation fails, entire undo is rolled back

This ensures users don't end up in inconsistent states where messages
are deleted but only some changes were reverted.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(api): use Zod schema for undo route validation

Fixes Issue #6 from PR #118 review:
- Replace manual mode validation with Zod schema
- Aligns with codebase patterns (see rollback route, history route)
- Provides type-safe body parsing with proper TypeScript inference

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address CodeRabbit review feedback

- Add CHECK constraint to retentionPolicies schema definition
  (aligns Drizzle schema with existing migration constraint)
- Change AI undo to all-or-nothing transaction semantics
  (any rollback failure aborts entire operation)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(db): add subscriptionTier enum for retention policies

- Create subscription_tier pgEnum with 'free', 'pro', 'business', 'founder'
- Convert retentionPolicies.subscriptionTier from text to enum
- Adds DB-level validation to prevent invalid tier values

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address final CodeRabbit review comments

- Add comments explaining rollbackFromActivityId intentionally lacks FK
  (allows provenance to survive source activity deletion for audit)
- Add TODO note for contentSnapshot storage considerations
- Optimize message fetching: include createdAt in AiUndoPreview to
  avoid double database query

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(db): add USING clause for text-to-enum cast

PostgreSQL requires explicit cast when converting text column to enum type.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ai): support undo for global assistant messages by checking both messages tables and updating permission logic

* fix(ai): resolve lint errors in undo route and tests

* test: add contract tests for version history rollback

- Add route tests for /api/activities/[activityId]/rollback (12 tests)
- Add route tests for /api/pages/[pageId]/history (21 tests)
- Add service tests for ai-undo-service (16 tests) with @scaffold label
- Add service tests for rollback-service (28 tests) with @scaffold label
- Add permission tests for rollback-permissions (69 tests)
- Fix undo route test: remove duplicate test expecting wrong status
- Add fake timers to history route tests for deterministic dates

Per rubric v2: service tests use @scaffold labels for ORM chain mocks
pending repository seam refactoring.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(ui): add rollback capability to activity views

Add "Restore this version" action to activity items in:
- ActivityDashboard (middle panel for /dashboard/activity and drive activity)
- SidebarActivityTab (right sidebar context-aware activity feed)

Changes:
- ActivityItem: Add hover dropdown menu with rollback action and confirmation dialog
- ActivityTimeline: Pass context and onRollback handler to items
- ActivityDashboard: Handle rollback with proper context mapping (user→user_dashboard, drive→drive)
- SidebarActivityTab: Add rollback UI with context-aware scoping (page/drive/user_dashboard)

Rollback is properly scoped by context to prevent unintended changes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test: add activity logger tests for rollback and undo operations

Add compliance tests for:
- logRollbackActivity: validates rollback operation logging with source activity reference
- logConversationUndo: validates conversation undo logging for both modes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(rollback): support create operation rollback and global messages

- Add 'create' as rollbackable operation (trash resource to undo creation)
- Support both global messages and page chat messages in rollback
- Fix AI undo timing to include tool calls from preceding message
- Handle message create rollback by deactivating the message

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address PR #118 code review feedback

- Fix build failure: replace dynamic db.query[] bracket notation with
  explicit conditional to resolve TypeScript union type error
- Extract checkUndoPermissions() helper to eliminate 31 lines of
  duplicated permission logic between GET and POST handlers
- Add existingPreview parameter to executeAiUndo() to avoid redundant
  preview computation (was being called twice per request)
- Simplify context determination: remove redundant isAiGenerated check
  since query already filters for it
- Update tests to match new 4-parameter executeAiUndo signature
- Add clarifying comment explaining partial failure tests document
  defensive handling (actual impl uses all-or-nothing transaction)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: resolve header loading and rendering issues across all page types

* style: add truncation for page titles and breadcrumb items

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2witstudios added a commit that referenced this pull request Dec 23, 2025
* feat: add version history and rollback functionality

Implement comprehensive version history browsing and rollback capabilities
for PageSpace, allowing users to restore resources to previous states.

## Schema Changes
- Add 'rollback' operation to activity_operation enum
- Add rollbackFromActivityId and contentFormat fields to activity_logs
- Create retention_policies table for plan-based history retention

## Core Features
- RBAC-based rollback permissions (edit access = rollback access)
- Resource-specific rollback handlers (pages, drives, agents, etc.)
- Plan-based retention limits (7/30/90/unlimited days)
- Rollback creates new activity entry (history never erased)

## API Endpoints
- GET /api/activities/[activityId] - Single activity with rollback eligibility
- POST /api/activities/[activityId]/rollback - Execute rollback
- GET /api/pages/[pageId]/history - Page version history
- GET /api/drives/[driveId]/history - Drive version history (admin)

## UI Components
- VersionHistoryPanel - Slide-out panel with timeline and filters
- VersionHistoryItem - Activity item with "Restore" action
- RollbackConfirmDialog - Confirmation modal with warnings

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: complete rollback handler implementations and fix lint errors

- Fully implement rollbackPermissionChange for grant/revoke/update operations
- Fully implement rollbackMemberChange for add/remove/role change operations
- Fully implement rollbackRoleChange for create/delete/update operations
- Fix lint errors: remove unused imports, fix useEffect dependencies
- Add package exports for @pagespace/lib/permissions and @pagespace/lib/monitoring
- Fix useToast import path to use correct hook location

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat: add undo AI changes feature for conversations

Add ability to undo AI changes from a specific message point:
- Preview what will be affected before undoing
- Two modes: revert conversation only OR revert with all tool changes
- Activity logging for audit compliance

New files:
- ai-undo-service.ts: preview and execute functions
- UndoAiChangesDialog.tsx: confirmation dialog with mode selection
- /api/ai/chat/messages/[messageId]/undo: GET preview, POST execute

Changes:
- Add conversation_undo operations to activity schema
- Add message rollback handler to rollback-service
- Wire undo button to MessageActionButtons and MessageRenderer

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: add missing radio-group UI component

Required for UndoAiChangesDialog mode selection.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: add @radix-ui/react-radio-group dependency

Required for radio-group UI component used in UndoAiChangesDialog.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: restore correct @electron/node-gyp resolution in lockfile

The previous lockfile had a malformed git SSH URL that broke CI:
- Before: git+https://git@github.com:electron/node-gyp.git (broken)
- After: https://codeload.github.com/electron/node-gyp/tar.gz/... (works)

Restored lockfile and re-ran pnpm install to properly resolve dependencies.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: add critical rules to CLAUDE.md and fix retention tier order

- Add CRITICAL rules for package manager (always pnpm, never npm)
- Add CRITICAL rules for database migrations (never manually edit)
- Fix retention days: founder=90, business=unlimited (was swapped)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(db): add missing default and rollback index

- Add DEFAULT now() for retention_policies.updatedAt column
- Add index on activity_logs.rollbackFromActivityId for queries
- Create migration 0027_fix_retention_updated_at.sql

Addresses PR #118 review issues #10, #20

* fix(lib/permissions): add block scoping and eligibility helper

- Wrap switch case declarations in blocks to prevent variable leakage
- Add isActivityEligibleForRollback() helper for DRY rollback checks
- Export helper for use in history API routes

Addresses PR #118 review issues #5, #13

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(lib/monitoring): add rollback fields to ActivityLogInput

- Add contentFormat field for content type tracking
- Add rollbackFromActivityId for rollback chain references
- Pass fields as top-level properties to logActivity

Addresses PR #118 review issue #3

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(api): add authorization and resourceType filtering

- Add permission check before returning activity details
- Implement resourceType query parameter filtering in history routes
- Use shared isActivityEligibleForRollback helper
- Add case-insensitive resource type matching

Addresses PR #118 review issues #6, #7, #13

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(services): improve type safety and add transactions

- Use Drizzle count() instead of .length for efficient queries
- Add operation validation against known valid operations
- Fix unsafe null type assertion in RollbackPreview
- Re-export RollbackContext type for API route usage
- Wrap AI undo mutations in database transaction
- Use switch statement for context determination

Addresses PR #118 review issues #2, #16, #18, #19, #23

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ui): add CSRF protection and improve error handling

- Use post() with CSRF token for rollback requests
- Add error toast for failed preview fetch
- Handle non-JSON error responses gracefully
- Fix non-existent postWithAuth import to use post
- Remove re-throw after toast notification
- Fix redundant ternary for buttonSize

Addresses PR #118 review issues #1, #8, #11, #17, #22, NEW

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(db): address schema issues from PR #118 review

Critical & Major fixes:
- Add missing .defaultNow() to retention_policies.updatedAt
  (fixes schema/migration mismatch that would cause insert failures)
- Add contentFormatEnum for type-safe content format validation
  (prevents invalid values during rollback parsing)
- Add CHECK constraint: retentionDays >= -1 (where -1 = unlimited)
- Add rollback source snapshot fields for audit trail preservation:
  - rollbackSourceOperation: captures source activity type
  - rollbackSourceTimestamp: captures when source change occurred
  - rollbackSourceTitle: captures resource title at time of change

These denormalized fields survive retention policy deletion, ensuring
complete audit trails even when source activities are purged.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(lib): move rollback fields to top-level in activity logger

Fixes Major issue #2 from PR #118 review:
- Move rollbackFromActivityId from metadata to top-level field
- Move contentFormat from metadata to top-level field
- Add rollback source snapshot fields to ActivityLogInput interface
- Update logRollbackActivity to accept and pass snapshot fields

Fields are now stored in dedicated database columns instead of being
nested in the metadata JSONB, enabling proper indexing and querying.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(services): add transaction support to rollback service

Enables atomic rollback operations (Issue #7 from PR #118 review):
- Add optional transaction parameter to executeRollback()
- Update all internal rollback functions to accept database parameter
- Pass rollback source snapshot fields to logRollbackActivity

When a transaction is provided, all database operations use it instead
of the default db connection, enabling atomic rollback + message
deletion in AI undo operations.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(services): improve atomicity and context logic in AI undo

Fixes Issues #5 and #7 from PR #118 review:

Context determination (#5):
- Align executeAiUndo context logic with previewAiUndo
- Use 'ai_tool' context for pages (all activities here are AI-generated)
- Simplifies from switch statement to direct assignment with drive check

Transaction atomicity (#7):
- Wrap rollbacks AND message deletion in single transaction
- Pass transaction to executeRollback for atomic operations
- If any operation fails, entire undo is rolled back

This ensures users don't end up in inconsistent states where messages
are deleted but only some changes were reverted.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(api): use Zod schema for undo route validation

Fixes Issue #6 from PR #118 review:
- Replace manual mode validation with Zod schema
- Aligns with codebase patterns (see rollback route, history route)
- Provides type-safe body parsing with proper TypeScript inference

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address CodeRabbit review feedback

- Add CHECK constraint to retentionPolicies schema definition
  (aligns Drizzle schema with existing migration constraint)
- Change AI undo to all-or-nothing transaction semantics
  (any rollback failure aborts entire operation)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(db): add subscriptionTier enum for retention policies

- Create subscription_tier pgEnum with 'free', 'pro', 'business', 'founder'
- Convert retentionPolicies.subscriptionTier from text to enum
- Adds DB-level validation to prevent invalid tier values

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address final CodeRabbit review comments

- Add comments explaining rollbackFromActivityId intentionally lacks FK
  (allows provenance to survive source activity deletion for audit)
- Add TODO note for contentSnapshot storage considerations
- Optimize message fetching: include createdAt in AiUndoPreview to
  avoid double database query

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(db): add USING clause for text-to-enum cast

PostgreSQL requires explicit cast when converting text column to enum type.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ai): support undo for global assistant messages by checking both messages tables and updating permission logic

* fix(ai): resolve lint errors in undo route and tests

* test: add contract tests for version history rollback

- Add route tests for /api/activities/[activityId]/rollback (12 tests)
- Add route tests for /api/pages/[pageId]/history (21 tests)
- Add service tests for ai-undo-service (16 tests) with @scaffold label
- Add service tests for rollback-service (28 tests) with @scaffold label
- Add permission tests for rollback-permissions (69 tests)
- Fix undo route test: remove duplicate test expecting wrong status
- Add fake timers to history route tests for deterministic dates

Per rubric v2: service tests use @scaffold labels for ORM chain mocks
pending repository seam refactoring.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(ui): add rollback capability to activity views

Add "Restore this version" action to activity items in:
- ActivityDashboard (middle panel for /dashboard/activity and drive activity)
- SidebarActivityTab (right sidebar context-aware activity feed)

Changes:
- ActivityItem: Add hover dropdown menu with rollback action and confirmation dialog
- ActivityTimeline: Pass context and onRollback handler to items
- ActivityDashboard: Handle rollback with proper context mapping (user→user_dashboard, drive→drive)
- SidebarActivityTab: Add rollback UI with context-aware scoping (page/drive/user_dashboard)

Rollback is properly scoped by context to prevent unintended changes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test: add activity logger tests for rollback and undo operations

Add compliance tests for:
- logRollbackActivity: validates rollback operation logging with source activity reference
- logConversationUndo: validates conversation undo logging for both modes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(rollback): support create operation rollback and global messages

- Add 'create' as rollbackable operation (trash resource to undo creation)
- Support both global messages and page chat messages in rollback
- Fix AI undo timing to include tool calls from preceding message
- Handle message create rollback by deactivating the message

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address PR #118 code review feedback

- Fix build failure: replace dynamic db.query[] bracket notation with
  explicit conditional to resolve TypeScript union type error
- Extract checkUndoPermissions() helper to eliminate 31 lines of
  duplicated permission logic between GET and POST handlers
- Add existingPreview parameter to executeAiUndo() to avoid redundant
  preview computation (was being called twice per request)
- Simplify context determination: remove redundant isAiGenerated check
  since query already filters for it
- Update tests to match new 4-parameter executeAiUndo signature
- Add clarifying comment explaining partial failure tests document
  defensive handling (actual impl uses all-or-nothing transaction)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test: update rollback-permissions tests for create operation

Update test expectations to match the new behavior where 'create'
is a rollbackable operation (rolling back a create = trashing the resource).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: reset undo counters on transaction failure

When executeAiUndo catches an error, the transaction has been rolled
back so no changes were committed. Reset messagesDeleted and
activitiesRolledBack to 0 to accurately reflect this.

Also simplify the route handler to always return 500 on failure since
partial success (207) is now unreachable with all-or-nothing semantics.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: remove unused params variable in OptimizedViewHeader

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(web): refine AI undo error message to reflect atomic transaction semantics

* fix(tests): correct hook test expectations for SWR behavior

- useBreadcrumbs: Fix isLoading expectation for null pageId
  When pageId is null, isLoading correctly returns false (nothing to load)

- usePermissions: Fix SWR mock to trigger onSuccess callback
  isPaused requires hasLoadedRef.current=true, which is set by onSuccess

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: handle all failure cases in AI undo route

- Remove `&& result.errors.length > 0` condition so any success:false
  triggers 500 response, not just failures with non-empty errors
- Add test for empty errors array edge case
- Enhance usePermissions test to properly validate after onSuccess

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2witstudios added a commit that referenced this pull request Dec 24, 2025
* feat: add version history and rollback functionality

Implement comprehensive version history browsing and rollback capabilities
for PageSpace, allowing users to restore resources to previous states.

## Schema Changes
- Add 'rollback' operation to activity_operation enum
- Add rollbackFromActivityId and contentFormat fields to activity_logs
- Create retention_policies table for plan-based history retention

## Core Features
- RBAC-based rollback permissions (edit access = rollback access)
- Resource-specific rollback handlers (pages, drives, agents, etc.)
- Plan-based retention limits (7/30/90/unlimited days)
- Rollback creates new activity entry (history never erased)

## API Endpoints
- GET /api/activities/[activityId] - Single activity with rollback eligibility
- POST /api/activities/[activityId]/rollback - Execute rollback
- GET /api/pages/[pageId]/history - Page version history
- GET /api/drives/[driveId]/history - Drive version history (admin)

## UI Components
- VersionHistoryPanel - Slide-out panel with timeline and filters
- VersionHistoryItem - Activity item with "Restore" action
- RollbackConfirmDialog - Confirmation modal with warnings

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: complete rollback handler implementations and fix lint errors

- Fully implement rollbackPermissionChange for grant/revoke/update operations
- Fully implement rollbackMemberChange for add/remove/role change operations
- Fully implement rollbackRoleChange for create/delete/update operations
- Fix lint errors: remove unused imports, fix useEffect dependencies
- Add package exports for @pagespace/lib/permissions and @pagespace/lib/monitoring
- Fix useToast import path to use correct hook location

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat: add undo AI changes feature for conversations

Add ability to undo AI changes from a specific message point:
- Preview what will be affected before undoing
- Two modes: revert conversation only OR revert with all tool changes
- Activity logging for audit compliance

New files:
- ai-undo-service.ts: preview and execute functions
- UndoAiChangesDialog.tsx: confirmation dialog with mode selection
- /api/ai/chat/messages/[messageId]/undo: GET preview, POST execute

Changes:
- Add conversation_undo operations to activity schema
- Add message rollback handler to rollback-service
- Wire undo button to MessageActionButtons and MessageRenderer

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: add missing radio-group UI component

Required for UndoAiChangesDialog mode selection.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: add @radix-ui/react-radio-group dependency

Required for radio-group UI component used in UndoAiChangesDialog.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: restore correct @electron/node-gyp resolution in lockfile

The previous lockfile had a malformed git SSH URL that broke CI:
- Before: git+https://git@github.com:electron/node-gyp.git (broken)
- After: https://codeload.github.com/electron/node-gyp/tar.gz/... (works)

Restored lockfile and re-ran pnpm install to properly resolve dependencies.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: add critical rules to CLAUDE.md and fix retention tier order

- Add CRITICAL rules for package manager (always pnpm, never npm)
- Add CRITICAL rules for database migrations (never manually edit)
- Fix retention days: founder=90, business=unlimited (was swapped)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(db): add missing default and rollback index

- Add DEFAULT now() for retention_policies.updatedAt column
- Add index on activity_logs.rollbackFromActivityId for queries
- Create migration 0027_fix_retention_updated_at.sql

Addresses PR #118 review issues #10, #20

* fix(lib/permissions): add block scoping and eligibility helper

- Wrap switch case declarations in blocks to prevent variable leakage
- Add isActivityEligibleForRollback() helper for DRY rollback checks
- Export helper for use in history API routes

Addresses PR #118 review issues #5, #13

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(lib/monitoring): add rollback fields to ActivityLogInput

- Add contentFormat field for content type tracking
- Add rollbackFromActivityId for rollback chain references
- Pass fields as top-level properties to logActivity

Addresses PR #118 review issue #3

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(api): add authorization and resourceType filtering

- Add permission check before returning activity details
- Implement resourceType query parameter filtering in history routes
- Use shared isActivityEligibleForRollback helper
- Add case-insensitive resource type matching

Addresses PR #118 review issues #6, #7, #13

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(services): improve type safety and add transactions

- Use Drizzle count() instead of .length for efficient queries
- Add operation validation against known valid operations
- Fix unsafe null type assertion in RollbackPreview
- Re-export RollbackContext type for API route usage
- Wrap AI undo mutations in database transaction
- Use switch statement for context determination

Addresses PR #118 review issues #2, #16, #18, #19, #23

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ui): add CSRF protection and improve error handling

- Use post() with CSRF token for rollback requests
- Add error toast for failed preview fetch
- Handle non-JSON error responses gracefully
- Fix non-existent postWithAuth import to use post
- Remove re-throw after toast notification
- Fix redundant ternary for buttonSize

Addresses PR #118 review issues #1, #8, #11, #17, #22, NEW

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(db): address schema issues from PR #118 review

Critical & Major fixes:
- Add missing .defaultNow() to retention_policies.updatedAt
  (fixes schema/migration mismatch that would cause insert failures)
- Add contentFormatEnum for type-safe content format validation
  (prevents invalid values during rollback parsing)
- Add CHECK constraint: retentionDays >= -1 (where -1 = unlimited)
- Add rollback source snapshot fields for audit trail preservation:
  - rollbackSourceOperation: captures source activity type
  - rollbackSourceTimestamp: captures when source change occurred
  - rollbackSourceTitle: captures resource title at time of change

These denormalized fields survive retention policy deletion, ensuring
complete audit trails even when source activities are purged.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(lib): move rollback fields to top-level in activity logger

Fixes Major issue #2 from PR #118 review:
- Move rollbackFromActivityId from metadata to top-level field
- Move contentFormat from metadata to top-level field
- Add rollback source snapshot fields to ActivityLogInput interface
- Update logRollbackActivity to accept and pass snapshot fields

Fields are now stored in dedicated database columns instead of being
nested in the metadata JSONB, enabling proper indexing and querying.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(services): add transaction support to rollback service

Enables atomic rollback operations (Issue #7 from PR #118 review):
- Add optional transaction parameter to executeRollback()
- Update all internal rollback functions to accept database parameter
- Pass rollback source snapshot fields to logRollbackActivity

When a transaction is provided, all database operations use it instead
of the default db connection, enabling atomic rollback + message
deletion in AI undo operations.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(services): improve atomicity and context logic in AI undo

Fixes Issues #5 and #7 from PR #118 review:

Context determination (#5):
- Align executeAiUndo context logic with previewAiUndo
- Use 'ai_tool' context for pages (all activities here are AI-generated)
- Simplifies from switch statement to direct assignment with drive check

Transaction atomicity (#7):
- Wrap rollbacks AND message deletion in single transaction
- Pass transaction to executeRollback for atomic operations
- If any operation fails, entire undo is rolled back

This ensures users don't end up in inconsistent states where messages
are deleted but only some changes were reverted.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(api): use Zod schema for undo route validation

Fixes Issue #6 from PR #118 review:
- Replace manual mode validation with Zod schema
- Aligns with codebase patterns (see rollback route, history route)
- Provides type-safe body parsing with proper TypeScript inference

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address CodeRabbit review feedback

- Add CHECK constraint to retentionPolicies schema definition
  (aligns Drizzle schema with existing migration constraint)
- Change AI undo to all-or-nothing transaction semantics
  (any rollback failure aborts entire operation)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(db): add subscriptionTier enum for retention policies

- Create subscription_tier pgEnum with 'free', 'pro', 'business', 'founder'
- Convert retentionPolicies.subscriptionTier from text to enum
- Adds DB-level validation to prevent invalid tier values

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address final CodeRabbit review comments

- Add comments explaining rollbackFromActivityId intentionally lacks FK
  (allows provenance to survive source activity deletion for audit)
- Add TODO note for contentSnapshot storage considerations
- Optimize message fetching: include createdAt in AiUndoPreview to
  avoid double database query

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(db): add USING clause for text-to-enum cast

PostgreSQL requires explicit cast when converting text column to enum type.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ai): support undo for global assistant messages by checking both messages tables and updating permission logic

* fix(ai): resolve lint errors in undo route and tests

* test: add contract tests for version history rollback

- Add route tests for /api/activities/[activityId]/rollback (12 tests)
- Add route tests for /api/pages/[pageId]/history (21 tests)
- Add service tests for ai-undo-service (16 tests) with @scaffold label
- Add service tests for rollback-service (28 tests) with @scaffold label
- Add permission tests for rollback-permissions (69 tests)
- Fix undo route test: remove duplicate test expecting wrong status
- Add fake timers to history route tests for deterministic dates

Per rubric v2: service tests use @scaffold labels for ORM chain mocks
pending repository seam refactoring.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(ui): add rollback capability to activity views

Add "Restore this version" action to activity items in:
- ActivityDashboard (middle panel for /dashboard/activity and drive activity)
- SidebarActivityTab (right sidebar context-aware activity feed)

Changes:
- ActivityItem: Add hover dropdown menu with rollback action and confirmation dialog
- ActivityTimeline: Pass context and onRollback handler to items
- ActivityDashboard: Handle rollback with proper context mapping (user→user_dashboard, drive→drive)
- SidebarActivityTab: Add rollback UI with context-aware scoping (page/drive/user_dashboard)

Rollback is properly scoped by context to prevent unintended changes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test: add activity logger tests for rollback and undo operations

Add compliance tests for:
- logRollbackActivity: validates rollback operation logging with source activity reference
- logConversationUndo: validates conversation undo logging for both modes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(rollback): support create operation rollback and global messages

- Add 'create' as rollbackable operation (trash resource to undo creation)
- Support both global messages and page chat messages in rollback
- Fix AI undo timing to include tool calls from preceding message
- Handle message create rollback by deactivating the message

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address PR #118 code review feedback

- Fix build failure: replace dynamic db.query[] bracket notation with
  explicit conditional to resolve TypeScript union type error
- Extract checkUndoPermissions() helper to eliminate 31 lines of
  duplicated permission logic between GET and POST handlers
- Add existingPreview parameter to executeAiUndo() to avoid redundant
  preview computation (was being called twice per request)
- Simplify context determination: remove redundant isAiGenerated check
  since query already filters for it
- Update tests to match new 4-parameter executeAiUndo signature
- Add clarifying comment explaining partial failure tests document
  defensive handling (actual impl uses all-or-nothing transaction)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test: update rollback-permissions tests for create operation

Update test expectations to match the new behavior where 'create'
is a rollbackable operation (rolling back a create = trashing the resource).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: reset undo counters on transaction failure

When executeAiUndo catches an error, the transaction has been rolled
back so no changes were committed. Reset messagesDeleted and
activitiesRolledBack to 0 to accurately reflect this.

Also simplify the route handler to always return 500 on failure since
partial success (207) is now unreachable with all-or-nothing semantics.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: remove unused params variable in OptimizedViewHeader

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(web): refine AI undo error message to reflect atomic transaction semantics

* fix(tests): correct hook test expectations for SWR behavior

- useBreadcrumbs: Fix isLoading expectation for null pageId
  When pageId is null, isLoading correctly returns false (nothing to load)

- usePermissions: Fix SWR mock to trigger onSuccess callback
  isPaused requires hasLoadedRef.current=true, which is set by onSuccess

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: handle all failure cases in AI undo route

- Remove `&& result.errors.length > 0` condition so any success:false
  triggers 500 response, not just failures with non-empty errors
- Add test for empty errors array edge case
- Enhance usePermissions test to properly validate after onSuccess

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: resolve AI chat undo feature not working

The undo feature was always showing "Failed to undo changes" due to incorrect
usage of the post() helper from auth-fetch. The post() function:
- Returns parsed JSON on success (not a Response object)
- Throws an error on non-2xx responses

Components were incorrectly checking res.ok (undefined on parsed JSON) and
calling res.json() on already-parsed objects, causing the error branch to
always trigger.

Changes:
- Fix UndoAiChangesDialog to properly use post() - await and catch errors
- Fix ActivityDashboard, SidebarActivityTab, VersionHistoryPanel (same pattern)
- Update page-write-tools activity logging to pass previousValues for rollback
  support (replace_lines, rename_page, trash/restore, move_page, edit_sheet_cells
  now store original state for proper undo)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2witstudios added a commit that referenced this pull request Feb 15, 2026
Updated GitHub Project with all completed issues:
- Marked 14 additional issues as Done via gh CLI
- Project status: 20/30 Done (67%)

Remaining 10 issues:
- 5 Remotion compositions (deferred - awaiting UI parity)
- 5 requiring external resources (auth, Google, app stores)

Extra pages implemented beyond original project scope:
- /blog with 4 posts
- /faq
- /docs with getting-started guide
- /changelog

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2witstudios added a commit that referenced this pull request Feb 17, 2026
…n, and auth isolation (#694)

* feat(marketing): Complete site architecture setup (#638)

- Configure shadcn/ui with 55+ components
- Copy public assets (favicons, icons, manifest) from web app
- Create .env.example with marketing site configuration
- Fix lint errors in combobox component
- Create tracking docs (PROJECT_MIRROR.md, PROGRESS.md, SESSION_LOG.md)
- Create ARCHITECTURE.md documenting marketing site structure

Issue #638 acceptance criteria met:
- Next.js 15 App Router configured
- Tailwind + shadcn/ui working
- TypeScript strict mode enabled
- Environment variables documented
- Build passes with no errors

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(marketing): Add sitemap.xml and update robots.txt (#642)

- Create dynamic sitemap.ts with all planned marketing routes
- Include 12 URLs: /, /pricing, /downloads, /tour, /integrations,
  /faq, /changelog, /docs/*, /blog
- Update robots.txt for marketing site (disallow /screenshots/, /api/, /_next/)
- Configure proper priorities and change frequencies

Issue #642 acceptance criteria met:
- sitemap.xml generated at build time
- All public pages included
- robots.txt allows crawling
- Sitemap accessible at /sitemap.xml
- No private/auth pages in sitemap

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(marketing): Add comprehensive SEO metadata and OG images (#644)

- Create src/lib/metadata.ts with reusable SEO helpers
- Add createMetadata() for per-page metadata generation
- Define siteMetadata with full OG, Twitter, icons, manifest config
- Create pageMetadata presets for all planned marketing pages
- Update layout.tsx with comprehensive site-wide metadata
- Add dynamic /og-image.png route for Open Graph images
- Configure theme-color meta for light/dark modes

Issue #644 acceptance criteria met:
- Every page has unique title/description (via metadata presets)
- Open Graph images for all pages (dynamic route)
- Twitter cards working (configured in siteMetadata)
- Canonical URLs set (via alternates.canonical)
- No duplicate meta tags

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(marketing): Add Schema.org structured data (#643)

- Create src/lib/schema.tsx with comprehensive JSON-LD schemas
- Add Organization schema (site-wide in layout.tsx)
- Add WebSite schema with search action
- Add WebApplication schema for landing page
- Add Product schema with all 4 pricing tiers
- Add SoftwareApplication schema for downloads
- Add FAQPage schema helper (createFAQSchema)
- Add Article schema helper (createArticleSchema)
- Add BreadcrumbList schema helper
- Add JsonLd component for rendering scripts

Issue #643 acceptance criteria met:
- Organization schema on all pages
- WebApplication schema on landing
- Product schema with offers
- FAQPage schema ready for FAQ page
- Article schema ready for blog posts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(marketing): Implement landing page Hero section (#646)

- Replace screenshot generator with full marketing landing page
- Create Hero section with core value proposition
  - Headline: "You, your team, and AI—working together"
  - AI-native badge
  - Clear subheadline
  - Get Started / View Pricing CTAs
  - Desktop/mobile app availability
- Add full UI mockup showing PageSpace interface
  - Sidebar with navigation
  - Document editor view
  - AI Assistant panel
- Add sticky navigation header
- Add features preview section
- Add minimal footer with navigation
- Include WebApplication schema JSON-LD
- Use proper metadata from lib/metadata.ts

Issue #646 acceptance criteria met:
- Clear value proposition in <5 seconds
- CTAs prominent and accessible
- Responsive across all breakpoints
- Accessible design

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(marketing): Add AI architecture section to landing page (#647)

- Add comprehensive AI Architecture section explaining:
  - Global Assistant (personal, cross-workspace AI)
  - Page Agents (file tree, custom prompts, conversation history)
  - Nested Context (hierarchical awareness)
  - Team AI (multi-user collaboration)
- Create visual file tree mockup showing:
  - Global Assistant at top level
  - Project-level AI agents
  - Document hierarchy with context flow
- Add ICP example quote from founder perspective
- Clearly differentiate from ChatGPT/Notion AI approach

Issue #647 acceptance criteria met:
- Clear explanation of Global vs Page agents
- Visual shows context hierarchy
- Differentiates from ChatGPT/Notion AI
- ICP example resonates

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Complete Landing Page Redesign epic + Remotion setup (#648-653)

Landing Page sections implemented:
- Documents section with document editor mockup and AI suggestions
- Channels + DMs section with @mention AI demonstration
- Tasks section with AI assignee and rollup visualization
- Calendar section with unified multi-source view
- Final CTA section with trust signals and quick links
- Enhanced footer with categorized navigation

Remotion Video System setup:
- Installed Remotion 4.x with Tailwind support
- Created Root.tsx with DESIGN_TOKENS and theme system
- Sample and Hero compositions with light/dark variants
- Render scripts for MP4 output
- Separate tsconfig.remotion.json for Remotion build
- Full documentation in remotion/README.md

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add Downloads and Pricing pages (#659-660, #663-664)

Downloads page (/downloads):
- Desktop downloads for macOS (Silicon/Intel), Windows, Linux (AppImage/deb)
- Mobile apps section with iOS TestFlight and Android Beta links
- System requirements for all platforms
- Auto-update information and version display
- Links to changelog

Pricing page (/pricing):
- 4 pricing tiers (Free, Pro, Founder, Business) with feature cards
- Full feature comparison table with tooltips
- Enterprise section with contact CTA
- FAQ preview section

Remotion partial work (deferred pending UI parity):
- DocumentEditing and Channels compositions created but not complete

14 static pages now build successfully.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add marketing pages: tour, integrations, blog, FAQ, docs, changelog

Complete implementation of 10 marketing site pages:

Tour (/tour):
- 5-step product tour workflow
- AI Architecture overview section
- Visual mockups with placeholders

Integrations (/integrations):
- MCP Servers showcase with 6 servers
- Native integrations section
- Developer section with API documentation

Blog (/blog, /blog/[slug]):
- Blog index with category filtering
- 4 initial blog posts with full content
- Newsletter signup CTA

FAQ (/faq):
- 20 FAQs across 6 categories
- Expandable details/summary UI

Documentation (/docs, /docs/getting-started):
- Documentation hub with search
- Getting started guide with 5 steps

Changelog (/changelog):
- Version history with 5 releases
- Timeline UI with change type badges

Also:
- Updated sitemap.ts with all new routes
- Updated tracking files (PROGRESS.md, SESSION_LOG.md, PROJECT_MIRROR.md)

Build: 25 static pages, all passing

Issues completed: #665, #666, #667, #668, #669, #670, #671, #672, #673, #674, #641

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Sync GitHub Project #5 status - 20 issues now Done

Updated GitHub Project with all completed issues:
- Marked 14 additional issues as Done via gh CLI
- Project status: 20/30 Done (67%)

Remaining 10 issues:
- 5 Remotion compositions (deferred - awaiting UI parity)
- 5 requiring external resources (auth, Google, app stores)

Extra pages implemented beyond original project scope:
- /blog with 4 posts
- /faq
- /docs with getting-started guide
- /changelog

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Update PROJECT_MIRROR.md notes to reflect current status

* feat(marketing): Redesign landing page for visual parity with real app + add auth pages

Overhaul the marketing site to pixel-match the actual PageSpace UI, add a
branded oklch color system, extract SiteNavbar, build login/signup auth
flow, and add the motion library for animations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(marketing): Redesign Page Tree section, extract shared components, clean up UI

- Rebuild "AI Architecture" section as "Everything is a page" with real sidebar mock
  matching actual PageSpace UI (drive switcher, nav, search, page tree with nesting)
- Add deeper nesting (channel with child pages) to show semantic parent/child
- Rewrite feature cards to 3 focused cards: Everything is a Page, Context is Structure, AI at Every Level
- Extract SiteFooter component (full + compact variants) used across all 12 pages
- Fix logo everywhere: replace Sparkles icon with real PageSpace logo
- Update SiteNavbar: add centered nav links, replace Home icon with logo, remove sidebar/nav buttons
- Remove all section header badges and testimonial cards
- Remove "Free tier / No credit card / Cancel anytime" from CTA section
- Remove unused Globe import

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(marketing): Complete non-Remotion marketing site work

- Register Channels Remotion composition (light/dark variants)
- Port privacy policy and terms of service pages from main app
- Add contact page with isolated Resend email integration (no DB)
- Delete main app's public /api/contact route (admin route kept)
- Fix dead footer links: remove About, Careers, API Reference
- Remove auth pages, redirect all auth links to main app
- Add Google One Tap passive host component
- Add Google Search Console verification placeholder
- Add downloads "Coming Soon" state + deep link scaffolding
- Clean up "Coming Soon" placeholder text
- Replace app.pagespace.ai fallbacks with pagespace.ai

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(marketing): Address PR #694 review comments (9 fixes)

- Add "use client" directive to navigation-menu.tsx for Radix hooks
- Use shared pageMetadata for terms/privacy pages instead of inline createMetadata
- Remove SearchAction from JSON-LD schema (no /search page exists)
- Fix HSL color opacity in Remotion DocumentEditing (withOpacity helper replaces broken hex concatenation)
- Fix render-videos.ts progress logging and wire enableTailwind into webpack override
- Remove unimplemented /docs/api and /docs/mcp from sitemap
- Point broken /docs/* links to /docs fallback across integrations, docs, and getting-started pages
- Replace copy-pasted inline headers with SiteNavbar in 7 pages
- Replace line-by-line blog markdown parser with react-markdown + remark-gfm

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(marketing): Address 21 code review findings

ESM: Add --esm to remotion scripts, polyfill __dirname/fileURLToPath

Blog: Extract blogPosts to data.ts, create ShareButtons client component, add openGraph.url

Docs: Fix duplicate React keys, dedup Popular Articles, replace search input with button, shared metadata for getting-started, Coming Soon cards

Integrations: Replace div-based curl snippet with semantic pre/code

Legal: Add LEGAL_LAST_UPDATED constant for terms and privacy, Discord link attrs

Tour: Use map index instead of fragile step.number coupling

Navigation: Remove unnecessary cn() wrapper, extract viewportFalseClasses

Schema: Remove invalid downloadUrl, sanitize JsonLd XSS, extract SOFTWARE_VERSION, strip nested @context from embedded organizationRef

* fix(marketing): Address follow-up review findings

Fix broken /docs/mcp markdown link in blog data

Add try/catch to clipboard API in ShareButtons

Add comingSoon flag to docs placeholder items, render as non-clickable

Mark SDK Libraries and Webhook Events as coming soon on integrations page

Fix double title suffix: createMetadata no longer appends '| PageSpace' (layout template handles it)

Extract shared APP_URL constant from metadata.ts, deduplicate across 12 files

Tighten JsonLd prop type from object to Record<string, unknown>

Add rel='noopener' to external CTA anchor on blog page

Add dev env comment for SITE_URL/APP_URL fallback values in schema.tsx

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant