Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 45 additions & 10 deletions .specify/memory/constitution.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
<!--
SYNC IMPACT REPORT
==================
Version change: 1.0.0 → 1.1.0
Version change: 1.1.0 → 1.2.0
Modified principles: none
Added sections:
- VI. Test-Driven Development (new principle)
- VII. Security (new principle)
- VIII. Frontend UI Conventions (new principle)
Removed sections: none
Templates requiring updates:
- .specify/templates/plan-template.md ✅ — Constitution Check now includes TDD gate and
Security gate; plan-template already references constitution check before Phase 0
- .specify/templates/spec-template.md ✅ — Success criteria MUST include security
acceptance scenarios and test coverage requirements
- .specify/templates/tasks-template.md ✅ — Task phases now reflect mandatory TDD
ordering (tests written & failing before implementation) and security hardening phase
- .specify/templates/plan-template.md — Constitution Check should include Frontend gate
when UI changes are in scope
Deferred TODOs: none
-->

Expand Down Expand Up @@ -155,6 +150,46 @@ security-focused tests (see `tests/server/routes/test_*_security*.py` and
and AI-derived insights. A single authorization bypass or credential leak would
expose highly sensitive personal data. Security must be designed in, not bolted on.

### VIII. Frontend UI Conventions

The frontend is Vanilla JS with Vue 3 (ESM browser build, no build step). All
components live in `supernote/server/static/js/components/` and are served as
static files. UI changes MUST follow the established visual language exactly
so the interface remains consistent across features.

**Button styles** (Tailwind CSS — MUST be used verbatim for each category):

| Category | Required classes |
|---|---|
| Primary (save / confirm) | `px-4 py-2 bg-black border border-black rounded text-sm font-medium text-white hover:bg-gray-800 disabled:opacity-50 transition-colors` |
| Secondary / cancel | `px-4 py-2 bg-white dark:bg-gray-600 border border-gray-300 dark:border-gray-500 rounded text-sm font-medium text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-500 disabled:opacity-50 transition-colors` |
| Danger (delete / remove) | `px-3 py-1.5 text-xs font-medium text-red-600 dark:text-red-400 border border-red-200 dark:border-red-700 bg-red-50 dark:bg-red-900/20 hover:bg-red-100 dark:hover:bg-red-900/40 rounded transition-colors disabled:opacity-50` |
| Icon-only (header / toolbar) | `text-gray-400 hover:text-black dark:hover:text-white transition-colors` |
| Amber / warning | `p-2 text-amber-600 dark:text-amber-400 hover:bg-amber-50 dark:hover:bg-amber-900/30 disabled:opacity-50 rounded transition-colors border border-amber-300 dark:border-amber-600` — reserved for status-driven indicators only (e.g. stale content), never for routine actions |

**Prohibited**: Framework accent colors (indigo, blue, green, etc.) MUST NOT
be used for interactive controls. The permitted palette for interactive elements
is black / gray / red / amber only, as defined above.

**Disabled state**: Always `disabled:opacity-50`; add `disabled:cursor-not-allowed`
only when a full-width button is used (e.g. form submission).

**Modal overlay pattern**: `fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-[N] p-4` with `@click.self="$emit('close')"`. Modal close (×) buttons MUST use the icon-only style above.

**Focus rings**: `focus:ring-2 focus:ring-black dark:focus:ring-white` — color-specific rings (indigo, blue, etc.) are prohibited.

**Loading spinners**: `animate-spin rounded-full h-8 w-8 border-b-2 border-black dark:border-white` (full-size) or `animate-spin w-4 h-4` with an SVG spinner inline (compact, inside a button).

**Dark mode**: Every interactive element MUST declare a `dark:` variant for all
background, text, and border properties. An element without a dark mode class
MUST NOT be merged.

**Rationale**: A consistent visual language makes the application feel coherent
and reduces cognitive overhead. Because there is no design system or component
library, the Tailwind class strings above serve as the single source of truth.
Deviating without updating this constitution creates drift that compounds
across features.

## Technology Stack

- **Runtime**: Python 3.13+, managed with `uv`
Expand Down Expand Up @@ -215,4 +250,4 @@ all seven Core Principles. Complexity that violates a principle MUST be
explicitly justified in the PR description with reference to the specific
principle and why the simpler alternative was rejected.

**Version**: 1.1.0 | **Ratified**: 2026-03-15 | **Last Amended**: 2026-03-15
**Version**: 1.2.0 | **Ratified**: 2026-03-15 | **Last Amended**: 2026-03-17
25 changes: 23 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
# supernote Development Guidelines

Auto-generated from all feature plans. Last updated: 2026-03-16
Auto-generated from all feature plans. Last updated: 2026-03-17

## Active Technologies
- N/A (no Python source changes) + GitHub Dependabot (native GitHub feature, no external service) (002-switch-dependabot)
- N/A (no Python source changes — CI/CD configuration only) + GitHub Actions (`docker/metadata-action`, `docker/build-push-action`, `docker/login-action`) (003-github-releases)
- Python 3.13+ + aiohttp (server), SQLAlchemy asyncio + aiosqlite, mashumaro, alembic; Vanilla JS (Vue 3, no build step) for frontend (004-ui-prompt-config)
- SQLite via SQLAlchemy asyncio — new `f_prompt_config` table; new `prompt_hash` column on `f_note_page_content` (004-ui-prompt-config)

- Python 3.13+ + mypy (strict), SQLAlchemy asyncio, aiohttp, mashumaro, pytest + pytest-asyncio (001-constitution-alignment)

Expand Down Expand Up @@ -43,10 +46,28 @@ supernote serve --ephemeral # Ephemeral server with debug@example.com / password
- **Testing**: `unittest.mock.patch` only (no `monkeypatch`); all test functions/fixtures must have type annotations; tests written before implementation
- **Logging**: `logging.getLogger(__name__)`; NEVER log note content

## Frontend UI Conventions (constitution §VIII)

Button Tailwind classes — use verbatim:

| Category | Classes |
|---|---|
| Primary (save/confirm) | `px-4 py-2 bg-black border border-black rounded text-sm font-medium text-white hover:bg-gray-800 disabled:opacity-50 transition-colors` |
| Secondary/cancel | `px-4 py-2 bg-white dark:bg-gray-600 border border-gray-300 dark:border-gray-500 rounded text-sm font-medium text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-500 transition-colors` |
| Danger (delete/remove) | `px-3 py-1.5 text-xs font-medium text-red-600 dark:text-red-400 border border-red-200 dark:border-red-700 bg-red-50 dark:bg-red-900/20 hover:bg-red-100 dark:hover:bg-red-900/40 rounded transition-colors disabled:opacity-50` |
| Icon-only (header) | `text-gray-400 hover:text-black dark:hover:text-white transition-colors` |
| Amber/warning | `p-2 text-amber-600 dark:text-amber-400 hover:bg-amber-50 dark:hover:bg-amber-900/30 disabled:opacity-50 rounded transition-colors border border-amber-300 dark:border-amber-600` — status indicators only |

- **No accent colors**: indigo, blue, green etc. are prohibited for interactive controls
- **Focus rings**: `focus:ring-2 focus:ring-black dark:focus:ring-white`
- **Spinners**: `animate-spin rounded-full h-8 w-8 border-b-2 border-black dark:border-white`
- **Dark mode**: every interactive element must have `dark:` variants

## Recent Changes
- 004-ui-prompt-config: Added Python 3.13+ + aiohttp (server), SQLAlchemy asyncio + aiosqlite, mashumaro, alembic; Vanilla JS (Vue 3, no build step) for frontend
- 003-github-releases: Added N/A (no Python source changes — CI/CD configuration only) + GitHub Actions (`docker/metadata-action`, `docker/build-push-action`, `docker/login-action`)
- 002-switch-dependabot: Added N/A (no Python source changes) + GitHub Dependabot (native GitHub feature, no external service)

- 001-constitution-alignment: Added Python 3.13+ + mypy (strict), SQLAlchemy asyncio, aiohttp, mashumaro, pytest + pytest-asyncio

<!-- MANUAL ADDITIONS START -->
<!-- MANUAL ADDITIONS END -->
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ requires = ["setuptools>=77.0"]

[project]
name = "supernote"
version = "1.0.1"
version = "1.1.0"
license = "Apache-2.0"
license-files = ["LICENSE"]
description = "All-in-one toolkit for Supernote devices: parse notebooks, self-host services, access services"
Expand Down
35 changes: 35 additions & 0 deletions specs/004-ui-prompt-config/checklists/requirements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Specification Quality Checklist: UI Prompt Configuration

**Purpose**: Validate specification completeness and quality before proceeding to planning
**Created**: 2026-03-17
**Feature**: [spec.md](../spec.md)

## Content Quality

- [x] No implementation details (languages, frameworks, APIs)
- [x] Focused on user value and business needs
- [x] Written for non-technical stakeholders
- [x] All mandatory sections completed

## Requirement Completeness

- [x] No [NEEDS CLARIFICATION] markers remain
- [x] Requirements are testable and unambiguous
- [x] Success criteria are measurable
- [x] Success criteria are technology-agnostic (no implementation details)
- [x] All acceptance scenarios are defined
- [x] Edge cases are identified
- [x] Scope is clearly bounded
- [x] Dependencies and assumptions identified

## Feature Readiness

- [x] All functional requirements have clear acceptance criteria
- [x] User scenarios cover primary flows
- [x] Feature meets measurable outcomes defined in Success Criteria
- [x] No implementation details leak into specification

## Notes

- All items pass. Spec updated to include prompt hash tracking, stale detection, and manual Reprocess button (FR-014–FR-020, User Story 5, SC-006–SC-007).
- Ready for `/speckit.clarify` or `/speckit.plan`.
221 changes: 221 additions & 0 deletions specs/004-ui-prompt-config/contracts/prompts-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
# API Contracts: Prompt Configuration

All endpoints are under the `extended` route module (`/api/extended/`). All require a valid `x-access-token` JWT header. Users may only access their own prompt configurations; cross-user access returns 403.

---

## Prompt Configuration Endpoints

### GET `/api/extended/prompts`

Returns all effective prompt configurations for the authenticated user. The response includes every known `(category, layer)` combination — built-in layers from the server files plus any user-defined custom layers — merged with the user's saved overrides.

**Auth**: JWT required

**Response 200**:
```json
{
"success": true,
"prompts": [
{
"category": "ocr",
"layer": "common",
"content": "...current effective text...",
"isOverride": true,
"defaultContent": "...server file text..."
},
{
"category": "ocr",
"layer": "default",
"content": "...server file text...",
"isOverride": false,
"defaultContent": "...server file text..."
},
{
"category": "ocr",
"layer": "daily",
"content": "...server file text...",
"isOverride": false,
"defaultContent": "...server file text..."
},
{
"category": "summary",
"layer": "common",
"content": "...current effective text...",
"isOverride": false,
"defaultContent": "...server file text..."
}
]
}
```

**Notes**:
- Built-in layers always appear (common, default, daily, weekly, monthly) for both categories
- User-defined custom layers also appear when saved
- `isOverride: true` means a `f_prompt_config` row exists for this user + category + layer
- `defaultContent` always contains the server file text for Reset support

---

### PUT `/api/extended/prompts`

Save or update a single prompt configuration for the authenticated user. Creates a new `f_prompt_config` row or updates the existing one (upsert on `(user_id, category, layer)`).

**Auth**: JWT required

**Request body**:
```json
{
"category": "summary",
"layer": "monthly",
"content": "This is a Monthly Log for bullet journaling. Summarise by week..."
}
```

**Validation**:
- `category`: required, must be `"ocr"` or `"summary"`
- `layer`: required, 1–64 characters, alphanumeric + hyphens only
- `content`: required, must not be empty or whitespace-only

**Response 200**:
```json
{
"success": true
}
```

**Response 400** (validation failure):
```json
{
"success": false,
"errorCode": "INVALID_INPUT",
"errorMsg": "content must not be empty"
}
```

---

### DELETE `/api/extended/prompts/{category}/{layer}`

Remove the user's saved override for a specific `(category, layer)`, reverting it to the server default. For user-defined custom layers, this fully removes the type. For built-in layers, this just removes the override row.

**Auth**: JWT required

**Path parameters**:
- `category`: `"ocr"` or `"summary"`
- `layer`: layer name

**Response 200**:
```json
{
"success": true
}
```

**Response 404** (no override exists to delete):
```json
{
"success": false,
"errorCode": "NOT_FOUND",
"errorMsg": "No override found for ocr/monthly"
}
```

---

## Staleness & Reprocess Endpoints

### GET `/api/extended/files/{file_id}/staleness`

Computes the current effective prompt hash for this file's note type and compares it against the stored `prompt_hash` on each page. Returns per-page staleness status.

**Auth**: JWT required. User must own the file (403 otherwise).

**Response 200**:
```json
{
"success": true,
"currentPromptHash": "a3f1e9c2...",
"staleCount": 2,
"totalCount": 12,
"pages": [
{
"pageId": "P20231027120000abc",
"pageIndex": 0,
"storedHash": "a3f1e9c2...",
"isStale": false
},
{
"pageId": "P20231028090000xyz",
"pageIndex": 1,
"storedHash": null,
"isStale": true
}
]
}
```

**Notes**:
- `storedHash: null` means the page was processed before this feature; treated as stale
- If the file has no processed pages yet, returns `staleCount: 0, totalCount: 0`

---

### POST `/api/extended/files/{file_id}/reprocess`

Queues stale pages of a note for reprocessing. Resets `SystemTaskDO` status for `OCR_EXTRACTION`, `EMBEDDING_GENERATION` (per page) and `SUMMARY_GENERATION` (global) for stale pages only, then enqueues the file.

**Auth**: JWT required. User must own the file (403 otherwise).

**Request body** (optional):
```json
{
"pageIds": ["P20231028090000xyz"]
}
```
If `pageIds` is omitted or null, all stale pages for the file are queued.

**Validation**:
- If `pageIds` is provided, only pages with `is_stale: true` are accepted; non-stale page IDs in the list are silently skipped (consistent with spec FR-018).

**Response 200**:
```json
{
"success": true,
"queuedPageCount": 1
}
```

**Response 409** (processing already in progress for this file):
```json
{
"success": false,
"errorCode": "ALREADY_PROCESSING",
"errorMsg": "This file is already queued for processing"
}
```

---

### POST `/api/extended/files/{file_id}/pages/{page_id}/reprocess`

Queues a single page for reprocessing. Resets task status for OCR and embedding for the specified page, and resets the summary task at file level.

**Auth**: JWT required. User must own the file (403 otherwise).

**Response 200**:
```json
{
"success": true,
"queuedPageCount": 1
}
```

**Response 400** (page is not stale):
```json
{
"success": false,
"errorCode": "NOT_STALE",
"errorMsg": "This page does not require reprocessing"
}
```
Loading
Loading