diff --git a/.claude/commands/openspec/apply.md b/.claude/commands/openspec/apply.md deleted file mode 100644 index a36fd96..0000000 --- a/.claude/commands/openspec/apply.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -name: OpenSpec: Apply -description: Implement an approved OpenSpec change and keep tasks in sync. -category: OpenSpec -tags: [openspec, apply] ---- - -**Guardrails** -- Favor straightforward, minimal implementations first and add complexity only when it is requested or clearly required. -- Keep changes tightly scoped to the requested outcome. -- Refer to `openspec/AGENTS.md` (located inside the `openspec/` directory—run `ls openspec` or `openspec update` if you don't see it) if you need additional OpenSpec conventions or clarifications. - -**Steps** -Track these steps as TODOs and complete them one by one. -1. Read `changes//proposal.md`, `design.md` (if present), and `tasks.md` to confirm scope and acceptance criteria. -2. Work through tasks sequentially, keeping edits minimal and focused on the requested change. -3. Confirm completion before updating statuses—make sure every item in `tasks.md` is finished. -4. Update the checklist after all work is done so each task is marked `- [x]` and reflects reality. -5. Reference `openspec list` or `openspec show ` when additional context is required. - -**Reference** -- Use `openspec show --json --deltas-only` if you need additional context from the proposal while implementing. - diff --git a/.claude/commands/openspec/archive.md b/.claude/commands/openspec/archive.md deleted file mode 100644 index dbc7695..0000000 --- a/.claude/commands/openspec/archive.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -name: OpenSpec: Archive -description: Archive a deployed OpenSpec change and update specs. -category: OpenSpec -tags: [openspec, archive] ---- - -**Guardrails** -- Favor straightforward, minimal implementations first and add complexity only when it is requested or clearly required. -- Keep changes tightly scoped to the requested outcome. -- Refer to `openspec/AGENTS.md` (located inside the `openspec/` directory—run `ls openspec` or `openspec update` if you don't see it) if you need additional OpenSpec conventions or clarifications. - -**Steps** -1. Determine the change ID to archive: - - If this prompt already includes a specific change ID (for example inside a `` block populated by slash-command arguments), use that value after trimming whitespace. - - If the conversation references a change loosely (for example by title or summary), run `openspec list` to surface likely IDs, share the relevant candidates, and confirm which one the user intends. - - Otherwise, review the conversation, run `openspec list`, and ask the user which change to archive; wait for a confirmed change ID before proceeding. - - If you still cannot identify a single change ID, stop and tell the user you cannot archive anything yet. -2. Validate the change ID by running `openspec list` (or `openspec show `) and stop if the change is missing, already archived, or otherwise not ready to archive. -3. Run `openspec archive --yes` so the CLI moves the change and applies spec updates without prompts (use `--skip-specs` only for tooling-only work). -4. Review the command output to confirm the target specs were updated and the change landed in `changes/archive/`. -5. Validate with `openspec validate --strict` and inspect with `openspec show ` if anything looks off. - -**Reference** -- Use `openspec list` to confirm change IDs before archiving. -- Inspect refreshed specs with `openspec list --specs` and address any validation issues before handing off. - diff --git a/.claude/commands/openspec/proposal.md b/.claude/commands/openspec/proposal.md deleted file mode 100644 index cbb75ce..0000000 --- a/.claude/commands/openspec/proposal.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -name: OpenSpec: Proposal -description: Scaffold a new OpenSpec change and validate strictly. -category: OpenSpec -tags: [openspec, change] ---- - -**Guardrails** -- Favor straightforward, minimal implementations first and add complexity only when it is requested or clearly required. -- Keep changes tightly scoped to the requested outcome. -- Refer to `openspec/AGENTS.md` (located inside the `openspec/` directory—run `ls openspec` or `openspec update` if you don't see it) if you need additional OpenSpec conventions or clarifications. -- Identify any vague or ambiguous details and ask the necessary follow-up questions before editing files. -- Do not write any code during the proposal stage. Only create design documents (proposal.md, tasks.md, design.md, and spec deltas). Implementation happens in the apply stage after approval. - -**Steps** -1. Review `openspec/project.md`, run `openspec list` and `openspec list --specs`, and inspect related code or docs (e.g., via `rg`/`ls`) to ground the proposal in current behaviour; note any gaps that require clarification. -2. Choose a unique verb-led `change-id` and scaffold `proposal.md`, `tasks.md`, and `design.md` (when needed) under `openspec/changes//`. -3. Map the change into concrete capabilities or requirements, breaking multi-scope efforts into distinct spec deltas with clear relationships and sequencing. -4. Capture architectural reasoning in `design.md` when the solution spans multiple systems, introduces new patterns, or demands trade-off discussion before committing to specs. -5. Draft spec deltas in `changes//specs//spec.md` (one folder per capability) using `## ADDED|MODIFIED|REMOVED Requirements` with at least one `#### Scenario:` per requirement and cross-reference related capabilities when relevant. -6. Draft `tasks.md` as an ordered list of small, verifiable work items that deliver user-visible progress, include validation (tests, tooling), and highlight dependencies or parallelizable work. -7. Validate with `openspec validate --strict` and resolve every issue before sharing the proposal. - -**Reference** -- Use `openspec show --json --deltas-only` or `openspec show --type spec` to inspect details when validation fails. -- Search existing requirements with `rg -n "Requirement:|Scenario:" openspec/specs` before writing new ones. -- Explore the codebase with `rg `, `ls`, or direct file reads so proposals align with current implementation realities. - diff --git a/.mcp.json b/.mcp.json deleted file mode 100644 index 5b897bc..0000000 --- a/.mcp.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "mcpServers": { - "playwright": { - "type": "stdio", - "command": "npx", - "args": [ - "@playwright/mcp@latest" - ], - "env": {} - }, - "vscode-lsp": { - "type": "stdio", - "command": "./mcp-lsp-proxy.sh" - } - } -} diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index 0669699..0000000 --- a/AGENTS.md +++ /dev/null @@ -1,18 +0,0 @@ - -# OpenSpec Instructions - -These instructions are for AI assistants working in this project. - -Always open `@/openspec/AGENTS.md` when the request: -- Mentions planning or proposals (words like proposal, spec, change, plan) -- Introduces new capabilities, breaking changes, architecture shifts, or big performance/security work -- Sounds ambiguous and you need the authoritative spec before coding - -Use `@/openspec/AGENTS.md` to learn: -- How to create and apply change proposals -- Spec format and conventions -- Project structure and guidelines - -Keep this managed block so 'openspec update' can refresh the instructions. - - \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 0669699..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,18 +0,0 @@ - -# OpenSpec Instructions - -These instructions are for AI assistants working in this project. - -Always open `@/openspec/AGENTS.md` when the request: -- Mentions planning or proposals (words like proposal, spec, change, plan) -- Introduces new capabilities, breaking changes, architecture shifts, or big performance/security work -- Sounds ambiguous and you need the authoritative spec before coding - -Use `@/openspec/AGENTS.md` to learn: -- How to create and apply change proposals -- Spec format and conventions -- Project structure and guidelines - -Keep this managed block so 'openspec update' can refresh the instructions. - - \ No newline at end of file diff --git a/Technical Specifications.txt b/Technical Specifications.txt deleted file mode 100644 index 4b4ef51..0000000 --- a/Technical Specifications.txt +++ /dev/null @@ -1,29 +0,0 @@ -1. 系統架構 (System Architecture) -採用三層式架構 (3-Tier Architecture) 以確保未來擴展性(如未來可對接 MES/ERP 數據)。 - -前端 (Presentation Layer): React.js 或 Vue.js (推薦 React,其生態系在處理複雜管理後台較成熟)。 - -後端 (Application Layer): Python (FastAPI) ;Python 方便未來導入 AI 風險預測與數據分析。 - -數據層 (Data Layer): * 主資料庫: mysql (關聯式結構最適合處理專案間的複雜層級與相依性)。 - -快取與即時通訊: Redis (用於即時通知推播與任務狀態鎖定)。 - -2. 核心數據模型設計 (ERD Concept) -User: ID, Name, Department, Role, Skills, Capacity. - -Project: ID, Title, Owner, Budget, Timeline, Security_Level. - -Task: ID, Project_ID, Assignee, Priority, Status, Original_Estimate, Time_Spent, Blocker_Flag. - -Attachment: ID, Task_ID, Version, File_Path (支援地端 NAS 存儲)。 - -3. 關鍵技術功能設計 -即時同步 (Real-time Sync): 使用 WebSocket,當多人同時編輯同一個專案看板時,狀態能即時更新而不需刷新頁面。 - -文件加密: 針對半導體敏感圖檔,存儲層進行 AES-256 加密,且下載時自動加上使用者浮水印。 - -整合介面 (Integrations): - -SSO: 整合企業內部 Windows AD。(使用https://pj-auth-api.vercel.app) - diff --git a/mcp-lsp-proxy.sh b/mcp-lsp-proxy.sh deleted file mode 100755 index 20b0ba2..0000000 --- a/mcp-lsp-proxy.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash - -# Add local bin to PATH -export PATH="$HOME/.local/bin:$PATH" - -# Check if mcp-proxy is installed, if not install it -if ! command -v mcp-proxy &> /dev/null; then - echo "mcp-proxy not found, installing..." >&2 - "$HOME/.local/bin/uv" tool install mcp-proxy || uv tool install mcp-proxy -fi - -# Get the port from environment variable, default to 37140 if not set -if [ ! -z "${LSP_MCP_PORT}" ]; then - PORT="${LSP_MCP_PORT}" -else - # Look for a file in the current dir called .lsp_mcp.port and get the port from there - if [ -f .lsp_mcp.port ]; then - PORT=$(cat .lsp_mcp.port) - else - PORT=37140 - fi -fi - -# Call mcp-proxy to connect to the LSP server -exec mcp-proxy --transport streamablehttp "http://localhost:${PORT}" diff --git a/openspec/AGENTS.md b/openspec/AGENTS.md deleted file mode 100644 index 96ab0bb..0000000 --- a/openspec/AGENTS.md +++ /dev/null @@ -1,456 +0,0 @@ -# OpenSpec Instructions - -Instructions for AI coding assistants using OpenSpec for spec-driven development. - -## TL;DR Quick Checklist - -- Search existing work: `openspec spec list --long`, `openspec list` (use `rg` only for full-text search) -- Decide scope: new capability vs modify existing capability -- Pick a unique `change-id`: kebab-case, verb-led (`add-`, `update-`, `remove-`, `refactor-`) -- Scaffold: `proposal.md`, `tasks.md`, `design.md` (only if needed), and delta specs per affected capability -- Write deltas: use `## ADDED|MODIFIED|REMOVED|RENAMED Requirements`; include at least one `#### Scenario:` per requirement -- Validate: `openspec validate [change-id] --strict` and fix issues -- Request approval: Do not start implementation until proposal is approved - -## Three-Stage Workflow - -### Stage 1: Creating Changes -Create proposal when you need to: -- Add features or functionality -- Make breaking changes (API, schema) -- Change architecture or patterns -- Optimize performance (changes behavior) -- Update security patterns - -Triggers (examples): -- "Help me create a change proposal" -- "Help me plan a change" -- "Help me create a proposal" -- "I want to create a spec proposal" -- "I want to create a spec" - -Loose matching guidance: -- Contains one of: `proposal`, `change`, `spec` -- With one of: `create`, `plan`, `make`, `start`, `help` - -Skip proposal for: -- Bug fixes (restore intended behavior) -- Typos, formatting, comments -- Dependency updates (non-breaking) -- Configuration changes -- Tests for existing behavior - -**Workflow** -1. Review `openspec/project.md`, `openspec list`, and `openspec list --specs` to understand current context. -2. Choose a unique verb-led `change-id` and scaffold `proposal.md`, `tasks.md`, optional `design.md`, and spec deltas under `openspec/changes//`. -3. Draft spec deltas using `## ADDED|MODIFIED|REMOVED Requirements` with at least one `#### Scenario:` per requirement. -4. Run `openspec validate --strict` and resolve any issues before sharing the proposal. - -### Stage 2: Implementing Changes -Track these steps as TODOs and complete them one by one. -1. **Read proposal.md** - Understand what's being built -2. **Read design.md** (if exists) - Review technical decisions -3. **Read tasks.md** - Get implementation checklist -4. **Implement tasks sequentially** - Complete in order -5. **Confirm completion** - Ensure every item in `tasks.md` is finished before updating statuses -6. **Update checklist** - After all work is done, set every task to `- [x]` so the list reflects reality -7. **Approval gate** - Do not start implementation until the proposal is reviewed and approved - -### Stage 3: Archiving Changes -After deployment, create separate PR to: -- Move `changes/[name]/` → `changes/archive/YYYY-MM-DD-[name]/` -- Update `specs/` if capabilities changed -- Use `openspec archive --skip-specs --yes` for tooling-only changes (always pass the change ID explicitly) -- Run `openspec validate --strict` to confirm the archived change passes checks - -## Before Any Task - -**Context Checklist:** -- [ ] Read relevant specs in `specs/[capability]/spec.md` -- [ ] Check pending changes in `changes/` for conflicts -- [ ] Read `openspec/project.md` for conventions -- [ ] Run `openspec list` to see active changes -- [ ] Run `openspec list --specs` to see existing capabilities - -**Before Creating Specs:** -- Always check if capability already exists -- Prefer modifying existing specs over creating duplicates -- Use `openspec show [spec]` to review current state -- If request is ambiguous, ask 1–2 clarifying questions before scaffolding - -### Search Guidance -- Enumerate specs: `openspec spec list --long` (or `--json` for scripts) -- Enumerate changes: `openspec list` (or `openspec change list --json` - deprecated but available) -- Show details: - - Spec: `openspec show --type spec` (use `--json` for filters) - - Change: `openspec show --json --deltas-only` -- Full-text search (use ripgrep): `rg -n "Requirement:|Scenario:" openspec/specs` - -## Quick Start - -### CLI Commands - -```bash -# Essential commands -openspec list # List active changes -openspec list --specs # List specifications -openspec show [item] # Display change or spec -openspec validate [item] # Validate changes or specs -openspec archive [--yes|-y] # Archive after deployment (add --yes for non-interactive runs) - -# Project management -openspec init [path] # Initialize OpenSpec -openspec update [path] # Update instruction files - -# Interactive mode -openspec show # Prompts for selection -openspec validate # Bulk validation mode - -# Debugging -openspec show [change] --json --deltas-only -openspec validate [change] --strict -``` - -### Command Flags - -- `--json` - Machine-readable output -- `--type change|spec` - Disambiguate items -- `--strict` - Comprehensive validation -- `--no-interactive` - Disable prompts -- `--skip-specs` - Archive without spec updates -- `--yes`/`-y` - Skip confirmation prompts (non-interactive archive) - -## Directory Structure - -``` -openspec/ -├── project.md # Project conventions -├── specs/ # Current truth - what IS built -│ └── [capability]/ # Single focused capability -│ ├── spec.md # Requirements and scenarios -│ └── design.md # Technical patterns -├── changes/ # Proposals - what SHOULD change -│ ├── [change-name]/ -│ │ ├── proposal.md # Why, what, impact -│ │ ├── tasks.md # Implementation checklist -│ │ ├── design.md # Technical decisions (optional; see criteria) -│ │ └── specs/ # Delta changes -│ │ └── [capability]/ -│ │ └── spec.md # ADDED/MODIFIED/REMOVED -│ └── archive/ # Completed changes -``` - -## Creating Change Proposals - -### Decision Tree - -``` -New request? -├─ Bug fix restoring spec behavior? → Fix directly -├─ Typo/format/comment? → Fix directly -├─ New feature/capability? → Create proposal -├─ Breaking change? → Create proposal -├─ Architecture change? → Create proposal -└─ Unclear? → Create proposal (safer) -``` - -### Proposal Structure - -1. **Create directory:** `changes/[change-id]/` (kebab-case, verb-led, unique) - -2. **Write proposal.md:** -```markdown -# Change: [Brief description of change] - -## Why -[1-2 sentences on problem/opportunity] - -## What Changes -- [Bullet list of changes] -- [Mark breaking changes with **BREAKING**] - -## Impact -- Affected specs: [list capabilities] -- Affected code: [key files/systems] -``` - -3. **Create spec deltas:** `specs/[capability]/spec.md` -```markdown -## ADDED Requirements -### Requirement: New Feature -The system SHALL provide... - -#### Scenario: Success case -- **WHEN** user performs action -- **THEN** expected result - -## MODIFIED Requirements -### Requirement: Existing Feature -[Complete modified requirement] - -## REMOVED Requirements -### Requirement: Old Feature -**Reason**: [Why removing] -**Migration**: [How to handle] -``` -If multiple capabilities are affected, create multiple delta files under `changes/[change-id]/specs//spec.md`—one per capability. - -4. **Create tasks.md:** -```markdown -## 1. Implementation -- [ ] 1.1 Create database schema -- [ ] 1.2 Implement API endpoint -- [ ] 1.3 Add frontend component -- [ ] 1.4 Write tests -``` - -5. **Create design.md when needed:** -Create `design.md` if any of the following apply; otherwise omit it: -- Cross-cutting change (multiple services/modules) or a new architectural pattern -- New external dependency or significant data model changes -- Security, performance, or migration complexity -- Ambiguity that benefits from technical decisions before coding - -Minimal `design.md` skeleton: -```markdown -## Context -[Background, constraints, stakeholders] - -## Goals / Non-Goals -- Goals: [...] -- Non-Goals: [...] - -## Decisions -- Decision: [What and why] -- Alternatives considered: [Options + rationale] - -## Risks / Trade-offs -- [Risk] → Mitigation - -## Migration Plan -[Steps, rollback] - -## Open Questions -- [...] -``` - -## Spec File Format - -### Critical: Scenario Formatting - -**CORRECT** (use #### headers): -```markdown -#### Scenario: User login success -- **WHEN** valid credentials provided -- **THEN** return JWT token -``` - -**WRONG** (don't use bullets or bold): -```markdown -- **Scenario: User login** ❌ -**Scenario**: User login ❌ -### Scenario: User login ❌ -``` - -Every requirement MUST have at least one scenario. - -### Requirement Wording -- Use SHALL/MUST for normative requirements (avoid should/may unless intentionally non-normative) - -### Delta Operations - -- `## ADDED Requirements` - New capabilities -- `## MODIFIED Requirements` - Changed behavior -- `## REMOVED Requirements` - Deprecated features -- `## RENAMED Requirements` - Name changes - -Headers matched with `trim(header)` - whitespace ignored. - -#### When to use ADDED vs MODIFIED -- ADDED: Introduces a new capability or sub-capability that can stand alone as a requirement. Prefer ADDED when the change is orthogonal (e.g., adding "Slash Command Configuration") rather than altering the semantics of an existing requirement. -- MODIFIED: Changes the behavior, scope, or acceptance criteria of an existing requirement. Always paste the full, updated requirement content (header + all scenarios). The archiver will replace the entire requirement with what you provide here; partial deltas will drop previous details. -- RENAMED: Use when only the name changes. If you also change behavior, use RENAMED (name) plus MODIFIED (content) referencing the new name. - -Common pitfall: Using MODIFIED to add a new concern without including the previous text. This causes loss of detail at archive time. If you aren’t explicitly changing the existing requirement, add a new requirement under ADDED instead. - -Authoring a MODIFIED requirement correctly: -1) Locate the existing requirement in `openspec/specs//spec.md`. -2) Copy the entire requirement block (from `### Requirement: ...` through its scenarios). -3) Paste it under `## MODIFIED Requirements` and edit to reflect the new behavior. -4) Ensure the header text matches exactly (whitespace-insensitive) and keep at least one `#### Scenario:`. - -Example for RENAMED: -```markdown -## RENAMED Requirements -- FROM: `### Requirement: Login` -- TO: `### Requirement: User Authentication` -``` - -## Troubleshooting - -### Common Errors - -**"Change must have at least one delta"** -- Check `changes/[name]/specs/` exists with .md files -- Verify files have operation prefixes (## ADDED Requirements) - -**"Requirement must have at least one scenario"** -- Check scenarios use `#### Scenario:` format (4 hashtags) -- Don't use bullet points or bold for scenario headers - -**Silent scenario parsing failures** -- Exact format required: `#### Scenario: Name` -- Debug with: `openspec show [change] --json --deltas-only` - -### Validation Tips - -```bash -# Always use strict mode for comprehensive checks -openspec validate [change] --strict - -# Debug delta parsing -openspec show [change] --json | jq '.deltas' - -# Check specific requirement -openspec show [spec] --json -r 1 -``` - -## Happy Path Script - -```bash -# 1) Explore current state -openspec spec list --long -openspec list -# Optional full-text search: -# rg -n "Requirement:|Scenario:" openspec/specs -# rg -n "^#|Requirement:" openspec/changes - -# 2) Choose change id and scaffold -CHANGE=add-two-factor-auth -mkdir -p openspec/changes/$CHANGE/{specs/auth} -printf "## Why\n...\n\n## What Changes\n- ...\n\n## Impact\n- ...\n" > openspec/changes/$CHANGE/proposal.md -printf "## 1. Implementation\n- [ ] 1.1 ...\n" > openspec/changes/$CHANGE/tasks.md - -# 3) Add deltas (example) -cat > openspec/changes/$CHANGE/specs/auth/spec.md << 'EOF' -## ADDED Requirements -### Requirement: Two-Factor Authentication -Users MUST provide a second factor during login. - -#### Scenario: OTP required -- **WHEN** valid credentials are provided -- **THEN** an OTP challenge is required -EOF - -# 4) Validate -openspec validate $CHANGE --strict -``` - -## Multi-Capability Example - -``` -openspec/changes/add-2fa-notify/ -├── proposal.md -├── tasks.md -└── specs/ - ├── auth/ - │ └── spec.md # ADDED: Two-Factor Authentication - └── notifications/ - └── spec.md # ADDED: OTP email notification -``` - -auth/spec.md -```markdown -## ADDED Requirements -### Requirement: Two-Factor Authentication -... -``` - -notifications/spec.md -```markdown -## ADDED Requirements -### Requirement: OTP Email Notification -... -``` - -## Best Practices - -### Simplicity First -- Default to <100 lines of new code -- Single-file implementations until proven insufficient -- Avoid frameworks without clear justification -- Choose boring, proven patterns - -### Complexity Triggers -Only add complexity with: -- Performance data showing current solution too slow -- Concrete scale requirements (>1000 users, >100MB data) -- Multiple proven use cases requiring abstraction - -### Clear References -- Use `file.ts:42` format for code locations -- Reference specs as `specs/auth/spec.md` -- Link related changes and PRs - -### Capability Naming -- Use verb-noun: `user-auth`, `payment-capture` -- Single purpose per capability -- 10-minute understandability rule -- Split if description needs "AND" - -### Change ID Naming -- Use kebab-case, short and descriptive: `add-two-factor-auth` -- Prefer verb-led prefixes: `add-`, `update-`, `remove-`, `refactor-` -- Ensure uniqueness; if taken, append `-2`, `-3`, etc. - -## Tool Selection Guide - -| Task | Tool | Why | -|------|------|-----| -| Find files by pattern | Glob | Fast pattern matching | -| Search code content | Grep | Optimized regex search | -| Read specific files | Read | Direct file access | -| Explore unknown scope | Task | Multi-step investigation | - -## Error Recovery - -### Change Conflicts -1. Run `openspec list` to see active changes -2. Check for overlapping specs -3. Coordinate with change owners -4. Consider combining proposals - -### Validation Failures -1. Run with `--strict` flag -2. Check JSON output for details -3. Verify spec file format -4. Ensure scenarios properly formatted - -### Missing Context -1. Read project.md first -2. Check related specs -3. Review recent archives -4. Ask for clarification - -## Quick Reference - -### Stage Indicators -- `changes/` - Proposed, not yet built -- `specs/` - Built and deployed -- `archive/` - Completed changes - -### File Purposes -- `proposal.md` - Why and what -- `tasks.md` - Implementation steps -- `design.md` - Technical decisions -- `spec.md` - Requirements and behavior - -### CLI Essentials -```bash -openspec list # What's in progress? -openspec show [item] # View details -openspec validate --strict # Is it correct? -openspec archive [--yes|-y] # Mark complete (add --yes for automation) -``` - -Remember: Specs are truth. Changes are proposals. Keep them in sync. diff --git a/openspec/changes/archive/2025-12-28-add-resource-workload/design.md b/openspec/changes/archive/2025-12-28-add-resource-workload/design.md deleted file mode 100644 index 6d152aa..0000000 --- a/openspec/changes/archive/2025-12-28-add-resource-workload/design.md +++ /dev/null @@ -1,241 +0,0 @@ -# Design: add-resource-workload - -## Architecture Overview - -``` -┌─────────────┐ ┌─────────────────────┐ ┌───────────────┐ -│ Frontend │────▶│ Workload API │────▶│ MySQL │ -│ (Heatmap) │ │ /api/v1/workload │ │ (Snapshots) │ -└─────────────┘ └─────────────────────┘ └───────────────┘ - │ - ▼ - ┌───────────────┐ - │ Redis │ - │ (Cache) │ - └───────────────┘ -``` - -## Key Design Decisions - -### 1. 負載計算策略:即時計算 vs 快照 - -**決定**:採用**混合策略** -- **即時計算**:API 請求時計算,結果快取 1 小時 -- **快照儲存**:每日凌晨儲存歷史快照供趨勢分析 - -**理由**: -- 即時計算確保數據新鮮度 -- 快照提供歷史趨勢分析能力 -- Redis 快取減少計算負擔 - -### 2. 週邊界定義 - -**決定**:採用 **ISO 8601 週**(週一至週日) - -**理由**: -- 國際標準,避免歧義 -- Python/MySQL 原生支援 -- 便於未來國際化 - -### 3. 負載計算公式 - -``` -週負載 = Σ(該週到期任務的 original_estimate) / 週容量 × 100% -``` - -**任務計入規則**: -- `due_date` 在該週範圍內 -- `assignee_id` 為目標使用者 -- `status` 非已完成狀態 - -**邊界情況處理**: -- `original_estimate` 為空:計為 0(不計入負載) -- `capacity` 為 0:顯示為 N/A(避免除以零) - -### 4. 負載等級閾值 - -| 等級 | 範圍 | 顏色 | 描述 | -|------|------|------|------| -| normal | 0-79% | green | 正常 | -| warning | 80-99% | yellow | 警告 | -| overloaded | ≥100% | red | 超載 | - -### 5. 快取策略 - -``` -快取鍵格式:workload:{user_id}:{week_start} -TTL:3600 秒(1 小時) -``` - -**失效時機**: -- 任務建立/更新/刪除 -- 使用者容量變更 - -**實作**:暫不實作主動失效,依賴 TTL 自然過期(Phase 1 簡化) - -### 6. 權限控制 - -| 角色 | 可查看範圍 | -|------|-----------| -| super_admin | 所有使用者 | -| manager | 同部門使用者 | -| engineer | 僅自己 | - -## API Design - -### GET /api/v1/workload/heatmap - -查詢團隊負載熱圖 - -**Query Parameters**: -- `week_start`: ISO 日期(預設當週一) -- `department_id`: 部門篩選(可選) -- `user_ids`: 使用者 ID 陣列(可選) - -**Response**: -```json -{ - "week_start": "2024-01-01", - "week_end": "2024-01-07", - "users": [ - { - "user_id": "uuid", - "user_name": "John Doe", - "department_id": "uuid", - "department_name": "R&D", - "capacity_hours": 40.0, - "allocated_hours": 32.5, - "load_percentage": 81.25, - "load_level": "warning", - "task_count": 5 - } - ] -} -``` - -### GET /api/v1/workload/user/{user_id} - -查詢特定使用者的負載詳情 - -**Query Parameters**: -- `week_start`: ISO 日期 - -**Response**: -```json -{ - "user_id": "uuid", - "week_start": "2024-01-01", - "capacity_hours": 40.0, - "allocated_hours": 32.5, - "load_percentage": 81.25, - "load_level": "warning", - "tasks": [ - { - "task_id": "uuid", - "title": "Task Name", - "project_name": "Project A", - "due_date": "2024-01-05", - "original_estimate": 8.0, - "status": "in_progress" - } - ] -} -``` - -### PUT /api/v1/users/{user_id}/capacity - -更新使用者容量 - -**Request Body**: -```json -{ - "capacity": 32.0, - "effective_from": "2024-01-08", - "effective_until": "2024-01-14", - "reason": "年假" -} -``` - -## Data Model - -### pjctrl_workload_snapshots - -儲存歷史負載快照 - -```sql -CREATE TABLE pjctrl_workload_snapshots ( - id VARCHAR(36) PRIMARY KEY, - user_id VARCHAR(36) NOT NULL, - week_start DATE NOT NULL, - allocated_hours DECIMAL(8,2) NOT NULL DEFAULT 0, - capacity_hours DECIMAL(8,2) NOT NULL DEFAULT 40, - load_percentage DECIMAL(5,2) NOT NULL DEFAULT 0, - task_count INT NOT NULL DEFAULT 0, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - - FOREIGN KEY (user_id) REFERENCES pjctrl_users(id) ON DELETE CASCADE, - UNIQUE KEY uk_user_week (user_id, week_start), - INDEX idx_week_start (week_start) -); -``` - -### pjctrl_capacity_adjustments(可選,Phase 1 暫不實作) - -儲存臨時容量調整(如請假) - -```sql -CREATE TABLE pjctrl_capacity_adjustments ( - id VARCHAR(36) PRIMARY KEY, - user_id VARCHAR(36) NOT NULL, - week_start DATE NOT NULL, - adjusted_capacity DECIMAL(5,2) NOT NULL, - reason VARCHAR(200), - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - - FOREIGN KEY (user_id) REFERENCES pjctrl_users(id) ON DELETE CASCADE, - UNIQUE KEY uk_user_week (user_id, week_start) -); -``` - -## Implementation Notes - -### 後端結構 - -``` -backend/app/ -├── api/ -│ └── workload/ -│ ├── __init__.py -│ └── router.py -├── services/ -│ └── workload_service.py -├── models/ -│ └── workload_snapshot.py -└── schemas/ - └── workload.py -``` - -### 週起始日計算 - -```python -from datetime import date, timedelta - -def get_week_start(d: date) -> date: - """取得 ISO 週的週一""" - return d - timedelta(days=d.weekday()) -``` - -### Redis 快取範例 - -```python -async def get_user_workload(user_id: str, week_start: date) -> dict: - cache_key = f"workload:{user_id}:{week_start}" - cached = redis_client.get(cache_key) - if cached: - return json.loads(cached) - - result = calculate_workload(user_id, week_start) - redis_client.setex(cache_key, 3600, json.dumps(result)) - return result -``` diff --git a/openspec/changes/archive/2025-12-28-add-resource-workload/proposal.md b/openspec/changes/archive/2025-12-28-add-resource-workload/proposal.md deleted file mode 100644 index 4f5344a..0000000 --- a/openspec/changes/archive/2025-12-28-add-resource-workload/proposal.md +++ /dev/null @@ -1,74 +0,0 @@ -# Proposal: add-resource-workload - -## Summary - -實作資源管理系統的核心功能:負載熱圖與容量追蹤。讓主管能夠視覺化了解團隊成員的工作負載狀況,並進行資源分配決策。 - -## Motivation - -根據 `project.md` 的核心目標: -- 提供**即時資源負載與專案進度分析** -- 讓 Unit Managers 獲得**團隊工作負載可見度、資源分配熱圖** -- 減輕工程師的**時間回報負擔** - -目前系統已完成 `user-auth` 和 `task-management`,具備: -- 使用者資料與容量(capacity)欄位 -- 任務的時間估算(original_estimate)與實際耗時(time_spent) -- 任務指派(assignee_id)與截止日期(due_date) - -基於現有基礎,可以開始計算並展示資源負載資訊。 - -## Scope - -### In Scope (Phase 1 - 核心負載功能) - -1. **Workload Heatmap API** - - 計算每位成員的週負載百分比 - - 依負載狀態分類(綠色/黃色/紅色) - - 支援時間範圍查詢 - -2. **User Capacity Management** - - 更新使用者容量設定 API - - 容量歷史記錄(未來週數的容量調整,如請假) - -3. **Workload Snapshot Storage** - - 建立 `pjctrl_workload_snapshots` 表 - - 定期快照或即時計算策略 - -4. **Team Workload Overview API** - - 部門級別的負載總覽 - - 支援按專案或部門篩選 - -### Out of Scope (Future Phases) - -- Multi-Project Health Dashboard(需要更多專案進度數據) -- 資源分配不均的自動建議 -- 資源分配 AI 預測 -- WebSocket 即時更新 - -## Affected Specs - -- `resource-management` - 實作以下需求: - - Workload Heatmap(部分) - - Capacity Planning(部分) - - Team Workload Distribution(部分) - -## Dependencies - -- `user-auth` - 使用者認證與權限 -- `task-management` - 任務與時間估算數據來源 - -## Risks - -| Risk | Impact | Mitigation | -|------|--------|------------| -| 負載計算效能 | 大量任務時計算緩慢 | 使用 Redis 快取計算結果 | -| 任務時間數據不完整 | 無 original_estimate 的任務無法計算 | 提供預設值或排除計算 | -| 週邊界定義不一致 | 不同使用者對「週」的理解不同 | 統一使用 ISO 週(週一至週日)| - -## Success Criteria - -- [ ] 主管可查看團隊成員的週負載熱圖 -- [ ] 負載百分比正確反映任務分配時數 vs 容量 -- [ ] 支援按部門篩選負載視圖 -- [ ] API 回應時間 < 500ms(快取命中時 < 100ms) diff --git a/openspec/changes/archive/2025-12-28-add-resource-workload/specs/resource-management/spec.md b/openspec/changes/archive/2025-12-28-add-resource-workload/specs/resource-management/spec.md deleted file mode 100644 index 861adc0..0000000 --- a/openspec/changes/archive/2025-12-28-add-resource-workload/specs/resource-management/spec.md +++ /dev/null @@ -1,87 +0,0 @@ -# resource-management spec delta - -此變更實作 `resource-management` spec 的核心需求,補充 API 規格與實作細節。 - -## MODIFIED Requirements - -### Requirement: Workload Heatmap - -系統 SHALL 提供負載熱圖 API,自動統計每人每週分配的任務總時數,並以顏色等級表示負載狀態。 - -#### Scenario: 負載正常顯示 -- **GIVEN** 某人員本週被指派的任務總時數低於其容量的 80% -- **WHEN** 主管查詢負載熱圖 API -- **THEN** 該人員的 `load_level` 為 `normal` -- **AND** 回傳包含 `load_percentage`、`allocated_hours`、`capacity_hours` - -#### Scenario: 負載警告顯示 -- **GIVEN** 某人員本週被指派的任務總時數達到其容量的 80%-99% -- **WHEN** 主管查詢負載熱圖 API -- **THEN** 該人員的 `load_level` 為 `warning` - -#### Scenario: 負載超載顯示 -- **GIVEN** 某人員本週被指派的任務總時數達到或超過其容量的 100% -- **WHEN** 主管查詢負載熱圖 API -- **THEN** 該人員的 `load_level` 為 `overloaded` - -#### Scenario: 查詢特定週的負載 -- **GIVEN** 主管需要查看非當週的負載 -- **WHEN** 主管以 `week_start` 參數查詢負載熱圖 API -- **THEN** 系統回傳該週的負載資料 - -#### Scenario: 快取機制 -- **GIVEN** 負載資料已被計算並快取 -- **WHEN** 相同查詢在 1 小時內再次發生 -- **THEN** 系統從 Redis 快取回傳結果 - -### Requirement: Capacity Planning - -系統 SHALL 支援人員容量規劃,包含預設容量與臨時調整。 - -#### Scenario: 設定人員預設容量 -- **GIVEN** 管理者需要設定人員的週工時上限 -- **WHEN** 管理者更新使用者的 `capacity` 值 -- **THEN** 系統儲存新的容量設定 -- **AND** 後續負載計算使用新容量值 - -#### Scenario: 容量為零處理 -- **GIVEN** 使用者的容量設為 0 -- **WHEN** 系統計算該使用者的負載 -- **THEN** `load_percentage` 顯示為 `null` -- **AND** `load_level` 顯示為 `unavailable` - -### Requirement: Team Workload Distribution - -系統 SHALL 提供團隊工作分配查詢功能。 - -#### Scenario: 部門負載總覽 -- **GIVEN** 主管需要了解部門整體負載 -- **WHEN** 主管以 `department_id` 參數查詢負載熱圖 API -- **THEN** 僅顯示該部門成員的負載狀況 - -#### Scenario: 使用者負載詳情 -- **GIVEN** 主管需要了解某人的詳細任務分配 -- **WHEN** 主管查詢使用者負載詳情 API -- **THEN** 回傳該週指派給該使用者的所有任務 -- **AND** 包含每個任務的 `original_estimate` 與 `due_date` - -## ADDED Requirements - -### Requirement: Workload Data Access Control - -系統 SHALL 限制負載資料的存取權限。 - -#### Scenario: 系統管理員查看所有人 -- **GIVEN** 登入者為 `super_admin` -- **WHEN** 查詢負載熱圖 API -- **THEN** 可查看所有使用者的負載資料 - -#### Scenario: 一般使用者查看自己 -- **GIVEN** 登入者為一般使用者 -- **WHEN** 查詢負載熱圖 API 未指定 `user_ids` -- **THEN** 僅回傳自己的負載資料 - -#### Scenario: 跨部門存取拒絕 -- **GIVEN** 登入者非系統管理員 -- **WHEN** 查詢其他部門使用者的負載 -- **THEN** 系統拒絕存取並回傳 403 Forbidden diff --git a/openspec/changes/archive/2025-12-28-add-resource-workload/tasks.md b/openspec/changes/archive/2025-12-28-add-resource-workload/tasks.md deleted file mode 100644 index c886910..0000000 --- a/openspec/changes/archive/2025-12-28-add-resource-workload/tasks.md +++ /dev/null @@ -1,75 +0,0 @@ -# Tasks: add-resource-workload - -## Phase 1: 資料模型與基礎設施 - -- [x] **1.1** 建立 WorkloadSnapshot model (`backend/app/models/workload_snapshot.py`) -- [x] **1.2** 建立 Alembic migration 建立 `pjctrl_workload_snapshots` 表 -- [x] **1.3** 建立 Workload schemas (`backend/app/schemas/workload.py`) - -## Phase 2: 核心服務層 - -- [x] **2.1** 實作 `workload_service.py` 核心邏輯 - - ISO 週計算函式 - - 使用者週負載計算 - - 負載等級判定(normal/warning/overloaded) -- [x] **2.2** 實作 Redis 快取整合 - - 快取讀取/寫入 - - TTL 設定 - -## Phase 3: API 端點 - -- [x] **3.1** 建立 workload router (`backend/app/api/workload/router.py`) -- [x] **3.2** 實作 `GET /api/workload/heatmap` - 團隊負載熱圖 -- [x] **3.3** 實作 `GET /api/workload/user/{user_id}` - 使用者負載詳情 -- [x] **3.4** 實作 `GET /api/workload/me` - 當前使用者負載(替代 3.4 的容量更新) -- [x] **3.5** 註冊 workload router 至 main.py - -## Phase 4: 權限控制 - -- [x] **4.1** 實作部門隔離邏輯 - - super_admin 可查看所有 - - manager 可查看同部門 - - engineer 僅可查看自己 - -## Phase 5: 測試 - -- [x] **5.1** 單元測試:負載計算邏輯 -- [x] **5.2** 單元測試:週邊界計算 -- [x] **5.3** API 測試:heatmap 端點 -- [x] **5.4** API 測試:user workload 端點 -- [x] **5.5** API 測試:權限控制 - -## Phase 6: E2E 測試與驗證 - -- [x] **6.1** 自動化 E2E 測試:負載熱圖完整流程 - - 建立測試使用者與任務資料 - - 驗證負載計算正確性 - - 驗證負載等級判定 -- [x] **6.2** 自動化 E2E 測試:權限控制流程 - - super_admin 可查看所有人 - - 一般使用者僅能查看自己 - - 跨部門存取拒絕 -- [x] **6.3** 自動化 E2E 測試:Redis 快取驗證 - - 首次請求計算並快取 - - 二次請求命中快取 -- [x] **6.4** 更新 API 文件(OpenAPI 自動生成) - -## Dependencies - -``` -1.1, 1.2 → 可平行執行 -1.3 → 依賴 1.1 -2.1 → 依賴 1.1, 1.3 -2.2 → 依賴 2.1 -3.1-3.5 → 依賴 2.1, 2.2 -4.1 → 依賴 3.1 -5.1-5.5 → 可平行執行,依賴 Phase 3, 4 -6.1-6.4 → 依賴 Phase 3, 4, 5 -``` - -## Validation Criteria - -每個任務完成後需確認: -- 程式碼無語法錯誤 -- 相關測試通過 -- 符合現有程式碼風格 diff --git a/openspec/changes/archive/2025-12-28-add-task-management/design.md b/openspec/changes/archive/2025-12-28-add-task-management/design.md deleted file mode 100644 index 6971d82..0000000 --- a/openspec/changes/archive/2025-12-28-add-task-management/design.md +++ /dev/null @@ -1,231 +0,0 @@ -# Design: add-task-management - -## Architecture Overview - -``` -┌─────────────────────────────────────────────────────────────┐ -│ Frontend │ -│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ -│ │ Spaces │ │ Projects │ │ Tasks │ │ Task Detail │ │ -│ │ List │ │ List │ │ ListView │ │ Form │ │ -│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └──────┬───────┘ │ -└───────┼─────────────┼─────────────┼────────────────┼────────┘ - │ │ │ │ - ▼ ▼ ▼ ▼ -┌─────────────────────────────────────────────────────────────┐ -│ API Layer │ -│ ┌──────────────┐ ┌────────────────┐ ┌─────────────────┐ │ -│ │ /api/spaces │ │ /api/projects │ │ /api/tasks │ │ -│ │ router │ │ router │ │ router │ │ -│ └──────┬───────┘ └───────┬────────┘ └────────┬────────┘ │ -└─────────┼──────────────────┼────────────────────┼───────────┘ - │ │ │ - ▼ ▼ ▼ -┌─────────────────────────────────────────────────────────────┐ -│ Middleware Layer │ -│ ┌─────────────────┐ ┌─────────────────────────────────┐ │ -│ │ Auth Middleware│ │ Permission Middleware │ │ -│ │ (JWT驗證) │ │ (RBAC + Security Level) │ │ -│ └────────┬────────┘ └────────────────┬────────────────┘ │ -└───────────┼────────────────────────────┼────────────────────┘ - │ │ - ▼ ▼ -┌─────────────────────────────────────────────────────────────┐ -│ Data Layer │ -│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ -│ │ pjctrl_ │ │ pjctrl_ │ │ pjctrl_ │ │ -│ │ spaces │ │ projects │ │ tasks │ │ -│ └────────────┘ └────────────┘ └────────────┘ │ -└─────────────────────────────────────────────────────────────┘ -``` - -## Data Model Details - -### pjctrl_spaces - -空間是最上層的組織單位,用於將相關專案分組。 - -```sql -CREATE TABLE pjctrl_spaces ( - id CHAR(36) PRIMARY KEY, - name VARCHAR(200) NOT NULL, - description TEXT, - owner_id CHAR(36) NOT NULL, - is_active BOOLEAN DEFAULT true, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - FOREIGN KEY (owner_id) REFERENCES pjctrl_users(id) -); - -CREATE INDEX idx_spaces_owner ON pjctrl_spaces(owner_id); -CREATE INDEX idx_spaces_active ON pjctrl_spaces(is_active); -``` - -### pjctrl_projects - -專案屬於某個空間,具有預算、時程和安全等級設定。 - -```sql -CREATE TABLE pjctrl_projects ( - id CHAR(36) PRIMARY KEY, - space_id CHAR(36) NOT NULL, - title VARCHAR(200) NOT NULL, - description TEXT, - owner_id CHAR(36) NOT NULL, - budget DECIMAL(15, 2), - start_date DATE, - end_date DATE, - security_level ENUM('public', 'department', 'confidential') DEFAULT 'department', - status VARCHAR(50) DEFAULT 'active', - department_id CHAR(36), - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - FOREIGN KEY (space_id) REFERENCES pjctrl_spaces(id) ON DELETE CASCADE, - FOREIGN KEY (owner_id) REFERENCES pjctrl_users(id), - FOREIGN KEY (department_id) REFERENCES pjctrl_departments(id) -); - -CREATE INDEX idx_projects_space ON pjctrl_projects(space_id); -CREATE INDEX idx_projects_owner ON pjctrl_projects(owner_id); -CREATE INDEX idx_projects_department ON pjctrl_projects(department_id); -CREATE INDEX idx_projects_security ON pjctrl_projects(security_level); -``` - -### pjctrl_task_statuses - -定義專案內可用的任務狀態。 - -```sql -CREATE TABLE pjctrl_task_statuses ( - id CHAR(36) PRIMARY KEY, - project_id CHAR(36) NOT NULL, - name VARCHAR(50) NOT NULL, - color VARCHAR(7) DEFAULT '#808080', - position INT DEFAULT 0, - is_done BOOLEAN DEFAULT false, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (project_id) REFERENCES pjctrl_projects(id) ON DELETE CASCADE, - UNIQUE KEY uk_status_name (project_id, name) -); - -CREATE INDEX idx_statuses_project ON pjctrl_task_statuses(project_id); -``` - -### pjctrl_tasks - -任務是核心實體,支援父子任務關係。 - -```sql -CREATE TABLE pjctrl_tasks ( - id CHAR(36) PRIMARY KEY, - project_id CHAR(36) NOT NULL, - parent_task_id CHAR(36), - title VARCHAR(500) NOT NULL, - description TEXT, - assignee_id CHAR(36), - status_id CHAR(36), - priority ENUM('low', 'medium', 'high', 'urgent') DEFAULT 'medium', - original_estimate DECIMAL(8, 2), - time_spent DECIMAL(8, 2) DEFAULT 0, - blocker_flag BOOLEAN DEFAULT false, - due_date DATETIME, - position INT DEFAULT 0, - created_by CHAR(36) NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - FOREIGN KEY (project_id) REFERENCES pjctrl_projects(id) ON DELETE CASCADE, - FOREIGN KEY (parent_task_id) REFERENCES pjctrl_tasks(id) ON DELETE CASCADE, - FOREIGN KEY (assignee_id) REFERENCES pjctrl_users(id), - FOREIGN KEY (status_id) REFERENCES pjctrl_task_statuses(id), - FOREIGN KEY (created_by) REFERENCES pjctrl_users(id) -); - -CREATE INDEX idx_tasks_project ON pjctrl_tasks(project_id); -CREATE INDEX idx_tasks_parent ON pjctrl_tasks(parent_task_id); -CREATE INDEX idx_tasks_assignee ON pjctrl_tasks(assignee_id); -CREATE INDEX idx_tasks_status ON pjctrl_tasks(status_id); -CREATE INDEX idx_tasks_due ON pjctrl_tasks(due_date); -CREATE INDEX idx_tasks_blocker ON pjctrl_tasks(blocker_flag); -``` - -## Permission Model - -### Security Level 行為 - -| Security Level | 可見範圍 | -|----------------|----------| -| public | 所有登入使用者 | -| department | 同部門使用者 + 專案成員 | -| confidential | 僅專案成員 | - -### 權限檢查流程 - -```python -def check_project_access(user, project): - # 系統管理員可存取所有 - if user.is_system_admin: - return True - - # 專案擁有者 - if project.owner_id == user.id: - return True - - # 依 security_level 判斷 - if project.security_level == 'public': - return True - elif project.security_level == 'department': - return user.department_id == project.department_id - else: # confidential - return is_project_member(user, project) -``` - -## API Design - -### Spaces API - -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | /api/spaces | 列出使用者可見的空間 | -| POST | /api/spaces | 建立新空間 | -| GET | /api/spaces/{id} | 取得空間詳情 | -| PATCH | /api/spaces/{id} | 更新空間 | -| DELETE | /api/spaces/{id} | 刪除空間 (軟刪除) | - -### Projects API - -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | /api/spaces/{space_id}/projects | 列出空間內的專案 | -| POST | /api/spaces/{space_id}/projects | 建立新專案 | -| GET | /api/projects/{id} | 取得專案詳情 | -| PATCH | /api/projects/{id} | 更新專案 | -| DELETE | /api/projects/{id} | 刪除專案 | - -### Tasks API - -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | /api/projects/{project_id}/tasks | 列出專案內的任務 | -| POST | /api/projects/{project_id}/tasks | 建立新任務 | -| GET | /api/tasks/{id} | 取得任務詳情 | -| PATCH | /api/tasks/{id} | 更新任務 | -| DELETE | /api/tasks/{id} | 刪除任務 | -| PATCH | /api/tasks/{id}/status | 變更任務狀態 | -| PATCH | /api/tasks/{id}/assign | 指派任務 | - -## Default Task Statuses - -每個新專案自動建立以下預設狀態: - -| Name | Color | is_done | -|------|-------|---------| -| To Do | #808080 | false | -| In Progress | #0066cc | false | -| Blocked | #cc0000 | false | -| Done | #00cc66 | true | - -## Trade-offs & Decisions - -1. **子任務深度限制**: 限制為 2 層 (Task → Sub-task),避免過度巢狀造成的複雜度 -2. **軟刪除 vs 硬刪除**: Space 使用軟刪除 (is_active),Tasks 使用級聯硬刪除 -3. **狀態定義層級**: 狀態定義在 Project 層級,而非全域,提供專案彈性 diff --git a/openspec/changes/archive/2025-12-28-add-task-management/proposal.md b/openspec/changes/archive/2025-12-28-add-task-management/proposal.md deleted file mode 100644 index 06457b8..0000000 --- a/openspec/changes/archive/2025-12-28-add-task-management/proposal.md +++ /dev/null @@ -1,60 +0,0 @@ -# Proposal: add-task-management - -## Summary - -實作任務管理核心系統,包含 Space > Project > Task > Sub-task 多層級架構、基本 CRUD 操作、狀態管理與任務指派功能。 - -## Motivation - -任務管理是專案控制系統的核心功能,其他模組(資源管理、協作、自動化)都依賴於任務資料。需優先建立穩固的任務管理基礎。 - -## Scope - -### 包含 (In Scope) - -1. **資料模型** - - pjctrl_spaces: 空間管理 - - pjctrl_projects: 專案管理 - - pjctrl_tasks: 任務與子任務管理 - - pjctrl_task_statuses: 任務狀態定義 - -2. **API 端點** - - Spaces CRUD: GET/POST/PATCH/DELETE /api/spaces - - Projects CRUD: GET/POST/PATCH/DELETE /api/projects - - Tasks CRUD: GET/POST/PATCH/DELETE /api/tasks - - Task 狀態管理與指派 - -3. **權限控制** - - Space/Project 層級的存取權限 - - Security Level 隔離 (public, department, confidential) - - 任務指派權限驗證 - -4. **前端基礎** - - Space/Project 列表與詳情頁 - - Task 列表視角 (List View) - - 任務建立/編輯表單 - -### 不包含 (Out of Scope) - -- 自定義欄位 (Custom Fields) - 下一階段 -- 看板視角 (Kanban View) - 下一階段 -- 甘特圖視角 (Gantt View) - 下一階段 -- 行事曆視角 (Calendar View) - 下一階段 -- WebSocket 即時同步 - 下一階段 -- 公式欄位計算 - 下一階段 - -## Affected Specs - -- `task-management`: 新增 Purpose 區塊,補充 ADDED Requirements - -## Dependencies - -- `user-auth`: 需使用認證中間件與使用者資料 - -## Risks - -1. **資料模型複雜度**: 多層級結構可能導致查詢效能問題 - - 緩解: 適當建立索引,限制子任務深度 - -2. **權限檢查效能**: 每次 API 請求都需驗證權限 - - 緩解: Redis 快取使用者權限資料 diff --git a/openspec/changes/archive/2025-12-28-add-task-management/specs/task-management/spec.md b/openspec/changes/archive/2025-12-28-add-task-management/specs/task-management/spec.md deleted file mode 100644 index 30198e5..0000000 --- a/openspec/changes/archive/2025-12-28-add-task-management/specs/task-management/spec.md +++ /dev/null @@ -1,80 +0,0 @@ -# Task Management - -## Purpose - -任務管理核心系統,支援多層級架構 (Space > Project > Task > Sub-task)、狀態管理與任務指派功能。 - -## ADDED Requirements - -### Requirement: Hierarchical Task Structure -系統 SHALL 支援多層級任務架構:空間 (Space) > 專案 (Project) > 任務 (Task) > 子任務 (Sub-task)。 - -#### Scenario: 建立空間 -- **GIVEN** 使用者擁有建立空間的權限 -- **WHEN** 使用者建立新空間 -- **THEN** 系統建立空間並設定擁有者 -- **AND** 空間可包含多個專案 - -#### Scenario: 建立專案 -- **GIVEN** 使用者在某空間內擁有建立專案的權限 -- **WHEN** 使用者建立新專案 -- **THEN** 系統建立專案並關聯至該空間 -- **AND** 設定專案的 Owner、Budget、Timeline、Security_Level -- **AND** 自動建立預設任務狀態 (To Do, In Progress, Blocked, Done) - -#### Scenario: 建立任務與子任務 -- **GIVEN** 使用者在專案內擁有建立任務的權限 -- **WHEN** 使用者建立任務或子任務 -- **THEN** 系統建立任務並維護父子關係 -- **AND** 子任務深度限制為 2 層 - -### Requirement: Project Security Level -系統 SHALL 根據專案的安全等級 (Security Level) 控制存取權限。 - -#### Scenario: Public 專案存取 -- **GIVEN** 專案設定為 security_level = 'public' -- **WHEN** 任何已登入使用者嘗試存取 -- **THEN** 系統允許存取 - -#### Scenario: Department 專案存取 -- **GIVEN** 專案設定為 security_level = 'department' -- **WHEN** 使用者嘗試存取 -- **THEN** 系統僅允許同部門使用者或專案成員存取 - -#### Scenario: Confidential 專案存取 -- **GIVEN** 專案設定為 security_level = 'confidential' -- **WHEN** 使用者嘗試存取 -- **THEN** 系統僅允許專案成員存取 -- **AND** 系統管理員不受此限制 - -### Requirement: Task Status Management -系統 SHALL 管理任務狀態,支援專案層級的狀態定義。 - -#### Scenario: 預設狀態建立 -- **GIVEN** 使用者建立新專案 -- **WHEN** 專案建立完成 -- **THEN** 系統自動建立預設狀態: To Do, In Progress, Blocked, Done - -#### Scenario: 狀態變更 -- **GIVEN** 使用者擁有更新任務的權限 -- **WHEN** 使用者變更任務狀態 -- **THEN** 系統更新狀態並記錄變更時間 - -#### Scenario: 阻礙標記 -- **GIVEN** 任務遇到阻礙無法進行 -- **WHEN** 使用者將任務狀態變更為 "Blocked" -- **THEN** 系統設定 blocker_flag = true - -### Requirement: Task Assignment -系統 SHALL 支援任務指派與時間估算。 - -#### Scenario: 指派任務 -- **GIVEN** 使用者擁有指派任務的權限 -- **WHEN** 使用者將任務指派給某人 -- **THEN** 系統更新 assignee_id -- **AND** 任務計入被指派者的工作負載 - -#### Scenario: 時間估算與追蹤 -- **GIVEN** 任務已被指派 -- **WHEN** 使用者設定 original_estimate 與回報 time_spent -- **THEN** 系統記錄並可計算剩餘時間 diff --git a/openspec/changes/archive/2025-12-28-add-task-management/tasks.md b/openspec/changes/archive/2025-12-28-add-task-management/tasks.md deleted file mode 100644 index f5783d7..0000000 --- a/openspec/changes/archive/2025-12-28-add-task-management/tasks.md +++ /dev/null @@ -1,163 +0,0 @@ -# Tasks: add-task-management - -## 1. 資料庫模型 - -- [x] 1.1 建立 `pjctrl_spaces` migration -- [x] 1.2 建立 `pjctrl_projects` migration -- [x] 1.3 建立 `pjctrl_task_statuses` migration -- [x] 1.4 建立 `pjctrl_tasks` migration -- [x] 1.5 建立 SQLAlchemy models (Space, Project, TaskStatus, Task) -- [x] 1.6 驗證 migration 可正確執行與回滾 - -## 2. Pydantic Schemas - -- [x] 2.1 建立 Space schemas (Create, Update, Response) -- [x] 2.2 建立 Project schemas (Create, Update, Response) -- [x] 2.3 建立 TaskStatus schemas (Create, Update, Response) -- [x] 2.4 建立 Task schemas (Create, Update, Response) - -## 3. 權限中間件 - -- [x] 3.1 實作 Space 權限檢查 (`check_space_access`) -- [x] 3.2 實作 Project 權限檢查 (`check_project_access`) -- [x] 3.3 實作 Security Level 過濾邏輯 -- [x] 3.4 整合至現有 auth middleware - -## 4. Spaces API - -- [x] 4.1 實作 `GET /api/spaces` (列出可見空間) -- [x] 4.2 實作 `POST /api/spaces` (建立空間) -- [x] 4.3 實作 `GET /api/spaces/{id}` (取得詳情) -- [x] 4.4 實作 `PATCH /api/spaces/{id}` (更新空間) -- [x] 4.5 實作 `DELETE /api/spaces/{id}` (軟刪除) - -## 5. Projects API - -- [x] 5.1 實作 `GET /api/spaces/{space_id}/projects` (列出專案) -- [x] 5.2 實作 `POST /api/spaces/{space_id}/projects` (建立專案) -- [x] 5.3 實作 `GET /api/projects/{id}` (取得詳情) -- [x] 5.4 實作 `PATCH /api/projects/{id}` (更新專案) -- [x] 5.5 實作 `DELETE /api/projects/{id}` (刪除專案) -- [x] 5.6 建立專案時自動建立預設狀態 - -## 6. Tasks API - -- [x] 6.1 實作 `GET /api/projects/{project_id}/tasks` (列出任務) -- [x] 6.2 實作 `POST /api/projects/{project_id}/tasks` (建立任務) -- [x] 6.3 實作 `GET /api/tasks/{id}` (取得詳情) -- [x] 6.4 實作 `PATCH /api/tasks/{id}` (更新任務) -- [x] 6.5 實作 `DELETE /api/tasks/{id}` (刪除任務) -- [x] 6.6 實作 `PATCH /api/tasks/{id}/status` (變更狀態) -- [x] 6.7 實作 `PATCH /api/tasks/{id}/assign` (指派任務) -- [x] 6.8 實作子任務建立 (parent_task_id) - -## 7. 測試 - -- [x] 7.1 撰寫 Space API 單元測試 -- [x] 7.2 撰寫 Project API 單元測試 -- [x] 7.3 撰寫 Task API 單元測試 -- [x] 7.4 撰寫權限檢查測試 (Security Level) -- [x] 7.5 撰寫子任務層級限制測試 -- [x] 7.6 整合測試 - -## 8. 前端 (基礎) - -- [x] 8.1 建立 Spaces 列表頁面 -- [x] 8.2 建立 Projects 列表頁面 -- [x] 8.3 建立 Tasks 列表視角 (ListView) -- [x] 8.4 建立任務建立/編輯表單 -- [x] 8.5 實作任務狀態變更 -- [x] 8.6 實作任務指派功能 - -## Dependencies - -``` -1.x (資料庫) → 2.x (Schemas) → 3.x (權限) - ↓ - 4.x (Spaces API) - ↓ - 5.x (Projects API) - ↓ - 6.x (Tasks API) - ↓ - 7.x (測試) - -8.x (前端) 可與 4.x-6.x 並行開發 -``` - -## Notes - -- 所有資料表使用 `pjctrl_` 前綴 -- 子任務深度限制為 2 層 -- 每個新專案自動建立 4 個預設狀態 -- Security Level 為 'confidential' 時需檢查專案成員資格 - -## Implementation Summary - -完成日期: 2024-12-28 -E2E 測試完成日期: 2024-12-29 - -### 已建立的檔案 - -**Backend:** -- `migrations/versions/002_task_management_tables.py` - 資料庫 migration -- `app/models/space.py` - Space model -- `app/models/project.py` - Project model -- `app/models/task_status.py` - TaskStatus model -- `app/models/task.py` - Task model -- `app/schemas/space.py` - Space schemas -- `app/schemas/project.py` - Project schemas -- `app/schemas/task_status.py` - TaskStatus schemas -- `app/schemas/task.py` - Task schemas -- `app/api/spaces/router.py` - Spaces API -- `app/api/projects/router.py` - Projects API -- `app/api/tasks/router.py` - Tasks API -- `tests/test_spaces.py` - Space 測試 -- `tests/test_projects.py` - Project 測試 -- `tests/test_tasks.py` - Task 測試 - -**Frontend:** -- `src/pages/Spaces.tsx` - Spaces 列表頁面 -- `src/pages/Projects.tsx` - Projects 列表頁面 -- `src/pages/Tasks.tsx` - Tasks 列表頁面 -- `src/components/Layout.tsx` - 共用 Layout 元件 - -### 測試結果 - -36 tests passed (包含 user-auth 的 13 個測試) - -### E2E 測試結果 (2024-12-29) - -使用真實帳號 (ymirliu@panjit.com.tw) 進行端對端測試: - -**Spaces API:** -- [x] POST /api/spaces - 建立空間 -- [x] GET /api/spaces - 列出空間 -- [x] GET /api/spaces/{id} - 取得空間詳情 -- [x] PATCH /api/spaces/{id} - 更新空間 -- [x] DELETE /api/spaces/{id} - 軟刪除空間 - -**Projects API:** -- [x] POST /api/spaces/{space_id}/projects - 建立專案 -- [x] GET /api/spaces/{space_id}/projects - 列出專案 -- [x] GET /api/projects/{id} - 取得專案詳情 -- [x] PATCH /api/projects/{id} - 更新專案 -- [x] DELETE /api/projects/{id} - 刪除專案 -- [x] GET /api/projects/{id}/statuses - 驗證 4 個預設狀態自動建立 - -**Tasks API:** -- [x] POST /api/projects/{project_id}/tasks - 建立任務 -- [x] GET /api/projects/{project_id}/tasks - 列出任務 -- [x] GET /api/tasks/{id} - 取得任務詳情 -- [x] PATCH /api/tasks/{id} - 更新任務 -- [x] PATCH /api/tasks/{id}/status - 變更狀態 -- [x] PATCH /api/tasks/{id}/assign - 指派任務 -- [x] DELETE /api/tasks/{id} - 刪除任務 -- [x] 子任務建立 (Level 1) - 成功 -- [x] 子任務深度限制 (Level 2) - 正確拒絕 -- [x] subtask_count 自動計算 - 正確 - -**權限控制:** -- [x] Admin 可存取 department 級別專案 -- [x] Admin 可存取 confidential 級別專案 -- [x] Security Level 過濾邏輯運作正常 diff --git a/openspec/changes/archive/2025-12-28-add-user-auth/design.md b/openspec/changes/archive/2025-12-28-add-user-auth/design.md deleted file mode 100644 index 7531bd2..0000000 --- a/openspec/changes/archive/2025-12-28-add-user-auth/design.md +++ /dev/null @@ -1,199 +0,0 @@ -# Design: add-user-auth - -## Context - -這是專案的第一個模組,需要建立認證與授權的基礎架構。系統使用外部認證 API 進行身份驗證,本地不儲存密碼。認證成功後由本地系統核發 JWT Token 並管理 Session。 - -### Stakeholders -- 所有系統使用者(工程師、主管、PMO) -- 系統管理員 (ymirliu@panjit.com.tw) -- 外部認證 API 維護者 - -### Constraints -- 必須使用外部 API 認證,不可本地繞過 -- 資料表必須使用 `pjctrl_` 前綴 -- 需支援部門級資料隔離 - -## Goals / Non-Goals - -### Goals -- 實現安全的外部 API 認證整合 -- 建立 RBAC 權限控制框架 -- 支援部門級資料隔離 -- 預設系統管理員帳號 - -### Non-Goals -- 本地密碼儲存與驗證 -- 多因素認證 (MFA) - 由外部 API 處理 -- OAuth2 第三方登入 -- 使用者自助註冊 - -## Decisions - -### Decision 1: 認證流程架構 - -**選擇**: Frontend-to-External-API 模式 - -``` -┌─────────┐ ┌─────────────┐ ┌──────────────────────────┐ -│ User │────▶│ Frontend │────▶│ pj-auth-api.vercel.app │ -└─────────┘ └─────────────┘ └──────────────────────────┘ - │ │ - │◀────── Auth Token ──────│ - │ - ▼ - ┌─────────────┐ - │ Backend │ - │ - 驗證 Token│ - │ - 核發 JWT │ - │ - 建立 Session - └─────────────┘ -``` - -**原因**: -- 減少後端介入認證流程的攻擊面 -- 外部 API 已處理密碼安全性 -- 後端只負責驗證與授權 - -**替代方案考量**: -- Backend Proxy 模式:增加延遲與複雜度,無明顯優勢 - -### Decision 2: JWT Token 策略 - -**選擇**: 短期 Access Token + Redis Session - -```python -JWT_PAYLOAD = { - "sub": "user_id", - "email": "user@example.com", - "role": "engineer", - "department_id": "uuid", - "is_system_admin": false, - "exp": "15 minutes from now", - "iat": "now" -} -``` - -**原因**: -- 短期 Token 減少被盜用風險 -- Redis Session 可即時撤銷 -- 權限資訊嵌入 Token 減少 DB 查詢 - -### Decision 3: 權限模型 - -**選擇**: RBAC + 部門隔離混合模式 - -```python -# 權限檢查順序 -def check_permission(user, resource, action): - # 1. 系統管理員 - 全權通過 - if user.is_system_admin: - return True - - # 2. 角色權限檢查 - if not has_role_permission(user.role, action): - return False - - # 3. 部門隔離檢查 - if not is_same_department_or_allowed(user, resource): - return False - - return True -``` - -**原因**: -- 系統管理員需無限制存取(用於問題排查) -- RBAC 提供功能層面控制 -- 部門隔離提供資料層面控制 - -### Decision 4: 資料表設計 - -```sql --- 使用 UUID 而非 auto-increment --- 便於分散式環境與資料合併 - -CREATE TABLE pjctrl_roles ( - id CHAR(36) PRIMARY KEY, - name VARCHAR(50) NOT NULL UNIQUE, - permissions JSON NOT NULL, - is_system_role BOOLEAN DEFAULT FALSE, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); - -CREATE TABLE pjctrl_departments ( - id CHAR(36) PRIMARY KEY, - name VARCHAR(100) NOT NULL, - parent_id CHAR(36) REFERENCES pjctrl_departments(id), - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); - -CREATE TABLE pjctrl_users ( - id CHAR(36) PRIMARY KEY, - email VARCHAR(200) NOT NULL UNIQUE, - name VARCHAR(200) NOT NULL, - department_id CHAR(36) REFERENCES pjctrl_departments(id), - role_id CHAR(36) REFERENCES pjctrl_roles(id), - skills JSON, - capacity DECIMAL(5,2) DEFAULT 40.00, - is_active BOOLEAN DEFAULT TRUE, - is_system_admin BOOLEAN DEFAULT FALSE, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -); -``` - -## Risks / Trade-offs - -| 風險 | 影響 | 緩解措施 | -|------|------|---------| -| 外部 API 不可用 | 所有使用者無法登入 | 顯示友善錯誤訊息、記錄事件、監控告警 | -| JWT Token 洩漏 | 短期內帳號被盜用 | 15 分鐘過期、Redis 可即時撤銷 | -| 部門隔離邏輯錯誤 | 資料外洩 | 完整測試案例、程式碼審查 | - -## Migration Plan - -1. 執行資料庫 migration 建立資料表 -2. 執行 seed data 建立預設角色與管理員 -3. 部署後端認證模組 -4. 部署前端登入頁面 -5. 驗證管理員可登入 - -**Rollback**: -- 資料庫:執行 down migration -- 程式碼:回復至前一版本 - -## Environment Configuration - -```bash -# Database -MYSQL_HOST=mysql.theaken.com -MYSQL_PORT=33306 -MYSQL_USER=A060 -MYSQL_PASSWORD=<見密碼管理> -MYSQL_DATABASE=db_A060 - -# Redis (待設定) -REDIS_HOST=localhost -REDIS_PORT=6379 - -# JWT -JWT_SECRET_KEY=<生成隨機金鑰> -JWT_ALGORITHM=HS256 -JWT_EXPIRE_MINUTES=15 - -# External Auth API -AUTH_API_URL=https://pj-auth-api.vercel.app -``` - -## Admin Credentials - -- **Email**: `ymirliu@panjit.com.tw` -- **Password**: 由外部認證 API 管理(已設定於外部系統) - -## Open Questions - -1. ~~外部認證 API 的回應格式?~~ (需確認) -2. ~~Session 過期時間?~~ 建議 15 分鐘,可透過環境變數調整 -3. ~~是否需要 Refresh Token 機制?~~ 暫不需要,視實際使用情況再評估 -4. ~~MySQL 連線資訊?~~ 已提供 -5. ~~Admin 帳號密碼?~~ 已提供 (ymirliu@panjit.com.tw) diff --git a/openspec/changes/archive/2025-12-28-add-user-auth/proposal.md b/openspec/changes/archive/2025-12-28-add-user-auth/proposal.md deleted file mode 100644 index 49b747d..0000000 --- a/openspec/changes/archive/2025-12-28-add-user-auth/proposal.md +++ /dev/null @@ -1,38 +0,0 @@ -# Change: Add User Authentication & Authorization - -## Why - -系統需要使用者認證與授權機制作為所有功能的基礎。沒有認證系統,無法識別使用者身份、無法實施權限控制、無法追蹤操作記錄。這是整個專案管理系統的第一個必要模組。 - -## What Changes - -- **新增** 外部 API 認證整合 (https://pj-auth-api.vercel.app) -- **新增** JWT Token 驗證與 Session 管理 -- **新增** 使用者資料表 (`pjctrl_users`) -- **新增** 部門資料表 (`pjctrl_departments`) -- **新增** 角色資料表 (`pjctrl_roles`) -- **新增** 預設系統管理員帳號 (`ymirliu@panjit.com.tw`) -- **新增** RBAC 權限檢查中間件 -- **新增** 部門級資料隔離機制 - -## Impact - -- **Affected specs**: `user-auth` (新增) -- **Affected code**: - - Backend: `app/api/auth/`, `app/models/user.py`, `app/core/security.py` - - Frontend: `src/contexts/AuthContext.tsx`, `src/pages/Login.tsx` - - Database: Migration for `pjctrl_users`, `pjctrl_departments`, `pjctrl_roles` - -## Dependencies - -- 外部認證 API: https://pj-auth-api.vercel.app (必須可用) -- MySQL 資料庫連線 -- Redis (用於 Session 儲存) - -## Success Criteria - -1. 使用者可透過外部 API 完成登入 -2. JWT Token 正確核發與驗證 -3. 系統管理員帳號可登入並存取所有資源 -4. 非授權使用者無法存取受保護的 API -5. 部門隔離正確運作 diff --git a/openspec/changes/archive/2025-12-28-add-user-auth/specs/user-auth/spec.md b/openspec/changes/archive/2025-12-28-add-user-auth/specs/user-auth/spec.md deleted file mode 100644 index 5c18921..0000000 --- a/openspec/changes/archive/2025-12-28-add-user-auth/specs/user-auth/spec.md +++ /dev/null @@ -1,86 +0,0 @@ -## ADDED Requirements - -### Requirement: API-Based Authentication -系統 SHALL 限定使用外部認證 API (https://pj-auth-api.vercel.app) 進行登入認證,不支援其他認證方式。 - -#### Scenario: API 登入成功 -- **GIVEN** 使用者擁有有效的企業帳號 -- **WHEN** 使用者透過前端提交憑證 -- **THEN** 系統呼叫 https://pj-auth-api.vercel.app 驗證憑證 -- **AND** 驗證成功後建立 session 並回傳 JWT token - -#### Scenario: API 登入失敗 -- **GIVEN** 使用者提供無效的憑證 -- **WHEN** 使用者嘗試登入 -- **THEN** 認證 API 回傳錯誤 -- **AND** 系統拒絕登入並顯示錯誤訊息 -- **AND** 記錄失敗的登入嘗試 - -#### Scenario: 認證 API 無法連線 -- **GIVEN** 認證 API 服務無法連線 -- **WHEN** 使用者嘗試登入 -- **THEN** 系統顯示服務暫時無法使用的訊息 -- **AND** 記錄連線失敗事件 - -### Requirement: System Administrator -系統 SHALL 預設一個系統管理員帳號,擁有所有權限。系統管理員帳號必須存在於外部認證系統,且登入流程仍需透過外部認證 API;不允許本地繞過認證。 - -#### Scenario: 預設管理員帳號 -- **GIVEN** 系統初始化完成 -- **WHEN** 系統啟動 -- **THEN** 存在預設管理員帳號 ymirliu@panjit.com.tw -- **AND** 該帳號擁有 super_admin 角色 -- **AND** 該帳號不可被刪除或降級 - -#### Scenario: 管理員登入流程 -- **GIVEN** 管理員帳號 ymirliu@panjit.com.tw 需要登入 -- **WHEN** 管理員提交憑證 -- **THEN** 系統仍需呼叫 https://pj-auth-api.vercel.app 驗證 -- **AND** 不存在任何本地繞過認證的機制 -- **AND** 驗證成功後才授予 super_admin 權限 - -#### Scenario: 管理員全域權限 -- **GIVEN** 管理員帳號 ymirliu@panjit.com.tw 已通過 API 認證並登入 -- **WHEN** 管理員存取任何資源 -- **THEN** 系統允許存取,無視部門隔離限制 - -### Requirement: Role-Based Access Control -系統 SHALL 支援基於角色的存取控制 (RBAC)。 - -#### Scenario: 角色權限檢查 -- **GIVEN** 使用者被指派特定角色 -- **WHEN** 使用者嘗試存取受保護的資源 -- **THEN** 系統根據角色權限決定是否允許存取 - -#### Scenario: 角色指派 -- **GIVEN** 管理員擁有使用者管理權限 -- **WHEN** 管理員為使用者指派角色 -- **THEN** 系統更新使用者的角色設定 -- **AND** 新權限立即生效 - -### Requirement: Department Isolation -系統 SHALL 實施部門級別的資料隔離,確保跨部門資料安全。 - -#### Scenario: 部門資料隔離 -- **GIVEN** 使用者屬於研發部門 -- **WHEN** 使用者嘗試存取廠務部門的專案 -- **THEN** 系統拒絕存取並顯示無權限訊息 - -#### Scenario: 跨部門專案存取 -- **GIVEN** 專案被設定為跨部門可見 -- **WHEN** 不同部門的使用者嘗試存取該專案 -- **THEN** 系統根據專案的 Security_Level 設定決定是否允許存取 - -### Requirement: Session Management -系統 SHALL 管理使用者 session,包含過期與登出機制。 - -#### Scenario: Session 過期 -- **GIVEN** 使用者已登入系統 -- **WHEN** Session 超過設定的有效期限 -- **THEN** 系統自動使 session 失效 -- **AND** 使用者需重新登入 - -#### Scenario: 主動登出 -- **GIVEN** 使用者已登入系統 -- **WHEN** 使用者執行登出操作 -- **THEN** 系統銷毀 session 並清除 token diff --git a/openspec/changes/archive/2025-12-28-add-user-auth/tasks.md b/openspec/changes/archive/2025-12-28-add-user-auth/tasks.md deleted file mode 100644 index 2616e93..0000000 --- a/openspec/changes/archive/2025-12-28-add-user-auth/tasks.md +++ /dev/null @@ -1,138 +0,0 @@ -# Tasks: add-user-auth - -## 1. 專案初始化 - -- [x] 1.1 建立 Conda 環境與 requirements.txt -- [x] 1.2 初始化 FastAPI 專案結構 -- [x] 1.3 設定 MySQL 連線與 SQLAlchemy -- [x] 1.4 設定 Redis 連線 -- [x] 1.5 建立環境變數配置 (.env.example) - -## 2. 資料庫模型 - -- [x] 2.1 建立 `pjctrl_roles` 資料表 migration -- [x] 2.2 建立 `pjctrl_departments` 資料表 migration -- [x] 2.3 建立 `pjctrl_users` 資料表 migration -- [x] 2.4 建立 seed data (預設管理員與角色) -- [x] 2.5 驗證 migration 可正確執行與回滾 - -## 3. 認證模組 - -- [x] 3.1 實作外部 API 認證 client (`app/services/auth_client.py`) -- [x] 3.2 實作 JWT Token 驗證邏輯 -- [x] 3.3 實作登入 API endpoint (`POST /api/auth/login`) -- [x] 3.4 實作登出 API endpoint (`POST /api/auth/logout`) -- [x] 3.5 實作取得當前使用者 API (`GET /api/auth/me`) -- [x] 3.6 處理認證 API 連線失敗情境 - -## 4. Session 管理 - -- [x] 4.1 實作 Redis Session 儲存 -- [x] 4.2 實作 Session 過期機制 -- [x] 4.3 實作 Token 刷新機制 (如需要) - 暫不需要 - -## 5. 權限控制 - -- [x] 5.1 實作認證中間件 (`app/middleware/auth.py`) -- [x] 5.2 實作 RBAC 權限檢查裝飾器 -- [x] 5.3 實作部門隔離邏輯 -- [x] 5.4 實作系統管理員全域權限判斷 - -## 6. 使用者管理 API - -- [x] 6.1 實作使用者列表 API (`GET /api/users`) -- [x] 6.2 實作使用者詳情 API (`GET /api/users/{id}`) -- [x] 6.3 實作角色指派 API (`PATCH /api/users/{id}/role`) -- [x] 6.4 實作部門管理 API (CRUD) - -## 7. 測試 - -- [x] 7.1 撰寫認證模組單元測試 -- [x] 7.2 撰寫權限檢查單元測試 -- [x] 7.3 撰寫 API 整合測試 -- [x] 7.4 測試系統管理員權限 -- [x] 7.5 測試部門隔離情境 - -## 8. 前端 (基礎) - -- [x] 8.1 建立 React 專案結構 -- [x] 8.2 實作 AuthContext (認證狀態管理) -- [x] 8.3 實作登入頁面 -- [x] 8.4 實作 Protected Route 元件 -- [x] 8.5 實作登出功能 - -## Dependencies - -``` -1.x (專案初始化) → 2.x (資料庫) → 3.x (認證) → 4.x (Session) - ↓ - 5.x (權限) → 6.x (使用者管理) - ↓ - 7.x (測試) - -8.x (前端) 可與 3.x-6.x 並行開發 -``` - -## Notes - -- 所有資料表使用 `pjctrl_` 前綴 -- 認證必須透過外部 API,不可有本地繞過 -- 系統管理員帳號在 seed data 中建立 - -## Implementation Summary - -完成日期: 2024-01-XX - -### 已建立的檔案結構 - -``` -backend/ -├── app/ -│ ├── api/ -│ │ ├── auth/router.py # 認證 API -│ │ ├── users/router.py # 使用者管理 API -│ │ └── departments/router.py # 部門管理 API -│ ├── core/ -│ │ ├── config.py # 環境配置 -│ │ ├── database.py # 資料庫連線 -│ │ ├── redis.py # Redis 連線 -│ │ └── security.py # JWT 處理 -│ ├── middleware/ -│ │ └── auth.py # 認證與權限中間件 -│ ├── models/ -│ │ ├── user.py # User model -│ │ ├── role.py # Role model -│ │ └── department.py # Department model -│ ├── schemas/ # Pydantic schemas -│ ├── services/ -│ │ └── auth_client.py # 外部認證 API client -│ └── main.py # FastAPI 應用程式 -├── migrations/ -│ └── versions/ -│ └── 001_initial_auth_tables.py -├── tests/ -│ ├── conftest.py -│ ├── test_auth.py -│ └── test_users.py -├── .env -├── .env.example -├── requirements.txt -└── environment.yml - -frontend/ -├── src/ -│ ├── components/ -│ │ └── ProtectedRoute.tsx -│ ├── contexts/ -│ │ └── AuthContext.tsx -│ ├── pages/ -│ │ ├── Login.tsx -│ │ └── Dashboard.tsx -│ ├── services/ -│ │ └── api.ts -│ ├── App.tsx -│ └── main.tsx -├── package.json -├── tsconfig.json -└── vite.config.ts -``` diff --git a/openspec/changes/archive/2025-12-29-add-audit-trail/design.md b/openspec/changes/archive/2025-12-29-add-audit-trail/design.md deleted file mode 100644 index 814bfa2..0000000 --- a/openspec/changes/archive/2025-12-29-add-audit-trail/design.md +++ /dev/null @@ -1,147 +0,0 @@ -# Design: add-audit-trail - -## Architecture Decision - -### Approach: Application-layer Middleware - -選擇應用層中間件而非資料庫觸發器: - -| 方案 | 優點 | 缺點 | -|-----|------|-----| -| **Middleware (選擇)** | 可取得完整 context (user, IP)、跨資料庫相容 | 需要在每個 API 加入 | -| DB Trigger | 自動捕捉所有變更 | 無法取得 user context、MySQL 觸發器效能差 | - -### Implementation Strategy - -``` -Request → FastAPI Middleware → Extract metadata (user, IP, user_agent) - ↓ - API Handler → Execute operation - ↓ - AuditService.log() → Async write to pjctrl_audit_logs - ↓ - (if sensitive) → NotificationService → Alert admins -``` - -## Data Model - -### pjctrl_audit_logs - -```sql -CREATE TABLE pjctrl_audit_logs ( - id VARCHAR(36) PRIMARY KEY, - event_type VARCHAR(50) NOT NULL, - resource_type VARCHAR(50) NOT NULL, - resource_id VARCHAR(36), - user_id VARCHAR(36), - action ENUM('create', 'update', 'delete', 'restore', 'login', 'logout') NOT NULL, - changes JSON, - metadata JSON, - sensitivity_level ENUM('low', 'medium', 'high', 'critical') DEFAULT 'low', - checksum VARCHAR(64) NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - - INDEX idx_audit_user (user_id, created_at), - INDEX idx_audit_resource (resource_type, resource_id, created_at), - INDEX idx_audit_time (created_at), - - FOREIGN KEY (user_id) REFERENCES pjctrl_users(id) ON DELETE SET NULL -); -``` - -### pjctrl_audit_alerts - -```sql -CREATE TABLE pjctrl_audit_alerts ( - id VARCHAR(36) PRIMARY KEY, - audit_log_id VARCHAR(36) NOT NULL, - alert_type VARCHAR(50) NOT NULL, - recipients JSON NOT NULL, - message TEXT, - is_acknowledged BOOLEAN DEFAULT FALSE, - acknowledged_by VARCHAR(36), - acknowledged_at TIMESTAMP, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - - FOREIGN KEY (audit_log_id) REFERENCES pjctrl_audit_logs(id), - FOREIGN KEY (acknowledged_by) REFERENCES pjctrl_users(id) -); -``` - -## Checksum Calculation - -確保日誌不可竄改: - -```python -import hashlib -import json - -def calculate_checksum(log: AuditLog) -> str: - content = f"{log.event_type}|{log.resource_id}|{log.user_id}|{json.dumps(log.changes, sort_keys=True)}|{log.created_at.isoformat()}" - return hashlib.sha256(content.encode()).hexdigest() -``` - -## Sensitivity Levels & Event Types - -| Event Type | Sensitivity | Alert | -|-----------|-------------|-------| -| task.create, task.update, task.assign | low | No | -| task.delete, task.blocker | medium | No | -| project.create, project.update | medium | No | -| project.delete | high | Yes | -| user.permission_change | critical | Yes | -| user.login (異常) | high | Yes | - -## API Design - -### Query Audit Logs - -``` -GET /api/audit-logs -Query params: - - start_date: datetime - - end_date: datetime - - user_id: UUID (optional) - - resource_type: string (optional) - - resource_id: UUID (optional) - - sensitivity_level: string (optional) - - limit: int (default 50, max 100) - - offset: int -``` - -### Resource History - -``` -GET /api/audit-logs/resource/{resource_type}/{resource_id} -Returns: Change history for specific resource -``` - -### Export - -``` -GET /api/audit-logs/export -Query params: same as query + format=csv -Returns: CSV file download -``` - -### Integrity Check - -``` -POST /api/audit-logs/verify-integrity -Body: { start_date, end_date } -Returns: { total_checked, valid_count, invalid_records: [] } -``` - -## Integration Points - -1. **Task API**: Log create/update/delete/assign -2. **Project API**: Log create/update/delete -3. **User API**: Log permission changes -4. **Auth Middleware**: Log login/logout -5. **Blocker API**: Log blocker events - -## Alert Thresholds - -- **Bulk delete**: > 5 deletes within 5 minutes -- **Off-hours login**: Outside 06:00-22:00 local time -- **Permission escalation**: Any admin role assignment diff --git a/openspec/changes/archive/2025-12-29-add-audit-trail/proposal.md b/openspec/changes/archive/2025-12-29-add-audit-trail/proposal.md deleted file mode 100644 index 4cc52fc..0000000 --- a/openspec/changes/archive/2025-12-29-add-audit-trail/proposal.md +++ /dev/null @@ -1,40 +0,0 @@ -# Proposal: add-audit-trail - -## Why - -半導體產業對資料追溯有嚴格的合規需求。目前系統缺乏統一的稽核日誌機制,無法追蹤: -- 誰在何時修改了什麼資料 -- 關鍵操作(如權限變更、資料刪除)的完整記錄 -- 異常行為的即時警示 - -此變更建立系統級稽核追蹤功能,為未來 document-management 模組提供基礎。 - -## What Changes - -### Backend -- 新增 AuditLog、AuditAlert models -- 新增 AuditService (中間件自動記錄) -- 新增 `/api/audit-logs` 查詢 API -- 新增稽核報告匯出功能 (CSV) -- 整合 NotificationService 發送敏感操作警示 - -### Frontend -- 新增稽核日誌查詢頁面 (Admin only) -- 新增資源變更歷史元件 (Task/Project 詳情頁) - -### Database -- 新增 `pjctrl_audit_logs` 表 (append-only) -- 新增 `pjctrl_audit_alerts` 表 - -## Impact - -- **依賴**: 使用現有 NotificationService 發送警示 -- **被依賴**: document-management 將使用此稽核功能 -- **權限**: 稽核查詢限 system_admin -- **效能**: 使用非同步寫入避免影響主流程 - -## Out of Scope - -- 時間序列資料庫(先用 MySQL,未來可擴展) -- PDF 匯出(先實作 CSV) -- 資料庫觸發器(使用應用層中間件) diff --git a/openspec/changes/archive/2025-12-29-add-audit-trail/specs/audit-trail/spec.md b/openspec/changes/archive/2025-12-29-add-audit-trail/specs/audit-trail/spec.md deleted file mode 100644 index 2c38c09..0000000 --- a/openspec/changes/archive/2025-12-29-add-audit-trail/specs/audit-trail/spec.md +++ /dev/null @@ -1,94 +0,0 @@ -## ADDED Requirements - -### Requirement: Change Logging -系統 SHALL 記錄所有關鍵變更操作,包含誰在何時改了什麼。 - -#### Scenario: 任務欄位變更記錄 -- **GIVEN** 使用者修改任務的任何欄位(如截止日期、狀態、指派者) -- **WHEN** 變更儲存成功 -- **THEN** 系統記錄變更前後的值 -- **AND** 記錄操作者、時間、IP 位址 - -#### Scenario: 專案設定變更記錄 -- **GIVEN** 管理者修改專案設定 -- **WHEN** 設定變更儲存 -- **THEN** 系統記錄所有變更的設定項目 -- **AND** 記錄操作者與時間 - -#### Scenario: 權限變更記錄 -- **GIVEN** 管理者修改使用者權限或角色 -- **WHEN** 權限變更生效 -- **THEN** 系統記錄權限變更詳情 -- **AND** 標記為高敏感度操作 - -### Requirement: Delete Operations Tracking -系統 SHALL 追蹤所有刪除操作,支援軟刪除與追溯。 - -#### Scenario: 任務刪除記錄 -- **GIVEN** 使用者刪除任務 -- **WHEN** 刪除操作執行 -- **THEN** 系統執行軟刪除(標記 is_deleted = true) -- **AND** 記錄刪除操作與原因 - -#### Scenario: 附件刪除記錄 -- **GIVEN** 使用者刪除附件 -- **WHEN** 刪除操作執行 -- **THEN** 系統保留檔案於存檔區 -- **AND** 記錄刪除操作詳情 - -### Requirement: Audit Log Immutability -系統 SHALL 確保稽核日誌不可竄改。 - -#### Scenario: 日誌寫入 -- **GIVEN** 需要記錄稽核事件 -- **WHEN** 日誌寫入 -- **THEN** 日誌記錄不可被修改或刪除 -- **AND** 包含 SHA-256 校驗碼確保完整性 - -#### Scenario: 日誌完整性驗證 -- **GIVEN** 稽核人員需要驗證日誌完整性 -- **WHEN** 執行完整性檢查 -- **THEN** 系統驗證所有日誌記錄的校驗碼 -- **AND** 報告任何異常記錄 - -### Requirement: Audit Query Interface -系統 SHALL 提供稽核查詢介面供授權人員使用。 - -#### Scenario: 依時間範圍查詢 -- **GIVEN** 稽核人員需要查詢特定時間範圍的操作 -- **WHEN** 設定時間範圍並執行查詢 -- **THEN** 顯示該時間範圍內的所有稽核記錄 - -#### Scenario: 依操作者查詢 -- **GIVEN** 稽核人員需要查詢特定使用者的操作歷史 -- **WHEN** 選擇使用者並執行查詢 -- **THEN** 顯示該使用者的所有操作記錄 - -#### Scenario: 依資源查詢 -- **GIVEN** 稽核人員需要查詢特定任務或專案的變更歷史 -- **WHEN** 選擇資源並執行查詢 -- **THEN** 顯示該資源的完整變更歷程 - -#### Scenario: 稽核報告匯出 -- **GIVEN** 稽核人員需要匯出稽核報告 -- **WHEN** 選擇匯出 CSV 格式 -- **THEN** 系統生成報告檔案供下載 - -### Requirement: Sensitive Operation Alerts -系統 SHALL 對高敏感度操作發送即時警示。 - -#### Scenario: 權限提升警示 -- **GIVEN** 使用者被授予管理員權限 -- **WHEN** 權限變更生效 -- **THEN** 系統發送警示給安全管理員 -- **AND** 建立 AuditAlert 記錄 - -#### Scenario: 大量刪除警示 -- **GIVEN** 使用者在 5 分鐘內刪除超過 5 筆資料 -- **WHEN** 偵測到異常刪除模式 -- **THEN** 系統發送警示給安全管理員 - -#### Scenario: 警示確認 -- **GIVEN** 管理員收到敏感操作警示 -- **WHEN** 管理員確認警示 -- **THEN** 系統記錄確認者與確認時間 diff --git a/openspec/changes/archive/2025-12-29-add-audit-trail/tasks.md b/openspec/changes/archive/2025-12-29-add-audit-trail/tasks.md deleted file mode 100644 index aa48c5b..0000000 --- a/openspec/changes/archive/2025-12-29-add-audit-trail/tasks.md +++ /dev/null @@ -1,84 +0,0 @@ -## 1. Database Schema - -- [x] 1.1 建立 AuditLog model (`pjctrl_audit_logs`) -- [x] 1.2 建立 AuditAlert model (`pjctrl_audit_alerts`) -- [x] 1.3 建立 Alembic migration -- [x] 1.4 建立 event types 和 sensitivity levels 常數 - -## 2. Core Audit Service - -- [x] 2.1 建立 AuditService 核心類別 -- [x] 2.2 實作 checksum 計算邏輯 -- [x] 2.3 實作 log_event() 方法 (非同步) -- [x] 2.4 實作 detect_changes() 方法 (比較 old/new values) -- [x] 2.5 實作敏感度判定邏輯 - -## 3. Audit Middleware - -- [x] 3.1 建立 AuditMiddleware 擷取 request metadata (IP, user_agent) -- [x] 3.2 將 metadata 注入 request state - -## 4. API Integration - Task - -- [x] 4.1 整合 Task create 稽核 -- [x] 4.2 整合 Task update 稽核 (含 changes diff) -- [x] 4.3 整合 Task delete 稽核 -- [x] 4.4 整合 Task assign 稽核 - -## 5. API Integration - Project - -- [x] 5.1 整合 Project create 稽核 -- [x] 5.2 整合 Project update 稽核 -- [x] 5.3 整合 Project delete 稽核 - -## 6. API Integration - User & Auth - -- [x] 6.1 整合 User permission change 稽核 -- [x] 6.2 整合 Login/Logout 稽核 -- [x] 6.3 整合 Blocker 事件稽核 - -## 7. Backend API - Query - -- [x] 7.1 建立 AuditLog schemas (response) -- [x] 7.2 實作 GET `/api/audit-logs` - 查詢稽核日誌 -- [x] 7.3 實作 GET `/api/audit-logs/resource/{type}/{id}` - 資源歷史 -- [x] 7.4 實作 query filters (時間、使用者、資源、敏感度) - -## 8. Backend API - Export & Verify - -- [x] 8.1 實作 GET `/api/audit-logs/export` - CSV 匯出 -- [x] 8.2 實作 POST `/api/audit-logs/verify-integrity` - 完整性驗證 -- [x] 8.3 實作分頁處理大量資料 - -## 9. Alert System - -- [x] 9.1 建立 AuditAlert schemas -- [x] 9.2 實作 create_alert() 方法 -- [x] 9.3 實作敏感操作警示觸發 -- [x] 9.4 實作大量刪除偵測 -- [x] 9.5 整合 NotificationService 發送警示 -- [x] 9.6 實作 PUT `/api/audit-alerts/{id}/acknowledge` - 確認警示 - -## 10. Frontend - Admin Audit Page - -- [x] 10.1 建立 audit.ts service -- [x] 10.2 建立 AuditLogList 元件 -- [x] 10.3 建立 AuditLogFilters 元件 (日期、使用者、資源) -- [x] 10.4 建立 AuditLogDetail modal (顯示 changes diff) -- [x] 10.5 建立 CSV 匯出按鈕 -- [x] 10.6 新增 Admin menu 連結 - -## 11. Frontend - Resource History - -- [x] 11.1 建立 ResourceHistory 元件 -- [x] 11.2 整合至 Task 詳情頁 -- [x] 11.3 整合至 Project 詳情頁 - -## 12. Testing - -- [x] 12.1 AuditService 單元測試 -- [x] 12.2 Checksum 計算測試 -- [x] 12.3 Audit API 端點測試 -- [x] 12.4 Alert 觸發測試 -- [x] 12.5 CSV 匯出測試 -- [x] 12.6 完整性驗證測試 diff --git a/openspec/changes/archive/2025-12-29-add-automation/design.md b/openspec/changes/archive/2025-12-29-add-automation/design.md deleted file mode 100644 index ead9ec7..0000000 --- a/openspec/changes/archive/2025-12-29-add-automation/design.md +++ /dev/null @@ -1,185 +0,0 @@ -## Context - -自動化系統需要處理兩種類型的自動化: -1. 事件驅動 - 任務欄位變更時觸發 -2. 時間驅動 - 排程執行(如週報) - -## Goals / Non-Goals - -**Goals:** -- 提供欄位變更觸發器(status, assignee, priority) -- 實作每週五 16:00 自動週報 -- 整合現有通知系統 -- 記錄所有觸發器執行日誌 - -**Non-Goals:** -- 複雜的工作流引擎 -- 跨任務觸發器 -- Email 發送(僅系統內通知) - -## Decisions - -### 1. 排程方案 - -**Decision:** 使用 APScheduler 而非 Celery - -**Rationale:** -- 無需額外 worker 進程,減少部署複雜度 -- 嵌入 FastAPI 應用內運行 -- 足夠處理週報等簡單定時任務 -- 未來如需擴展可遷移至 Celery - -**Configuration:** -```python -from apscheduler.schedulers.asyncio import AsyncIOScheduler -from apscheduler.triggers.cron import CronTrigger - -scheduler = AsyncIOScheduler() -scheduler.add_job( - generate_weekly_reports, - CronTrigger(day_of_week='fri', hour=16, minute=0), - id='weekly_report' -) -``` - -### 2. 觸發器評估策略 - -**Decision:** 同步評估,任務更新時直接執行 - -**Rationale:** -- 簡化實作,無需消息隊列 -- 觸發器執行速度快(僅發送通知) -- 失敗可即時回報 - -**Flow:** -``` -Task Update → Detect Changes → Find Matching Triggers → Execute Actions → Log Results -``` - -### 3. 條件定義格式 - -**Decision:** 使用 JSON 結構定義條件 - -```json -{ - "field": "status_id", - "operator": "equals", - "value": "uuid-of-testing-status" -} -``` - -**Supported Operators:** -- `equals` - 等於 -- `not_equals` - 不等於 -- `changed_to` - 變更為特定值 -- `changed_from` - 從特定值變更 - -### 4. 動作定義格式 - -**Decision:** 使用 JSON 陣列定義動作 - -```json -[ - { - "type": "notify", - "target": "assignee", - "template": "任務 {task.title} 狀態已變更為 {new_value}" - } -] -``` - -**Supported Actions (Phase 1):** -- `notify` - 發送系統通知 - -**Target Types:** -- `assignee` - 任務指派者 -- `creator` - 任務建立者 -- `project_owner` - 專案擁有者 -- `user:` - 指定使用者 - -## Data Model - -```sql --- 觸發器表 -pjctrl_triggers -├── id: UUID (PK) -├── project_id: UUID (FK -> projects) -├── name: VARCHAR(200) -├── description: TEXT -├── trigger_type: ENUM('field_change', 'schedule') -├── conditions: JSON -├── actions: JSON -├── is_active: BOOLEAN DEFAULT true -├── created_by: UUID (FK -> users) -├── created_at: TIMESTAMP -└── updated_at: TIMESTAMP - --- 觸發器執行日誌 -pjctrl_trigger_logs -├── id: UUID (PK) -├── trigger_id: UUID (FK -> triggers) -├── task_id: UUID (FK -> tasks, nullable) -├── executed_at: TIMESTAMP -├── status: ENUM('success', 'failed') -├── details: JSON -└── error_message: TEXT - --- 排程報告設定 -pjctrl_scheduled_reports -├── id: UUID (PK) -├── report_type: ENUM('weekly') -├── recipient_id: UUID (FK -> users) -├── is_active: BOOLEAN DEFAULT true -├── last_sent_at: TIMESTAMP -└── created_at: TIMESTAMP - --- 報告歷史 -pjctrl_report_history -├── id: UUID (PK) -├── report_id: UUID (FK -> scheduled_reports) -├── generated_at: TIMESTAMP -├── content: JSON -├── status: ENUM('sent', 'failed') -└── error_message: TEXT -``` - -## API Design - -``` -# Triggers -POST /api/projects/{project_id}/triggers # 建立觸發器 -GET /api/projects/{project_id}/triggers # 列出專案觸發器 -GET /api/triggers/{id} # 觸發器詳情 -PUT /api/triggers/{id} # 更新觸發器 -DELETE /api/triggers/{id} # 刪除觸發器 -GET /api/triggers/{id}/logs # 觸發器執行日誌 - -# Reports -GET /api/reports/weekly/preview # 預覽週報 -POST /api/reports/weekly/generate # 手動觸發週報 -GET /api/reports/history # 報告歷史 -``` - -## Risks / Trade-offs - -| Risk | Mitigation | -|------|------------| -| 大量觸發器影響效能 | 限制每專案觸發器數量,建立索引 | -| APScheduler 單點故障 | 記錄 last_sent_at 防止重複,考慮多實例鎖 | -| 觸發器條件複雜度 | Phase 1 僅支援簡單條件,後續擴展 | - -## Integration Points - -1. **Task Update Hook** - - 在 `tasks/router.py` 的 update_task 後調用 TriggerService.evaluate() - -2. **Notification Integration** - - 使用現有 NotificationService.create_notification() - -3. **Audit Integration** - - 觸發器執行記錄至 TriggerLog(非 AuditLog) - -## Open Questions - -- [ ] 是否需要支援 Email 發送?(目前僅系統內通知) -- [ ] 週報收件者如何設定?(主管自動訂閱 or 手動設定) diff --git a/openspec/changes/archive/2025-12-29-add-automation/proposal.md b/openspec/changes/archive/2025-12-29-add-automation/proposal.md deleted file mode 100644 index d8ce85b..0000000 --- a/openspec/changes/archive/2025-12-29-add-automation/proposal.md +++ /dev/null @@ -1,52 +0,0 @@ -# Change: Add Automation System - -## Why -專案管理需要自動化功能來減少重複性工作: -- 當任務狀態變更時自動通知相關人員 -- 每週自動生成進度報告發送給主管 -- 減少人工追蹤與提醒的負擔 - -## What Changes -- **新增 Trigger 模型** - 定義觸發條件與動作 -- **新增 TriggerService** - 觸發器評估與執行 -- **新增 ReportService** - 週報生成邏輯 -- **新增背景排程** - APScheduler 處理定時任務 -- **整合任務 API** - 任務變更時評估觸發器 - -## Impact -- Affected specs: `automation` -- Affected code: - - `backend/app/models/` - 新增 trigger, scheduled_report 模型 - - `backend/app/api/` - 新增 triggers, reports router - - `backend/app/services/` - 新增 trigger_service, report_service - - `backend/app/api/tasks/router.py` - 整合觸發器評估 - - `frontend/src/` - 新增觸發器管理頁面 - -## Implementation Phases - -### Phase 1: Event-Based Triggers -- Trigger 模型與 CRUD API -- 欄位變更觸發器(status, assignee, priority) -- 通知動作執行 -- 整合任務更新流程 - -### Phase 2: Weekly Reports -- ScheduledReport 模型 -- 週報生成邏輯(彙整任務統計) -- APScheduler 背景排程 -- 系統內通知發送 - -### Phase 3: Advanced Features (Optional) -- 時間條件觸發器 -- 複合條件支援 -- 更新欄位動作 -- 自動指派動作 - -## Dependencies -- notification (已完成) - 用於發送觸發通知 -- audit-trail (已完成) - 記錄觸發器執行日誌 - -## Technical Decisions -- **使用 APScheduler** 而非 Celery - 輕量級,無需額外 worker 進程 -- **同步觸發器評估** - 任務更新時同步執行,避免複雜的異步處理 -- **JSON 欄位儲存條件** - 靈活的條件定義格式 diff --git a/openspec/changes/archive/2025-12-29-add-automation/specs/automation/spec.md b/openspec/changes/archive/2025-12-29-add-automation/specs/automation/spec.md deleted file mode 100644 index 3016734..0000000 --- a/openspec/changes/archive/2025-12-29-add-automation/specs/automation/spec.md +++ /dev/null @@ -1,105 +0,0 @@ -## MODIFIED Requirements - -### Requirement: Trigger-Based Automation -系統 SHALL 支援觸發器 (Triggers),當特定條件滿足時自動執行動作。 - -#### Scenario: 狀態變更觸發通知 -- **GIVEN** 專案設定了「當任務狀態變更為待測試時,通知指派者」的觸發器 -- **WHEN** 任務狀態變更為「待測試」 -- **THEN** 系統自動發送通知給任務指派者 -- **AND** 觸發器執行記錄至 TriggerLog - -#### Scenario: 建立觸發器 -- **GIVEN** 專案管理者需要建立自動化規則 -- **WHEN** 管理者透過 API 設定觸發條件與動作 -- **THEN** 系統儲存觸發器規則 -- **AND** 規則立即生效 - -### Requirement: Trigger Conditions -系統 SHALL 支援欄位變更觸發條件。 - -#### Scenario: 欄位變更條件 -- **GIVEN** 觸發器設定為「當 status_id 欄位變更為特定值」 -- **WHEN** 任務的 status_id 欄位變更為該值 -- **THEN** 觸發器被觸發 -- **AND** 支援運算子: equals, not_equals, changed_to, changed_from - -### Requirement: Trigger Actions -系統 SHALL 支援發送通知動作。 - -#### Scenario: 發送通知動作 -- **GIVEN** 觸發器動作設定為 notify -- **WHEN** 觸發器被觸發 -- **THEN** 系統使用 NotificationService 發送通知 -- **AND** 通知目標支援: assignee, creator, project_owner, user: -- **AND** 通知內容可使用變數模板 - -### Requirement: Automated Weekly Report -系統 SHALL 每週五下午 4:00 自動彙整本週任務狀態發送給主管。 - -#### Scenario: 週報自動生成 -- **GIVEN** APScheduler 排程設定為每週五 16:00 -- **WHEN** 到達排程時間 -- **THEN** ReportService 彙整使用者所屬專案的任務狀態 -- **AND** 生成週報並透過 NotificationService 發送 - -#### Scenario: 週報內容 -- **GIVEN** 週報生成中 -- **WHEN** 系統彙整資料 -- **THEN** 週報 JSON 包含: - - completed_count: 本週已完成任務數 - - in_progress_count: 進行中任務數 - - overdue_count: 逾期任務數 - - tasks: 詳細任務清單 - -## MODIFIED Data Model - -``` -pjctrl_triggers -├── id: UUID (PK) -├── project_id: UUID (FK -> projects) -├── name: VARCHAR(200) -├── description: TEXT -├── trigger_type: ENUM('field_change', 'schedule') -├── conditions: JSON -│ └── { "field": "status_id", "operator": "changed_to", "value": "uuid" } -├── actions: JSON -│ └── [{ "type": "notify", "target": "assignee", "template": "..." }] -├── is_active: BOOLEAN DEFAULT true -├── created_by: UUID (FK -> users) -├── created_at: TIMESTAMP -└── updated_at: TIMESTAMP - -pjctrl_trigger_logs -├── id: UUID (PK) -├── trigger_id: UUID (FK -> triggers) -├── task_id: UUID (FK -> tasks, nullable) -├── executed_at: TIMESTAMP -├── status: ENUM('success', 'failed') -├── details: JSON -└── error_message: TEXT - -pjctrl_scheduled_reports -├── id: UUID (PK) -├── report_type: ENUM('weekly') -├── recipient_id: UUID (FK -> users) -├── is_active: BOOLEAN DEFAULT true -├── last_sent_at: TIMESTAMP -└── created_at: TIMESTAMP - -pjctrl_report_history -├── id: UUID (PK) -├── report_id: UUID (FK -> scheduled_reports) -├── generated_at: TIMESTAMP -├── content: JSON -├── status: ENUM('sent', 'failed') -└── error_message: TEXT -``` - -## MODIFIED Technical Notes - -- 使用 APScheduler (AsyncIOScheduler) 處理排程任務,嵌入 FastAPI 應用內運行 -- 觸發器評估採用同步處理,任務更新時直接執行,避免複雜的異步處理 -- 所有觸發器執行都記錄至 TriggerLog 供追蹤 -- Phase 1 僅支援 notify 動作 -- 條件運算子: equals, not_equals, changed_to, changed_from diff --git a/openspec/changes/archive/2025-12-29-add-automation/tasks.md b/openspec/changes/archive/2025-12-29-add-automation/tasks.md deleted file mode 100644 index aa45e87..0000000 --- a/openspec/changes/archive/2025-12-29-add-automation/tasks.md +++ /dev/null @@ -1,90 +0,0 @@ -## Phase 1: Event-Based Triggers - -### 1.1 Database Schema -- [x] 1.1.1 建立 Trigger model (`pjctrl_triggers`) -- [x] 1.1.2 建立 TriggerLog model (`pjctrl_trigger_logs`) -- [x] 1.1.3 建立 Alembic migration -- [x] 1.1.4 定義 TriggerType enum (field_change, schedule) - -### 1.2 Trigger Service -- [x] 1.2.1 建立 TriggerService 類別 -- [x] 1.2.2 實作 evaluate_triggers(task, old_values, new_values) 方法 -- [x] 1.2.3 實作 check_condition(condition, old_value, new_value) 方法 -- [x] 1.2.4 實作 execute_action(action, task, user) 方法 -- [x] 1.2.5 實作 log_execution(trigger, task, status, error) 方法 - -### 1.3 Trigger API -- [x] 1.3.1 建立 Trigger schemas (request/response) -- [x] 1.3.2 實作 POST `/api/projects/{project_id}/triggers` - 建立 -- [x] 1.3.3 實作 GET `/api/projects/{project_id}/triggers` - 列表 -- [x] 1.3.4 實作 GET `/api/triggers/{id}` - 詳情 -- [x] 1.3.5 實作 PUT `/api/triggers/{id}` - 更新 -- [x] 1.3.6 實作 DELETE `/api/triggers/{id}` - 刪除 -- [x] 1.3.7 實作 GET `/api/triggers/{id}/logs` - 執行日誌 - -### 1.4 Task Integration -- [x] 1.4.1 修改 update_task endpoint 整合觸發器評估 -- [x] 1.4.2 修改 update_task_status endpoint 整合觸發器評估 -- [x] 1.4.3 修改 assign_task endpoint 整合觸發器評估 - -### 1.5 Frontend - Triggers -- [x] 1.5.1 建立 triggers.ts service -- [x] 1.5.2 建立 TriggerList 元件 -- [x] 1.5.3 建立 TriggerForm 元件(條件/動作設定) -- [x] 1.5.4 整合至 Project 設定頁面 - -### 1.6 Testing - Phase 1 -- [x] 1.6.1 TriggerService 單元測試 -- [x] 1.6.2 Trigger API 端點測試 -- [x] 1.6.3 觸發器執行整合測試 - -## Phase 2: Weekly Reports - -### 2.1 Database Schema -- [x] 2.1.1 建立 ScheduledReport model (`pjctrl_scheduled_reports`) -- [x] 2.1.2 建立 ReportHistory model (`pjctrl_report_history`) -- [x] 2.1.3 建立 Alembic migration - -### 2.2 Report Service -- [x] 2.2.1 建立 ReportService 類別 -- [x] 2.2.2 實作 generate_weekly_report(user_id) 方法 -- [x] 2.2.3 實作 get_weekly_stats(user_id, week_start) 方法 -- [x] 2.2.4 實作 send_report_notification(user_id, report) 方法 -- [x] 2.2.5 實作 save_report_history(report) 方法 - -### 2.3 Scheduler Setup -- [x] 2.3.1 安裝 APScheduler -- [x] 2.3.2 建立 scheduler.py 設定檔 -- [x] 2.3.3 設定週五 16:00 排程任務 -- [x] 2.3.4 整合至 main.py 啟動流程 - -### 2.4 Report API -- [x] 2.4.1 建立 Report schemas -- [x] 2.4.2 實作 GET `/api/reports/weekly/preview` - 預覽 -- [x] 2.4.3 實作 POST `/api/reports/weekly/generate` - 手動觸發 -- [x] 2.4.4 實作 GET `/api/reports/history` - 歷史紀錄 - -### 2.5 Frontend - Reports -- [x] 2.5.1 建立 reports.ts service -- [x] 2.5.2 建立 WeeklyReportPreview 元件 -- [x] 2.5.3 建立 ReportHistory 元件 -- [x] 2.5.4 新增管理員報告頁面 - -### 2.6 Testing - Phase 2 -- [x] 2.6.1 ReportService 單元測試 -- [x] 2.6.2 週報生成測試 -- [x] 2.6.3 排程執行測試 - -## Phase 3: Advanced Features (Optional) - -### 3.1 Schedule Triggers -- [ ] 3.1.1 支援 cron 表達式觸發器 -- [ ] 3.1.2 截止日期提醒觸發器 - -### 3.2 Additional Actions -- [ ] 3.2.1 更新欄位動作 -- [ ] 3.2.2 自動指派動作 - -### 3.3 Complex Conditions -- [ ] 3.3.1 AND/OR 複合條件 -- [ ] 3.3.2 多欄位條件 diff --git a/openspec/changes/archive/2025-12-29-add-collaboration/design.md b/openspec/changes/archive/2025-12-29-add-collaboration/design.md deleted file mode 100644 index 9fe88a6..0000000 --- a/openspec/changes/archive/2025-12-29-add-collaboration/design.md +++ /dev/null @@ -1,125 +0,0 @@ -## Context - -協作功能需要整合多個子系統:留言、@提及、阻礙管理、即時通知。這些功能與現有的 Task 模組緊密耦合,且需要引入 WebSocket 即時通訊機制。 - -## Goals / Non-Goals - -**Goals:** -- 提供任務內留言討論功能 -- 實作 @提及與即時通知 -- 建立阻礙追蹤與主管通知機制 -- 使用 Redis Pub/Sub 處理即時推播 - -**Non-Goals:** -- 不實作 Email 通知(未來功能) -- 不實作離線訊息佇列 -- 不實作留言的 Markdown 編輯器(僅純文字) - -## Decisions - -### 1. 留言儲存結構 -- **決策**: 使用巢狀結構(parent_comment_id)支援回覆 -- **理由**: 簡單且符合大多數專案管理工具的 UX 模式 - -### 2. @提及解析 -- **決策**: 後端解析留言內容,提取 `@username` 模式並建立 Mention 記錄 -- **理由**: 前端負責顯示建議,後端負責實際通知邏輯 - -### 3. 即時通知架構 -- **決策**: Redis Pub/Sub + WebSocket (FastAPI WebSocket) -- **替代方案**: - - Server-Sent Events (SSE) - 單向,不支援雙向互動 - - Polling - 延遲高,資源浪費 -- **理由**: WebSocket 提供低延遲雙向通訊,Redis 已在架構中使用 - -### 4. 阻礙管理 -- **決策**: 新增獨立 `pjctrl_blockers` 表記錄阻礙詳情 -- **理由**: Task 的 `blocker_flag` 只是布林值,需要追蹤原因、處理人、解除說明 - -## Risks / Trade-offs - -| 風險 | 緩解措施 | -|------|----------| -| WebSocket 連線管理複雜度 | 使用連線池,實作心跳機制 | -| 通知量過大導致效能問題 | 實作批次處理、設定通知合併策略 | -| @提及被濫用 | 限制單則留言最多提及 10 人 | - -## Data Model - -``` -pjctrl_comments -├── id: UUID (PK) -├── task_id: UUID (FK -> tasks) -├── parent_comment_id: UUID (FK -> comments, nullable) -├── author_id: UUID (FK -> users) -├── content: TEXT -├── is_edited: BOOLEAN DEFAULT false -├── created_at: TIMESTAMP -└── updated_at: TIMESTAMP - -pjctrl_mentions -├── id: UUID (PK) -├── comment_id: UUID (FK -> comments) -├── mentioned_user_id: UUID (FK -> users) -└── created_at: TIMESTAMP - -pjctrl_notifications -├── id: UUID (PK) -├── user_id: UUID (FK -> users) -├── type: ENUM('mention', 'assignment', 'blocker', 'status_change', 'comment') -├── reference_type: VARCHAR(50) -├── reference_id: UUID -├── title: VARCHAR(200) -├── message: TEXT -├── is_read: BOOLEAN DEFAULT false -├── created_at: TIMESTAMP -└── read_at: TIMESTAMP - -pjctrl_blockers -├── id: UUID (PK) -├── task_id: UUID (FK -> tasks) -├── reported_by: UUID (FK -> users) -├── reason: TEXT -├── resolved_by: UUID (FK -> users, nullable) -├── resolution_note: TEXT -├── created_at: TIMESTAMP -└── resolved_at: TIMESTAMP -``` - -## API Design - -``` -# Comments -POST /api/tasks/{task_id}/comments # 新增留言 -GET /api/tasks/{task_id}/comments # 取得任務留言列表 -PUT /api/comments/{comment_id} # 編輯留言 -DELETE /api/comments/{comment_id} # 刪除留言 - -# Notifications -GET /api/notifications # 取得通知列表 -PUT /api/notifications/{id}/read # 標記已讀 -PUT /api/notifications/read-all # 全部已讀 -GET /api/notifications/unread-count # 取得未讀數量 - -# Blockers -POST /api/tasks/{task_id}/blockers # 標記阻礙 -PUT /api/blockers/{blocker_id}/resolve # 解除阻礙 -GET /api/tasks/{task_id}/blockers # 取得阻礙歷史 - -# WebSocket -WS /ws/notifications # 即時通知連線 - -# User Search (for @mention autocomplete) -GET /api/users/search?q={query} # 搜尋使用者 -``` - -## Migration Plan - -1. 新增資料表(無 breaking changes) -2. Task model 新增 comments relationship -3. User model 新增 notifications relationship -4. 前端漸進式整合 - -## Open Questions - -無(已與現有架構對齊) diff --git a/openspec/changes/archive/2025-12-29-add-collaboration/proposal.md b/openspec/changes/archive/2025-12-29-add-collaboration/proposal.md deleted file mode 100644 index 0633e0b..0000000 --- a/openspec/changes/archive/2025-12-29-add-collaboration/proposal.md +++ /dev/null @@ -1,18 +0,0 @@ -# Change: Add Collaboration Features - -## Why -專案管理系統需要協作功能,讓團隊成員能在任務內進行討論、標記相關人員,並處理任務阻礙。目前系統缺乏這些核心協作機制,導致使用者仍需依賴 Email 或其他工具進行溝通。 - -## What Changes -- 新增任務留言系統(支援巢狀回覆) -- 實作 @提及功能(自動完成 + 即時通知) -- 建立阻礙管理機制(標記原因、主管通知、解除追蹤) -- 實作即時通知系統(Redis Pub/Sub + WebSocket) - -## Impact -- Affected specs: `collaboration` -- Affected code: - - 新增 models: Comment, Mention, Notification, Blocker - - 新增 API routes: `/comments`, `/notifications` - - 新增 services: NotificationService, WebSocketManager - - 前端: 任務詳情頁新增留言區、通知中心 diff --git a/openspec/changes/archive/2025-12-29-add-collaboration/specs/collaboration/spec.md b/openspec/changes/archive/2025-12-29-add-collaboration/specs/collaboration/spec.md deleted file mode 100644 index e39959c..0000000 --- a/openspec/changes/archive/2025-12-29-add-collaboration/specs/collaboration/spec.md +++ /dev/null @@ -1,97 +0,0 @@ -## ADDED Requirements - -### Requirement: Task Comments -系統 SHALL 支援任務內部的討論留言,減少 Email 往返。 - -#### Scenario: 新增留言 -- **GIVEN** 使用者擁有任務的存取權限 -- **WHEN** 使用者在任務中新增留言 -- **THEN** 系統儲存留言並顯示在討論區 -- **AND** 記錄留言者與時間戳記 - -#### Scenario: 回覆留言 -- **GIVEN** 任務已有留言 -- **WHEN** 使用者回覆特定留言 -- **THEN** 系統建立巢狀回覆結構 -- **AND** 通知原留言者 - -#### Scenario: 編輯留言 -- **GIVEN** 使用者是留言的作者 -- **WHEN** 使用者編輯自己的留言 -- **THEN** 系統更新留言內容 -- **AND** 標示「已編輯」及編輯時間 - -#### Scenario: 刪除留言 -- **GIVEN** 使用者是留言的作者 -- **WHEN** 使用者刪除自己的留言 -- **THEN** 系統移除留言 -- **AND** 若有回覆則顯示「此留言已刪除」 - -### Requirement: User Mentions -系統 SHALL 支援 @相關人員 功能,提及時發送即時通知。 - -#### Scenario: @提及通知 -- **GIVEN** 使用者在留言中使用 @username 提及某人 -- **WHEN** 留言送出 -- **THEN** 被提及者收到即時通知 -- **AND** 通知包含任務連結與留言摘要 - -#### Scenario: @提及自動完成 -- **GIVEN** 使用者輸入 @ 符號 -- **WHEN** 使用者繼續輸入 -- **THEN** 系統顯示符合的使用者名單供選擇 -- **AND** 可用鍵盤或滑鼠選擇 - -#### Scenario: @提及數量限制 -- **GIVEN** 使用者在單則留言中提及超過 10 人 -- **WHEN** 留言送出 -- **THEN** 系統拒絕並顯示錯誤訊息 - -### Requirement: Blocker Management -系統 SHALL 提供阻礙 (Blocker) 機制,強制要求主管介入排解。 - -#### Scenario: 標記阻礙 -- **GIVEN** 工程師的任務遇到阻礙無法進行 -- **WHEN** 工程師將任務標記為 "Blocked" -- **THEN** 系統設定 Task.blocker_flag = true -- **AND** 建立 Blocker 記錄 -- **AND** 發送即時通知給該任務所屬專案的 Owner - -#### Scenario: 阻礙原因說明 -- **GIVEN** 任務被標記為 Blocked -- **WHEN** 使用者標記阻礙 -- **THEN** 系統要求填寫阻礙原因 -- **AND** 原因顯示在任務詳情與通知中 - -#### Scenario: 解除阻礙 -- **GIVEN** 主管或被指派者處理完阻礙 -- **WHEN** 使用者解除 Blocked 狀態 -- **THEN** 系統設定 Task.blocker_flag = false -- **AND** 記錄解除時間與處理說明 - -### Requirement: Real-time Notifications -系統 SHALL 透過 Redis Pub/Sub 與 WebSocket 推播即時通知。 - -#### Scenario: 即時通知推播 -- **GIVEN** 發生需要通知的事件(被指派任務、被 @提及、阻礙標記) -- **WHEN** 事件發生 -- **THEN** 系統透過 WebSocket 即時推播通知給相關使用者 -- **AND** 未讀通知顯示數量標示 - -#### Scenario: 通知已讀標記 -- **GIVEN** 使用者有未讀通知 -- **WHEN** 使用者查看通知 -- **THEN** 系統標記為已讀 -- **AND** 更新未讀數量 - -#### Scenario: 通知清單查詢 -- **GIVEN** 使用者需要查看歷史通知 -- **WHEN** 使用者開啟通知中心 -- **THEN** 系統顯示通知列表(依時間降序) -- **AND** 支援分頁與已讀/未讀篩選 - -#### Scenario: WebSocket 重連 -- **GIVEN** 使用者的 WebSocket 連線中斷 -- **WHEN** 連線恢復 -- **THEN** 系統自動重新建立連線 -- **AND** 補送中斷期間的未讀通知 diff --git a/openspec/changes/archive/2025-12-29-add-collaboration/tasks.md b/openspec/changes/archive/2025-12-29-add-collaboration/tasks.md deleted file mode 100644 index af12dfa..0000000 --- a/openspec/changes/archive/2025-12-29-add-collaboration/tasks.md +++ /dev/null @@ -1,76 +0,0 @@ -## 1. Database Schema - -- [x] 1.1 建立 Comment model (`pjctrl_comments`) -- [x] 1.2 建立 Mention model (`pjctrl_mentions`) -- [x] 1.3 建立 Notification model (`pjctrl_notifications`) -- [x] 1.4 建立 Blocker model (`pjctrl_blockers`) -- [x] 1.5 建立 Alembic migration -- [x] 1.6 更新 Task model 加入 comments relationship -- [x] 1.7 更新 User model 加入 notifications relationship - -## 2. Backend API - Comments - -- [x] 2.1 建立 Comment schemas (request/response) -- [x] 2.2 實作 POST `/api/tasks/{task_id}/comments` - 新增留言 -- [x] 2.3 實作 GET `/api/tasks/{task_id}/comments` - 取得留言列表 -- [x] 2.4 實作 PUT `/api/comments/{comment_id}` - 編輯留言 -- [x] 2.5 實作 DELETE `/api/comments/{comment_id}` - 刪除留言 -- [x] 2.6 實作 @mention 解析邏輯(從留言內容提取 @username) - -## 3. Backend API - Notifications - -- [x] 3.1 建立 Notification schemas -- [x] 3.2 實作 NotificationService(建立、發送通知) -- [x] 3.3 實作 GET `/api/notifications` - 取得通知列表 -- [x] 3.4 實作 PUT `/api/notifications/{id}/read` - 標記已讀 -- [x] 3.5 實作 PUT `/api/notifications/read-all` - 全部已讀 -- [x] 3.6 實作 GET `/api/notifications/unread-count` - 未讀數量 - -## 4. Backend API - Blockers - -- [x] 4.1 建立 Blocker schemas -- [x] 4.2 實作 POST `/api/tasks/{task_id}/blockers` - 標記阻礙 -- [x] 4.3 實作 PUT `/api/blockers/{blocker_id}/resolve` - 解除阻礙 -- [x] 4.4 實作 GET `/api/tasks/{task_id}/blockers` - 取得阻礙歷史 -- [x] 4.5 整合阻礙通知(通知專案 Owner) - -## 5. WebSocket Integration - -- [x] 5.1 建立 WebSocket connection manager -- [x] 5.2 實作 `/ws/notifications` endpoint -- [x] 5.3 整合 Redis Pub/Sub 發布通知 -- [x] 5.4 實作 WebSocket 認證(JWT token) -- [x] 5.5 實作心跳機制(keepalive) - -## 6. Backend API - User Search - -- [x] 6.1 實作 GET `/api/users/search?q={query}` - 使用者搜尋(支援 @mention 自動完成) - -## 7. Frontend - Comments - -- [x] 7.1 建立 CommentList 元件 -- [x] 7.2 建立 CommentItem 元件(支援巢狀回覆) -- [x] 7.3 建立 CommentForm 元件(含 @mention 自動完成) -- [x] 7.4 整合至 Task 詳情頁 - -## 8. Frontend - Notifications - -- [x] 8.1 建立 NotificationContext(狀態管理) -- [x] 8.2 建立 NotificationBell 元件(未讀數量 badge) -- [x] 8.3 建立 NotificationList 元件 -- [x] 8.4 建立 NotificationItem 元件 -- [x] 8.5 整合 WebSocket 連線 - -## 9. Frontend - Blockers - -- [x] 9.1 建立 BlockerDialog 元件(標記阻礙表單) -- [x] 9.2 建立 BlockerHistory 元件 -- [x] 9.3 整合至 Task 詳情頁 - -## 10. Testing - -- [x] 10.1 Comment API 單元測試 -- [x] 10.2 Notification API 單元測試 -- [x] 10.3 Blocker API 單元測試 -- [x] 10.4 WebSocket 連線測試 -- [x] 10.5 @mention 解析測試 diff --git a/openspec/changes/archive/2025-12-29-add-document-management/design.md b/openspec/changes/archive/2025-12-29-add-document-management/design.md deleted file mode 100644 index e50ef7b..0000000 --- a/openspec/changes/archive/2025-12-29-add-document-management/design.md +++ /dev/null @@ -1,159 +0,0 @@ -## Context - -文件管理是專案系統的核心功能,需要考慮: -- 檔案存儲策略(本地 vs NAS) -- 安全需求(加密、浮水印) -- 版本控制邏輯 -- 大檔案處理 - -## Goals / Non-Goals - -**Goals:** -- 提供任務層級的檔案附件功能 -- 支援基本 CRUD 操作 -- 整合現有 Audit Trail -- 為未來 NAS 整合預留擴展性 - -**Non-Goals:** -- 即時協作編輯(不在此範圍) -- 全文搜尋(未來功能) -- 檔案預覽(未來功能) - -## Decisions - -### 1. 檔案存儲策略 - -**Decision:** 使用本地檔案系統 + 環境變數配置路徑 - -**Rationale:** -- 開發階段使用本地存儲簡化設置 -- 生產環境透過環境變數指向 NAS 掛載點 -- 路徑結構:`{UPLOAD_DIR}/{project_id}/{task_id}/{attachment_id}/{version}/` - -**Alternatives considered:** -- 直接 NAS 整合 - 開發環境設置複雜 -- S3 相容存儲 - 增加外部依賴 - -### 2. 版本控制模型 - -**Decision:** 主表 + 版本歷史表分離 - -``` -pjctrl_attachments (主表,存儲最新版本資訊) -├── id, task_id, filename, current_version, ... - -pjctrl_attachment_versions (歷史表) -├── id, attachment_id, version, file_path, ... -``` - -**Rationale:** -- 主表快速查詢當前附件 -- 歷史表保留所有版本 -- 上傳同名檔案 → 建立新版本 → 更新主表 current_version - -### 3. 加密策略 - -**Decision:** 使用 Fernet (基於 AES-128-CBC) 對稱加密 - -**Rationale:** -- Python cryptography 庫內建支援 -- 自動處理 IV、padding、HMAC 驗證 -- 比原生 AES-256 更安全(防止實作錯誤) -- 效能足夠(非大規模加密場景) - -**實作方式:** -- 加密金鑰存儲於環境變數 `ENCRYPTION_KEY` -- 僅對機密專案的附件加密 -- 加密狀態存於 `is_encrypted` 欄位 - -### 4. 浮水印策略 - -**Decision:** 下載時動態生成浮水印 - -**Rationale:** -- 不修改原始檔案 -- 每次下載包含當下使用者資訊 -- 使用 Pillow (圖片) 和 PyMuPDF (PDF) 處理 - -**浮水印內容:** -- 使用者姓名 + 工號 -- 下載時間 -- 機密等級(如適用) - -### 5. 檔案大小限制 - -**Decision:** 預設 50MB,可透過環境變數調整 - -```python -MAX_FILE_SIZE = int(os.getenv("MAX_FILE_SIZE_MB", 50)) * 1024 * 1024 -``` - -**Rationale:** -- 避免記憶體溢出 -- 大檔案使用串流處理 -- 生產環境可依需求調整 - -## Data Model - -```sql --- 附件主表 -pjctrl_attachments -├── id: UUID (PK) -├── task_id: UUID (FK -> tasks) -├── filename: VARCHAR(255) -- 顯示名稱 -├── original_filename: VARCHAR(255) -- 原始上傳名稱 -├── mime_type: VARCHAR(100) -├── file_size: BIGINT -├── current_version: INT DEFAULT 1 -├── is_encrypted: BOOLEAN DEFAULT false -├── uploaded_by: UUID (FK -> users) -├── is_deleted: BOOLEAN DEFAULT false -├── created_at: TIMESTAMP -└── updated_at: TIMESTAMP - --- 版本歷史表 -pjctrl_attachment_versions -├── id: UUID (PK) -├── attachment_id: UUID (FK -> attachments) -├── version: INT -├── file_path: VARCHAR(1000) -- 實際存儲路徑 -├── file_size: BIGINT -├── checksum: VARCHAR(64) -- SHA-256 -├── uploaded_by: UUID (FK -> users) -├── created_at: TIMESTAMP -└── INDEX (attachment_id, version) -``` - -## API Design - -``` -POST /api/tasks/{task_id}/attachments # 上傳附件 -GET /api/tasks/{task_id}/attachments # 列出附件 -GET /api/attachments/{id} # 取得附件資訊 -GET /api/attachments/{id}/download # 下載附件 -GET /api/attachments/{id}/download?version=2 # 下載特定版本 -DELETE /api/attachments/{id} # 刪除附件(軟刪除) -GET /api/attachments/{id}/versions # 版本歷史 -POST /api/attachments/{id}/restore/{version} # 回復特定版本 -``` - -## Risks / Trade-offs - -| Risk | Mitigation | -|------|------------| -| 大檔案記憶體溢出 | 使用串流上傳/下載 | -| 加密金鑰洩漏 | 僅存於環境變數,定期輪換 | -| 浮水印處理耗時 | 限制支援的檔案類型,非同步處理大檔案 | -| NAS 不可用 | 本地存儲 fallback,監控告警 | - -## Migration Plan - -1. Phase 1: 建立模型、基本 CRUD、本地存儲 -2. Phase 2: 版本控制 -3. Phase 3: 加密與浮水印(可選) - -## Open Questions - -- [ ] NAS 掛載點路徑確認 -- [ ] 生產環境加密金鑰管理方式(KMS? Vault?) -- [ ] 是否需要支援拖放上傳多檔案? diff --git a/openspec/changes/archive/2025-12-29-add-document-management/proposal.md b/openspec/changes/archive/2025-12-29-add-document-management/proposal.md deleted file mode 100644 index 433767a..0000000 --- a/openspec/changes/archive/2025-12-29-add-document-management/proposal.md +++ /dev/null @@ -1,44 +0,0 @@ -# Change: Add Document Management - -## Why -專案管理系統需要文件附件功能,讓使用者能在任務層級上傳、下載、管理檔案。半導體產業對機密文件有特殊安全需求(加密存儲、浮水印追溯)。 - -## What Changes -- **新增 Attachment 模型** - 支援任務層級的檔案附件 -- **新增 AttachmentVersion 模型** - 檔案版本控制 -- **新增 File Storage Service** - 本地檔案存儲(可擴展至 NAS) -- **新增 Attachment API** - 上傳、下載、刪除、版本管理 -- **新增加密功能** - AES-256 加密存儲(機密專案) -- **新增浮水印功能** - 下載時動態加入使用者資訊 -- **整合 Audit Trail** - 記錄所有文件操作 - -## Impact -- Affected specs: `document-management`, `audit-trail` (已實作) -- Affected code: - - `backend/app/models/` - 新增 attachment 相關模型 - - `backend/app/api/` - 新增 attachments router - - `backend/app/services/` - 新增 file_service, encryption_service - - `frontend/src/components/` - 新增附件元件 - - `backend/migrations/` - 新增資料表 - -## Implementation Phases - -### Phase 1: Basic Attachments (MVP) -- 檔案上傳/下載/刪除 -- 本地檔案存儲 -- 基本 API 與前端整合 -- Audit 日誌整合 - -### Phase 2: Version Control -- 同名檔案版本控制 -- 版本歷史查看 -- 版本回復 - -### Phase 3: Security Features (Optional) -- AES-256 加密存儲 -- 動態浮水印(圖片/PDF) -- 加密金鑰管理 - -## Dependencies -- audit-trail (已完成) - 用於文件操作日誌 -- collaboration (已完成) - 可在評論中引用附件 diff --git a/openspec/changes/archive/2025-12-29-add-document-management/specs/document-management/spec.md b/openspec/changes/archive/2025-12-29-add-document-management/specs/document-management/spec.md deleted file mode 100644 index 952d00e..0000000 --- a/openspec/changes/archive/2025-12-29-add-document-management/specs/document-management/spec.md +++ /dev/null @@ -1,44 +0,0 @@ -## MODIFIED Requirements - -### Requirement: Audit Trail -系統 SHALL 記錄所有文件操作供稽核追溯,整合現有 audit-trail 模組。 - -#### Scenario: 操作日誌記錄 -- **GIVEN** 使用者對附件執行任何操作(上傳、下載、刪除) -- **WHEN** 操作完成 -- **THEN** 系統透過 AuditService 記錄操作至 `pjctrl_audit_logs` -- **AND** 使用 event_type: `attachment.upload`, `attachment.download`, `attachment.delete` - -#### Scenario: 稽核查詢 -- **GIVEN** 稽核人員需要查詢文件操作歷史 -- **WHEN** 稽核人員透過 Audit API 執行查詢 -- **THEN** 可依 resource_type=attachment 篩選 -- **AND** 顯示完整操作歷史 - -## ADDED Requirements - -### Requirement: File Size Limits -系統 SHALL 限制上傳檔案大小以確保系統穩定性。 - -#### Scenario: 檔案大小驗證 -- **GIVEN** 使用者上傳檔案 -- **WHEN** 檔案大小超過限制(預設 50MB) -- **THEN** 系統拒絕上傳並回傳錯誤訊息 - -#### Scenario: 大小限制配置 -- **GIVEN** 管理者需要調整檔案大小限制 -- **WHEN** 設定環境變數 MAX_FILE_SIZE_MB -- **THEN** 系統使用新的限制值 - -### Requirement: Mime Type Validation -系統 SHALL 驗證上傳檔案類型以確保安全性。 - -#### Scenario: 允許的檔案類型 -- **GIVEN** 使用者上傳附件 -- **WHEN** 檔案類型為常見文件格式(pdf, doc, xls, jpg, png, zip 等) -- **THEN** 系統接受上傳 - -#### Scenario: 危險檔案類型拒絕 -- **GIVEN** 使用者上傳附件 -- **WHEN** 檔案類型為可執行檔(exe, bat, sh, dll 等) -- **THEN** 系統拒絕上傳並回傳錯誤訊息 diff --git a/openspec/changes/archive/2025-12-29-add-document-management/tasks.md b/openspec/changes/archive/2025-12-29-add-document-management/tasks.md deleted file mode 100644 index 70e2d4c..0000000 --- a/openspec/changes/archive/2025-12-29-add-document-management/tasks.md +++ /dev/null @@ -1,71 +0,0 @@ -## Phase 1: Basic Attachments - -### 1.1 Database Schema -- [x] 1.1.1 建立 Attachment model (`pjctrl_attachments`) -- [x] 1.1.2 建立 AttachmentVersion model (`pjctrl_attachment_versions`) -- [x] 1.1.3 建立 Alembic migration -- [x] 1.1.4 新增 Task model 的 attachments relationship - -### 1.2 File Storage Service -- [x] 1.2.1 建立 FileStorageService 類別 -- [x] 1.2.2 實作 save_file() 方法(串流處理) -- [x] 1.2.3 實作 get_file() 方法 -- [x] 1.2.4 實作 delete_file() 方法 -- [x] 1.2.5 新增檔案存儲路徑配置 (UPLOAD_DIR) -- [x] 1.2.6 實作 checksum 計算 (SHA-256) - -### 1.3 Attachment API -- [x] 1.3.1 建立 Attachment schemas (request/response) -- [x] 1.3.2 實作 POST `/api/tasks/{task_id}/attachments` - 上傳 -- [x] 1.3.3 實作 GET `/api/tasks/{task_id}/attachments` - 列表 -- [x] 1.3.4 實作 GET `/api/attachments/{id}` - 詳情 -- [x] 1.3.5 實作 GET `/api/attachments/{id}/download` - 下載 -- [x] 1.3.6 實作 DELETE `/api/attachments/{id}` - 軟刪除 -- [x] 1.3.7 整合 Audit Trail - 記錄上傳/下載/刪除操作 - -### 1.4 Frontend - Basic -- [x] 1.4.1 建立 attachments.ts service -- [x] 1.4.2 建立 AttachmentList 元件 -- [x] 1.4.3 建立 AttachmentUpload 元件(支援拖放) -- [x] 1.4.4 整合至 Task 詳情頁 (TaskAttachments 元件) - -### 1.5 Testing - Phase 1 -- [x] 1.5.1 FileStorageService 單元測試 -- [x] 1.5.2 Attachment API 端點測試 -- [x] 1.5.3 上傳/下載整合測試 - -## Phase 2: Version Control - -### 2.1 Version Logic -- [x] 2.1.1 修改上傳邏輯支援版本控制 -- [x] 2.1.2 實作 GET `/api/attachments/{id}/versions` - 版本歷史 -- [x] 2.1.3 實作 POST `/api/attachments/{id}/restore/{version}` - 回復版本 -- [x] 2.1.4 實作 GET `/api/attachments/{id}/download?version=N` - 下載特定版本 - -### 2.2 Frontend - Version -- [x] 2.2.1 建立 VersionHistory 元件 (integrated in AttachmentList) -- [x] 2.2.2 新增版本選擇下載功能 (in attachments service) -- [x] 2.2.3 新增版本回復功能 (in attachments service) - -### 2.3 Testing - Phase 2 -- [x] 2.3.1 版本控制邏輯測試 -- [x] 2.3.2 版本 API 端點測試 - -## Phase 3: Security Features (Optional) - -### 3.1 Encryption -- [ ] 3.1.1 建立 EncryptionService 類別 -- [ ] 3.1.2 實作 encrypt_file() / decrypt_file() 方法 -- [ ] 3.1.3 新增 Project security_level 欄位(如不存在) -- [ ] 3.1.4 修改上傳邏輯:機密專案自動加密 -- [ ] 3.1.5 修改下載邏輯:自動解密 - -### 3.2 Watermarking -- [ ] 3.2.1 建立 WatermarkService 類別 -- [ ] 3.2.2 實作圖片浮水印(Pillow) -- [ ] 3.2.3 實作 PDF 浮水印(PyMuPDF) -- [ ] 3.2.4 整合至下載流程 - -### 3.3 Testing - Phase 3 -- [ ] 3.3.1 加密/解密測試 -- [ ] 3.3.2 浮水印生成測試 diff --git a/openspec/changes/archive/2025-12-29-fix-audit-trail/design.md b/openspec/changes/archive/2025-12-29-fix-audit-trail/design.md deleted file mode 100644 index 658b0b3..0000000 --- a/openspec/changes/archive/2025-12-29-fix-audit-trail/design.md +++ /dev/null @@ -1,107 +0,0 @@ -## Context - -audit-trail spec 定義了軟刪除、權限變更記錄、append-only 日誌等需求,但現行實作未完全對齊。 - -## Goals / Non-Goals - -**Goals:** -- 任務刪除改為軟刪除,保留資料可追溯 -- 所有權限變更記錄至 audit log -- 確保 audit_logs 表不可被修改或刪除 - -**Non-Goals:** -- 不實作任務還原 UI(僅提供 API) -- 不變更現有 checksum 計算邏輯 - -## Decisions - -### 1. 軟刪除欄位設計 - -**Decision:** 使用 `is_deleted` + `deleted_at` + `deleted_by` - -**Rationale:** -- `is_deleted` 簡化查詢過濾 -- `deleted_at` 提供時間資訊 -- `deleted_by` 追蹤操作者 - -```python -is_deleted = Column(Boolean, default=False, nullable=False) -deleted_at = Column(DateTime, nullable=True) -deleted_by = Column(String(36), ForeignKey("pjctrl_users.id"), nullable=True) -``` - -### 2. 查詢過濾策略 - -**Decision:** API 層過濾,非 ORM 層 - -**Rationale:** -- 保持彈性,部分查詢需要包含已刪除項目 -- 避免 ORM 層複雜性 - -```python -# 預設過濾 -query = query.filter(Task.is_deleted == False) - -# 管理員可查看已刪除 -if include_deleted and current_user.is_system_admin: - query = query.filter() # 不過濾 -``` - -### 3. Append-Only 實作 - -**Decision:** 使用 MySQL/PostgreSQL trigger - -**MySQL:** -```sql -DELIMITER // -CREATE TRIGGER prevent_audit_update BEFORE UPDATE ON pjctrl_audit_logs -FOR EACH ROW BEGIN - SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Audit logs are immutable'; -END// - -CREATE TRIGGER prevent_audit_delete BEFORE DELETE ON pjctrl_audit_logs -FOR EACH ROW BEGIN - SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Audit logs are immutable'; -END// -DELIMITER ; -``` - -### 4. 權限變更事件 - -**Decision:** 記錄以下變更類型 - -| 事件 | event_type | sensitivity | -|------|------------|-------------| -| 角色指派 | user.role_change | high | -| 角色權限更新 | role.permission_change | critical | -| 系統管理員變更 | user.admin_change | critical | - -## Data Model Changes - -```sql --- Task 表新增欄位 -ALTER TABLE pjctrl_tasks ADD COLUMN is_deleted BOOLEAN DEFAULT FALSE NOT NULL; -ALTER TABLE pjctrl_tasks ADD COLUMN deleted_at DATETIME NULL; -ALTER TABLE pjctrl_tasks ADD COLUMN deleted_by VARCHAR(36) NULL; -ALTER TABLE pjctrl_tasks ADD INDEX idx_task_deleted (is_deleted); -``` - -## API Changes - -``` -# 現有 API 行為變更 -DELETE /api/tasks/{id} -> 軟刪除,回傳 200 而非 204 -GET /api/projects/{id}/tasks -> 預設排除 is_deleted=true - -# 新增 API -POST /api/tasks/{id}/restore -> 還原已刪除任務(需權限) -GET /api/projects/{id}/tasks?include_deleted=true -> 管理員可查看全部 -``` - -## Risks / Trade-offs - -| Risk | Mitigation | -|------|------------| -| 軟刪除增加資料量 | 定期歸檔/清理策略 | -| DB trigger 影響效能 | trigger 邏輯簡單,影響極小 | -| 權限變更頻繁觸發 | 僅記錄實際變更 | diff --git a/openspec/changes/archive/2025-12-29-fix-audit-trail/proposal.md b/openspec/changes/archive/2025-12-29-fix-audit-trail/proposal.md deleted file mode 100644 index b0588b1..0000000 --- a/openspec/changes/archive/2025-12-29-fix-audit-trail/proposal.md +++ /dev/null @@ -1,45 +0,0 @@ -# Change: Fix Audit Trail Alignment - -## Why -現行實作與 audit-trail spec 有以下差距: -1. 任務刪除為硬刪除,spec 要求軟刪除 (`is_deleted` 欄位) -2. 權限變更未記錄 `user.permission_change` 事件 -3. 資料庫層未強制 append-only(可被 UPDATE/DELETE) - -## What Changes -- **Task Model** - 新增 `is_deleted`、`deleted_at`、`deleted_by` 欄位 -- **Task API** - 刪除改為軟刪除,查詢預設過濾已刪除 -- **User/Role API** - 權限/角色變更時記錄 `user.permission_change` 事件 -- **Migration** - 新增 Task 軟刪除欄位、設定 audit_logs 表 triggers 防止 UPDATE/DELETE - -## Impact -- Affected specs: `audit-trail` -- Affected code: - - `backend/app/models/task.py` - 新增軟刪除欄位 - - `backend/app/api/tasks/router.py` - 修改刪除邏輯與查詢過濾 - - `backend/app/api/users/router.py` - 新增權限變更審計 - - `backend/migrations/versions/` - 新增遷移 - -## Implementation Phases - -### Phase 1: Task Soft Delete -- 新增 Task 軟刪除欄位 -- 修改 delete_task 為軟刪除 -- 修改查詢過濾已刪除任務 -- 新增 restore_task API (可選) - -### Phase 2: Permission Change Audit -- 角色指派變更記錄 -- 權限更新記錄 -- is_system_admin 變更記錄 - -### Phase 3: Append-Only Enforcement -- DB trigger 防止 UPDATE/DELETE -- 驗證 checksum 機制 - -## Dependencies -- audit-trail (已完成) - -## Technical Decisions -- 軟刪除使用 `is_deleted` boolean 而非時間戳,簡化查詢 -- DB trigger 使用 BEFORE UPDATE/DELETE RAISE EXCEPTION diff --git a/openspec/changes/archive/2025-12-29-fix-audit-trail/specs/audit-trail/spec.md b/openspec/changes/archive/2025-12-29-fix-audit-trail/specs/audit-trail/spec.md deleted file mode 100644 index efee09d..0000000 --- a/openspec/changes/archive/2025-12-29-fix-audit-trail/specs/audit-trail/spec.md +++ /dev/null @@ -1,98 +0,0 @@ -## MODIFIED Requirements - -### Requirement: Delete Operations Tracking -系統 SHALL 追蹤所有刪除操作,支援軟刪除與追溯。 - -#### Scenario: 任務刪除記錄 -- **GIVEN** 使用者刪除任務 -- **WHEN** 刪除操作執行 -- **THEN** 系統執行軟刪除(設定 is_deleted = true, deleted_at, deleted_by) -- **AND** 記錄刪除操作至 audit_logs -- **AND** 子任務同步軟刪除 - -#### Scenario: 附件刪除記錄 -- **GIVEN** 使用者刪除附件 -- **WHEN** 刪除操作執行 -- **THEN** 系統保留檔案於存檔區 -- **AND** 記錄刪除操作詳情 - -#### Scenario: 任務還原 -- **GIVEN** 管理員需要還原已刪除任務 -- **WHEN** 執行還原操作 -- **THEN** 系統設定 is_deleted = false -- **AND** 記錄還原操作 - -### Requirement: Change Logging -系統 SHALL 記錄所有關鍵變更操作,包含誰在何時改了什麼。 - -#### Scenario: 任務欄位變更記錄 -- **GIVEN** 使用者修改任務的任何欄位(如截止日期、狀態、指派者) -- **WHEN** 變更儲存成功 -- **THEN** 系統記錄變更前後的值 -- **AND** 記錄操作者、時間、IP 位址 - -#### Scenario: 專案設定變更記錄 -- **GIVEN** 管理者修改專案設定 -- **WHEN** 設定變更儲存 -- **THEN** 系統記錄所有變更的設定項目 -- **AND** 記錄操作者與時間 - -#### Scenario: 權限變更記錄 -- **GIVEN** 管理者修改使用者權限或角色 -- **WHEN** 權限變更生效 -- **THEN** 系統記錄權限變更詳情 -- **AND** 標記為高敏感度操作 - -#### Scenario: 角色指派變更記錄 -- **GIVEN** 管理者變更使用者角色 -- **WHEN** role_id 變更儲存 -- **THEN** 系統記錄 user.role_change 事件 -- **AND** 標記 sensitivity_level = high - -#### Scenario: 系統管理員變更記錄 -- **GIVEN** 管理者變更使用者 is_system_admin -- **WHEN** 變更生效 -- **THEN** 系統記錄 user.admin_change 事件 -- **AND** 標記 sensitivity_level = critical -- **AND** 觸發即時警示 - -### Requirement: Audit Log Immutability -系統 SHALL 確保稽核日誌不可竄改。 - -#### Scenario: 日誌寫入 -- **GIVEN** 需要記錄稽核事件 -- **WHEN** 日誌寫入 -- **THEN** 日誌記錄不可被修改或刪除 -- **AND** 包含校驗碼確保完整性 - -#### Scenario: 日誌完整性驗證 -- **GIVEN** 稽核人員需要驗證日誌完整性 -- **WHEN** 執行完整性檢查 -- **THEN** 系統驗證所有日誌記錄的校驗碼 -- **AND** 報告任何異常 - -#### Scenario: 防止日誌修改 -- **GIVEN** 任何對 audit_logs 表的 UPDATE 操作 -- **WHEN** 操作執行 -- **THEN** 資料庫 trigger 拒絕操作並拋出錯誤 - -#### Scenario: 防止日誌刪除 -- **GIVEN** 任何對 audit_logs 表的 DELETE 操作 -- **WHEN** 操作執行 -- **THEN** 資料庫 trigger 拒絕操作並拋出錯誤 - -## MODIFIED Data Model - -``` -pjctrl_tasks (新增欄位) -├── is_deleted: BOOLEAN DEFAULT false -├── deleted_at: DATETIME (nullable) -├── deleted_by: UUID (FK -> users, nullable) -└── INDEX idx_task_deleted (is_deleted) -``` - -## MODIFIED Technical Notes - -- 任務刪除改為軟刪除,保留 is_deleted, deleted_at, deleted_by -- 資料庫使用 BEFORE UPDATE/DELETE trigger 強制 append-only -- 查詢 API 預設過濾 is_deleted = true,管理員可用 include_deleted 參數 diff --git a/openspec/changes/archive/2025-12-29-fix-audit-trail/tasks.md b/openspec/changes/archive/2025-12-29-fix-audit-trail/tasks.md deleted file mode 100644 index 5614f32..0000000 --- a/openspec/changes/archive/2025-12-29-fix-audit-trail/tasks.md +++ /dev/null @@ -1,63 +0,0 @@ -## Phase 1: Task Soft Delete - -### 1.1 Database Schema -- [x] 1.1.1 Task model 新增 is_deleted, deleted_at, deleted_by 欄位 -- [x] 1.1.2 建立 Alembic migration -- [x] 1.1.3 新增 idx_task_deleted 索引 - -### 1.2 Task API 修改 -- [x] 1.2.1 修改 delete_task 為軟刪除 -- [x] 1.2.2 修改 list_tasks 預設過濾 is_deleted -- [x] 1.2.3 修改 get_task 檢查 is_deleted -- [x] 1.2.4 新增 include_deleted 查詢參數(管理員) -- [x] 1.2.5 新增 POST /api/tasks/{id}/restore 還原 API - -### 1.3 Cascading Updates -- [x] 1.3.1 子任務隨父任務軟刪除 -- [x] 1.3.2 更新 subtask_count 計算排除已刪除 - -### 1.4 Testing - Phase 1 -- [x] 1.4.1 軟刪除功能測試 -- [x] 1.4.2 查詢過濾測試 -- [x] 1.4.3 還原功能測試 - -## Phase 2: Permission Change Audit - -### 2.1 User Role Change -- [x] 2.1.1 修改 update_user API 記錄 role_id 變更 -- [x] 2.1.2 記錄 is_system_admin 變更 - -### 2.2 Role Permission Change -- [x] 2.2.1 修改 update_role API 記錄 permissions 變更 (事件類型已定義) -- [x] 2.2.2 設定 sensitivity_level = critical - -### 2.3 Audit Alert Integration -- [x] 2.3.1 權限變更觸發高敏感度警示 -- [x] 2.3.2 通知系統管理員 - -### 2.4 Testing - Phase 2 -- [x] 2.4.1 角色變更審計測試 (事件類型已定義並整合) -- [x] 2.4.2 權限變更審計測試 -- [x] 2.4.3 警示觸發測試 - -## Phase 3: Append-Only Enforcement - -### 3.1 Database Triggers -- [x] 3.1.1 建立 prevent_audit_update trigger (需手動執行於 production) -- [x] 3.1.2 建立 prevent_audit_delete trigger (需手動執行於 production) -- [x] 3.1.3 新增 migration 包含 triggers - -### 3.2 Verification -- [x] 3.2.1 測試 UPDATE 被拒絕 (需 production 環境驗證) -- [x] 3.2.2 測試 DELETE 被拒絕 (需 production 環境驗證) -- [x] 3.2.3 確認 INSERT 正常運作 - -### 3.3 Testing - Phase 3 -- [x] 3.3.1 Append-only 強制測試 (trigger 語法已驗證) -- [x] 3.3.2 Checksum 驗證測試 (已有 test_audit.py 測試) - -## Notes - -- **Triggers**: MySQL triggers 需要 SUPER 權限才能在有 binary logging 的環境建立。Migration 會嘗試建立 trigger,失敗時記錄警告。Production 環境需手動執行 trigger SQL。 -- **Tests**: 新增 11 個軟刪除相關測試於 tests/test_soft_delete.py -- **Total Tests**: 153 tests passing diff --git a/openspec/changes/archive/2025-12-30-fix-realtime-notifications/design.md b/openspec/changes/archive/2025-12-30-fix-realtime-notifications/design.md deleted file mode 100644 index a9f7dd7..0000000 --- a/openspec/changes/archive/2025-12-30-fix-realtime-notifications/design.md +++ /dev/null @@ -1,134 +0,0 @@ -## Context - -collaboration spec 要求即時通知透過 WebSocket 推播,但現行 NotificationService 僅寫入資料庫,未實作即時推送。 - -## Goals / Non-Goals - -**Goals:** -- 建立 WebSocket 連線管理基礎設施 -- 通知建立時透過 Redis Pub/Sub 廣播 -- 使用者連線時補送未讀通知 -- 前端即時接收並更新通知 - -**Non-Goals:** -- 不實作通知偏好設定(靜音/訂閱) -- 不實作 Push Notification (PWA/Mobile) -- 不實作通知分組或摺疊 - -## Decisions - -### 1. WebSocket 連線管理 - -**Decision:** 使用 in-memory dict + Redis Pub/Sub - -**Rationale:** -- 單 process 內使用 dict 維護 WebSocket 連線 -- 跨 process 透過 Redis Pub/Sub 廣播 -- 簡單且符合現有架構 - -```python -class ConnectionManager: - def __init__(self): - self.active_connections: Dict[str, List[WebSocket]] = {} - - async def connect(self, user_id: str, websocket: WebSocket): - await websocket.accept() - if user_id not in self.active_connections: - self.active_connections[user_id] = [] - self.active_connections[user_id].append(websocket) - - async def disconnect(self, user_id: str, websocket: WebSocket): - self.active_connections[user_id].remove(websocket) - if not self.active_connections[user_id]: - del self.active_connections[user_id] - - async def send_to_user(self, user_id: str, message: dict): - if user_id in self.active_connections: - for connection in self.active_connections[user_id]: - await connection.send_json(message) -``` - -### 2. Redis Pub/Sub 架構 - -**Decision:** 使用 user-specific channel - -**Rationale:** -- Channel 命名: `notifications:{user_id}` -- 避免 broadcast 給不相關的 worker -- 減少訊息處理量 - -```python -async def publish_notification(user_id: str, notification: dict): - channel = f"notifications:{user_id}" - await redis.publish(channel, json.dumps(notification)) - -async def subscribe_notifications(user_id: str): - pubsub = redis.pubsub() - await pubsub.subscribe(f"notifications:{user_id}") - return pubsub -``` - -### 3. 連線時補送未讀 - -**Decision:** 連線建立後立即查詢並推送 - -**Rationale:** -- 確保使用者不漏接通知 -- 簡化前端狀態同步邏輯 - -```python -@router.websocket("/ws/notifications") -async def websocket_endpoint(websocket: WebSocket, token: str = Query(...)): - user = await verify_ws_token(token) - await manager.connect(user.id, websocket) - - # 連線時補送未讀 - unread = get_unread_notifications(db, user.id) - await websocket.send_json({ - "type": "unread_sync", - "notifications": [n.dict() for n in unread] - }) - - # 開始監聽 - await listen_for_notifications(user.id, websocket) -``` - -### 4. 訊息格式 - -**Decision:** 統一 JSON 格式 - -```json -{ - "type": "notification", - "data": { - "id": "uuid", - "type": "mention|assignment|blocker|...", - "title": "...", - "message": "...", - "reference_type": "task|comment", - "reference_id": "uuid", - "created_at": "ISO8601" - } -} -``` - -## API Changes - -``` -# 新增 WebSocket endpoint -WS /ws/notifications?token={jwt_token} - -# 訊息類型 --> {"type": "unread_sync", "notifications": [...]} # 連線時 --> {"type": "notification", "data": {...}} # 新通知 --> {"type": "mark_read", "notification_id": "..."} # 已讀確認 -<- {"type": "ping"} # 心跳 -``` - -## Risks / Trade-offs - -| Risk | Mitigation | -|------|------------| -| WebSocket 連線數量大 | 使用心跳偵測清理斷線 | -| Redis Pub/Sub 可靠性 | Pub/Sub 為 fire-and-forget,已有 DB 紀錄作為 fallback | -| Token 驗證 in query | WebSocket 標準限制,token 有過期機制 | diff --git a/openspec/changes/archive/2025-12-30-fix-realtime-notifications/proposal.md b/openspec/changes/archive/2025-12-30-fix-realtime-notifications/proposal.md deleted file mode 100644 index 8b58848..0000000 --- a/openspec/changes/archive/2025-12-30-fix-realtime-notifications/proposal.md +++ /dev/null @@ -1,50 +0,0 @@ -# Change: Fix Real-time Notifications Alignment - -## Why -現行實作與 collaboration spec 的 Real-time Notifications requirement 有以下差距: -1. 通知僅寫入資料庫,未透過 WebSocket 即時推播 -2. 未使用 Redis Pub/Sub 處理多 process 推播 -3. 使用者連線時未補送未讀通知 - -## What Changes -- **WebSocket Manager** - 建立 WebSocket 連線管理模組 -- **Redis Pub/Sub** - 整合 Redis 處理跨 process 通知推播 -- **NotificationService** - 新增即時推播呼叫 -- **API** - 新增 `/ws/notifications` WebSocket endpoint -- **Frontend** - 整合 WebSocket 接收即時通知 - -## Impact -- Affected specs: `collaboration` -- Affected code: - - `backend/app/core/websocket.py` - 新增 WebSocket 管理 - - `backend/app/core/redis_pubsub.py` - 新增 Redis Pub/Sub 服務 - - `backend/app/services/notification_service.py` - 加入即時推播 - - `backend/app/api/notifications/router.py` - 新增 WebSocket endpoint - - `frontend/src/services/websocket.ts` - 新增 WebSocket client - - `frontend/src/contexts/NotificationContext.tsx` - 整合即時通知 - -## Implementation Phases - -### Phase 1: WebSocket Infrastructure -- WebSocket 連線管理器 -- 使用者連線/斷線處理 -- 連線時補送未讀通知 - -### Phase 2: Redis Pub/Sub Integration -- Redis Pub/Sub 服務封裝 -- 多 process 通知廣播 -- 訊息序列化/反序列化 - -### Phase 3: Service Integration -- NotificationService 加入推播 -- 前端 WebSocket client -- 未讀數量即時更新 - -## Dependencies -- collaboration (已完成) -- Redis 已在 user-auth 中使用 - -## Technical Decisions -- 使用 FastAPI WebSocket 原生支援 -- Redis Pub/Sub 處理多 worker 同步 -- 使用者以 user_id 為 channel key diff --git a/openspec/changes/archive/2025-12-30-fix-realtime-notifications/specs/collaboration/spec.md b/openspec/changes/archive/2025-12-30-fix-realtime-notifications/specs/collaboration/spec.md deleted file mode 100644 index cf4b5ce..0000000 --- a/openspec/changes/archive/2025-12-30-fix-realtime-notifications/specs/collaboration/spec.md +++ /dev/null @@ -1,36 +0,0 @@ -## MODIFIED Requirements - -### Requirement: Real-time Notifications -系統 SHALL 透過 WebSocket 與 Redis Pub/Sub 推播即時通知。 - -#### Scenario: 即時通知推播 -- **GIVEN** 發生需要通知的事件(如:被指派任務、被 @提及、阻礙標記) -- **WHEN** NotificationService.create_notification() 執行 -- **THEN** 系統透過 Redis Pub/Sub 發布通知至 `notifications:{user_id}` channel -- **AND** 訂閱該 channel 的 WebSocket 連線接收訊息 -- **AND** ConnectionManager 推送通知給使用者的 WebSocket - -#### Scenario: 連線時補送未讀 -- **GIVEN** 使用者建立 WebSocket 連線 -- **WHEN** 連線驗證成功 -- **THEN** 系統查詢該使用者的未讀通知 (is_read = false) -- **AND** 透過 unread_sync 訊息一次推送所有未讀通知 -- **AND** 開始訂閱 Redis channel 接收新通知 - -#### Scenario: 心跳偵測 -- **GIVEN** 使用者已建立 WebSocket 連線 -- **WHEN** 連線超過心跳間隔無回應 -- **THEN** 系統將連線標記為斷線並從 ConnectionManager 移除 - -## MODIFIED Technical Notes - -- 使用 Redis Pub/Sub 處理即時通知推播 -- WebSocket 連線管理: - - ConnectionManager 維護 user_id → WebSocket[] 映射 - - 心跳偵測清理斷線連線 - - Token 驗證透過 query parameter -- 通知推播流程: - 1. NotificationService.create_notification() 建立通知 - 2. 呼叫 redis_pubsub.publish_notification() 發布 - 3. 訂閱該 user channel 的 worker 收到訊息 - 4. ConnectionManager.send_to_user() 推送給連線的 WebSocket diff --git a/openspec/changes/archive/2025-12-30-fix-realtime-notifications/tasks.md b/openspec/changes/archive/2025-12-30-fix-realtime-notifications/tasks.md deleted file mode 100644 index 3f4401e..0000000 --- a/openspec/changes/archive/2025-12-30-fix-realtime-notifications/tasks.md +++ /dev/null @@ -1,56 +0,0 @@ -## Phase 1: WebSocket Infrastructure - -### 1.1 Connection Manager -- [ ] 1.1.1 建立 backend/app/core/websocket.py -- [ ] 1.1.2 實作 ConnectionManager class -- [ ] 1.1.3 實作 connect/disconnect/send_to_user 方法 -- [ ] 1.1.4 加入心跳偵測機制 - -### 1.2 WebSocket Endpoint -- [ ] 1.2.1 新增 WS /ws/notifications endpoint -- [ ] 1.2.2 實作 WebSocket token 驗證 -- [ ] 1.2.3 連線時查詢並推送未讀通知 -- [ ] 1.2.4 處理 WebSocket 異常與斷線 - -### 1.3 Testing - Phase 1 -- [ ] 1.3.1 WebSocket 連線測試 -- [ ] 1.3.2 未讀通知補送測試 -- [ ] 1.3.3 斷線處理測試 - -## Phase 2: Redis Pub/Sub Integration - -### 2.1 Redis Pub/Sub Service -- [ ] 2.1.1 建立 backend/app/core/redis_pubsub.py -- [ ] 2.1.2 實作 publish_notification 函數 -- [ ] 2.1.3 實作 subscribe_user_channel 函數 -- [ ] 2.1.4 訊息 JSON 序列化處理 - -### 2.2 Cross-Process Broadcasting -- [ ] 2.2.1 WebSocket endpoint 訂閱 user channel -- [ ] 2.2.2 收到 Redis 訊息時推送給連線 -- [ ] 2.2.3 處理訂閱錯誤與重連 - -### 2.3 Testing - Phase 2 -- [ ] 2.3.1 Redis Pub/Sub 單元測試 -- [ ] 2.3.2 跨 process 通知測試(手動驗證) - -## Phase 3: Service Integration - -### 3.1 NotificationService 整合 -- [ ] 3.1.1 create_notification 後呼叫 publish_notification -- [ ] 3.1.2 確保所有通知類型都即時推播 -- [ ] 3.1.3 處理 Redis 連線失敗 gracefully - -### 3.2 Frontend WebSocket Client -- [ ] 3.2.1 建立 frontend/src/services/websocket.ts -- [ ] 3.2.2 實作 WebSocket 連線與重連邏輯 -- [ ] 3.2.3 訊息處理與分發 - -### 3.3 NotificationContext 整合 -- [ ] 3.3.1 修改 NotificationContext 使用 WebSocket -- [ ] 3.3.2 收到通知時更新未讀數量 -- [ ] 3.3.3 收到 unread_sync 時同步狀態 - -### 3.4 Testing - Phase 3 -- [ ] 3.4.1 完整即時通知流程測試 -- [ ] 3.4.2 前端 WebSocket 整合測試 diff --git a/openspec/changes/archive/2025-12-30-fix-weekly-report/design.md b/openspec/changes/archive/2025-12-30-fix-weekly-report/design.md deleted file mode 100644 index 0369e21..0000000 --- a/openspec/changes/archive/2025-12-30-fix-weekly-report/design.md +++ /dev/null @@ -1,119 +0,0 @@ -## Context - -automation spec 定義週報須包含:已完成、進行中、逾期、阻礙中、下週預計任務清單。現行實作僅提供數量統計與部分摘要。 - -## Goals / Non-Goals - -**Goals:** -- 週報內容包含完整任務清單 -- 新增阻礙中任務清單 -- 新增下週預計完成任務清單 - -**Non-Goals:** -- 不實作週報自訂欄位篩選 -- 不實作週報匯出 PDF/Excel - -## Decisions - -### 1. 週報內容結構 - -**Decision:** 擴充 content JSON 結構 - -**Current:** -```json -{ - "summary": { "completed_count": 5, "in_progress_count": 3, ... }, - "projects": [ - { - "completed_tasks": [{"id": "...", "title": "..."}], // 限制 5 筆 - "overdue_tasks": [...] // 限制 5 筆 - } - ] -} -``` - -**Proposed:** -```json -{ - "summary": { - "completed_count": 5, - "in_progress_count": 3, - "overdue_count": 2, - "blocked_count": 1, - "next_week_count": 4, - "total_tasks": 15 - }, - "projects": [ - { - "project_id": "uuid", - "project_title": "Project Name", - "completed_tasks": [ - {"id": "...", "title": "...", "completed_at": "ISO8601", "assignee_name": "..."} - ], - "in_progress_tasks": [ - {"id": "...", "title": "...", "assignee_name": "...", "due_date": "ISO8601"} - ], - "overdue_tasks": [ - {"id": "...", "title": "...", "due_date": "ISO8601", "days_overdue": 3} - ], - "blocked_tasks": [ - {"id": "...", "title": "...", "blocker_reason": "...", "blocked_since": "ISO8601"} - ], - "next_week_tasks": [ - {"id": "...", "title": "...", "due_date": "ISO8601", "assignee_name": "..."} - ] - } - ] -} -``` - -### 2. 阻礙任務識別 - -**Decision:** 查詢 Blocker 表關聯 - -```python -# 取得有未解除阻礙的任務 -blocked_task_ids = db.query(Blocker.task_id).filter( - Blocker.resolved_at.is_(None) -).subquery() - -blocked_tasks = db.query(Task).filter( - Task.project_id.in_(project_ids), - Task.id.in_(blocked_task_ids) -).all() -``` - -### 3. 下週預計任務 - -**Decision:** due_date 在下週一至週日 - -```python -next_week_start = week_end # 本週末 = 下週初 -next_week_end = next_week_start + timedelta(days=7) - -next_week_tasks = db.query(Task).filter( - Task.project_id.in_(project_ids), - Task.due_date >= next_week_start, - Task.due_date < next_week_end, - Task.status.not_in(["done", "completed"]) -).all() -``` - -### 4. 任務明細欄位 - -**Decision:** 包含以下欄位供顯示 - -| 任務類型 | 額外欄位 | -|---------|---------| -| completed | completed_at (updated_at), assignee_name | -| in_progress | assignee_name, due_date | -| overdue | due_date, days_overdue | -| blocked | blocker_reason, blocked_since | -| next_week | due_date, assignee_name | - -## Risks / Trade-offs - -| Risk | Mitigation | -|------|------------| -| 大量任務導致 content JSON 過大 | 評估後續加入分頁或限制 | -| 阻礙查詢需 JOIN | 使用 subquery 減少 N+1 | diff --git a/openspec/changes/archive/2025-12-30-fix-weekly-report/proposal.md b/openspec/changes/archive/2025-12-30-fix-weekly-report/proposal.md deleted file mode 100644 index b293383..0000000 --- a/openspec/changes/archive/2025-12-30-fix-weekly-report/proposal.md +++ /dev/null @@ -1,37 +0,0 @@ -# Change: Fix Weekly Report Content Alignment - -## Why -現行 ReportService.get_weekly_stats 與 automation spec 的週報內容要求有差距: -1. 任務清單僅顯示 5 筆摘要,spec 要求完整清單 -2. 未包含阻礙中任務清單 (blocker_flag = true) -3. 未包含下週預計完成任務 (due_date 在下週) - -## What Changes -- **ReportService** - 擴充 get_weekly_stats 回傳完整任務明細 -- **Report Content** - 新增 blocked_tasks 與 next_week_tasks 欄位 -- **ReportHistory** - content JSON 結構擴充 - -## Impact -- Affected specs: `automation` -- Affected code: - - `backend/app/services/report_service.py` - 擴充週報內容 - - `frontend/src/components/WeeklyReportPreview.tsx` - 顯示完整清單 - -## Implementation Phases - -### Phase 1: Backend Report Enhancement -- 擴充 get_weekly_stats 回傳完整任務清單 -- 新增 blocked_tasks 欄位 -- 新增 next_week_tasks 欄位 - -### Phase 2: Frontend Display -- 更新 WeeklyReportPreview 顯示完整清單 -- 可摺疊/展開的任務分類區塊 - -## Dependencies -- automation (已完成) -- collaboration (blocker 功能) - -## Technical Decisions -- 任務清單不設上限,由前端分頁或摺疊處理 -- 下週預計任務以 due_date 在下週一至週日為準 diff --git a/openspec/changes/archive/2025-12-30-fix-weekly-report/specs/automation/spec.md b/openspec/changes/archive/2025-12-30-fix-weekly-report/specs/automation/spec.md deleted file mode 100644 index fc23afd..0000000 --- a/openspec/changes/archive/2025-12-30-fix-weekly-report/specs/automation/spec.md +++ /dev/null @@ -1,38 +0,0 @@ -## MODIFIED Requirements - -### Requirement: Automated Weekly Report -系統 SHALL 每週五下午 4:00 自動彙整完整任務清單發送給主管。 - -#### Scenario: 週報內容完整清單 -- **GIVEN** 週報生成中 -- **WHEN** 系統彙整資料 -- **THEN** 週報包含各專案的: - - 本週已完成任務清單(含 completed_at, assignee_name) - - 進行中任務清單(含 assignee_name, due_date) - - 逾期任務警示(含 due_date, days_overdue) - - 阻礙中任務清單(含 blocker_reason, blocked_since) - - 下週預計完成任務(含 due_date, assignee_name) -- **AND** 不設任務數量上限 - -#### Scenario: 阻礙任務識別 -- **GIVEN** 任務有未解除的 Blocker 記錄 -- **WHEN** 週報查詢阻礙任務 -- **THEN** 系統查詢 Blocker 表 resolved_at IS NULL 的任務 -- **AND** 顯示阻礙原因與開始時間 - -#### Scenario: 下週預計任務 -- **GIVEN** 任務的 due_date 在下週範圍內 -- **WHEN** 週報查詢下週預計任務 -- **THEN** 系統篩選 due_date >= 下週一 且 < 下週日 -- **AND** 排除已完成狀態的任務 - -## MODIFIED Technical Notes - -- 週報 content JSON 結構擴充: - - summary: 包含 blocked_count, next_week_count - - projects[].completed_tasks: 無數量限制,含 completed_at, assignee_name - - projects[].in_progress_tasks: 新增欄位 - - projects[].blocked_tasks: 新增欄位,含 blocker_reason, blocked_since - - projects[].next_week_tasks: 新增欄位,含 due_date, assignee_name -- 阻礙任務透過 Blocker 表 subquery 查詢 -- 下週計算以本週結束後 7 天為範圍 diff --git a/openspec/changes/archive/2025-12-30-fix-weekly-report/tasks.md b/openspec/changes/archive/2025-12-30-fix-weekly-report/tasks.md deleted file mode 100644 index 267d73a..0000000 --- a/openspec/changes/archive/2025-12-30-fix-weekly-report/tasks.md +++ /dev/null @@ -1,38 +0,0 @@ -## Phase 1: Backend Report Enhancement - -### 1.1 ReportService 擴充 -- [x] 1.1.1 移除 completed_tasks/overdue_tasks 的 5 筆限制 -- [x] 1.1.2 新增 in_progress_tasks 完整清單 -- [x] 1.1.3 新增 blocked_tasks 查詢與清單 -- [x] 1.1.4 新增 next_week_tasks 查詢與清單 -- [x] 1.1.5 擴充 summary 包含 blocked_count 與 next_week_count - -### 1.2 任務明細欄位 -- [x] 1.2.1 completed_tasks 加入 completed_at, assignee_name -- [x] 1.2.2 in_progress_tasks 加入 assignee_name, due_date -- [x] 1.2.3 overdue_tasks 加入 days_overdue 計算 -- [x] 1.2.4 blocked_tasks 加入 blocker_reason, blocked_since -- [x] 1.2.5 next_week_tasks 加入 due_date, assignee_name - -### 1.3 Testing - Phase 1 -- [x] 1.3.1 週報內容結構測試 -- [x] 1.3.2 阻礙任務查詢測試 -- [x] 1.3.3 下週預計任務測試 - -## Phase 2: Frontend Display - -### 2.1 WeeklyReportPreview 更新 -- [x] 2.1.1 新增 BlockedTasksSection 元件 -- [x] 2.1.2 新增 NextWeekTasksSection 元件 -- [x] 2.1.3 更新 CompletedTasksSection 顯示完整清單 -- [x] 2.1.4 更新 InProgressTasksSection 顯示完整清單 -- [x] 2.1.5 更新 OverdueTasksSection 顯示 days_overdue - -### 2.2 UI 改善 -- [x] 2.2.1 可摺疊區塊設計 -- [x] 2.2.2 任務項目樣式統一 -- [x] 2.2.3 逾期/阻礙 highlight 樣式 - -### 2.3 Testing - Phase 2 -- [x] 2.3.1 前端週報顯示測試 -- [x] 2.3.2 空清單狀態測試 diff --git a/openspec/changes/archive/2026-01-04-add-capacity-update-api/proposal.md b/openspec/changes/archive/2026-01-04-add-capacity-update-api/proposal.md deleted file mode 100644 index 9fc7a00..0000000 --- a/openspec/changes/archive/2026-01-04-add-capacity-update-api/proposal.md +++ /dev/null @@ -1,15 +0,0 @@ -# Change: Add Capacity Update API - -## Why -MED-007: The `Capacity Planning` requirement exists but there is no API to update user capacity. The original design had `PUT /api/users/{id}/capacity` but it was replaced with `GET /api/workload/me`. Managers cannot adjust team members' weekly capacity. - -## What Changes -- Add `PUT /api/users/{user_id}/capacity` endpoint -- Add capacity validation logic -- Record capacity changes in audit trail - -## Impact -- Affected specs: resource-management -- Affected code: - - `backend/app/api/users/router.py` (modify) - - `backend/app/schemas/user.py` (modify) diff --git a/openspec/changes/archive/2026-01-04-add-capacity-update-api/specs/resource-management/spec.md b/openspec/changes/archive/2026-01-04-add-capacity-update-api/specs/resource-management/spec.md deleted file mode 100644 index 41ebdf1..0000000 --- a/openspec/changes/archive/2026-01-04-add-capacity-update-api/specs/resource-management/spec.md +++ /dev/null @@ -1,29 +0,0 @@ -## MODIFIED Requirements - -### Requirement: Capacity Planning - -系統 SHALL 支援人員容量規劃,包含預設容量與臨時調整。 - -#### Scenario: 設定人員預設容量 -- **GIVEN** 管理者需要設定人員的週工時上限 -- **WHEN** 管理者更新使用者的 `capacity` 值 -- **THEN** 系統儲存新的容量設定 -- **AND** 後續負載計算使用新容量值 - -#### Scenario: 容量為零處理 -- **GIVEN** 使用者的容量設為 0 -- **WHEN** 系統計算該使用者的負載 -- **THEN** `load_percentage` 顯示為 `null` -- **AND** `load_level` 顯示為 `unavailable` - -#### Scenario: 容量更新 API -- **GIVEN** 管理者需要更新團隊成員的容量 -- **WHEN** 管理者呼叫 `PUT /api/users/{user_id}/capacity` 並提供新容量值 -- **THEN** 系統驗證容量值在有效範圍內 (0-168 小時) -- **AND** 更新使用者的 capacity 欄位 -- **AND** 記錄變更至稽核日誌 - -#### Scenario: 容量更新權限控制 -- **GIVEN** 一般使用者嘗試更新他人容量 -- **WHEN** 使用者呼叫 `PUT /api/users/{other_id}/capacity` -- **THEN** 系統拒絕請求並回傳 403 Forbidden diff --git a/openspec/changes/archive/2026-01-04-add-capacity-update-api/tasks.md b/openspec/changes/archive/2026-01-04-add-capacity-update-api/tasks.md deleted file mode 100644 index 00614aa..0000000 --- a/openspec/changes/archive/2026-01-04-add-capacity-update-api/tasks.md +++ /dev/null @@ -1,24 +0,0 @@ -# Tasks: add-capacity-update-api - -## Phase 1: Backend - Schema & Validation - -- [x] 1.1 Add CapacityUpdate schema to `backend/app/schemas/user.py` -- [x] 1.2 Add capacity validation (must be >= 0, <= 168 hours/week) - -## Phase 2: Backend - API Endpoint - -- [x] 2.1 Implement `PUT /api/users/{user_id}/capacity` endpoint -- [x] 2.2 Add permission check (only admin/manager can update others) -- [x] 2.3 Record capacity change in audit trail -- [x] 2.4 Invalidate workload cache after capacity update - -## Phase 3: Backend - Testing - -- [x] 3.1 Unit tests for capacity update endpoint -- [x] 3.2 Permission tests (admin vs regular user) - -## Validation Criteria - -- Only authorized users can update capacity -- Capacity changes are audit logged -- Workload calculations reflect new capacity immediately diff --git a/openspec/changes/archive/2026-01-04-add-project-health-dashboard/proposal.md b/openspec/changes/archive/2026-01-04-add-project-health-dashboard/proposal.md deleted file mode 100644 index a3a5013..0000000 --- a/openspec/changes/archive/2026-01-04-add-project-health-dashboard/proposal.md +++ /dev/null @@ -1,18 +0,0 @@ -# Change: Add Project Health Dashboard - -## Why -MED-006: The `Multi-Project Health Dashboard` requirement exists in the spec but has no implementation. Managers cannot view an overview of all projects' health status, making it difficult to identify at-risk projects. - -## What Changes -- Add `pjctrl_project_health` database table and migration -- Implement backend API endpoints for project health data -- Create frontend Project Health Dashboard page -- Add health score calculation service - -## Impact -- Affected specs: resource-management -- Affected code: - - `backend/app/models/project_health.py` (new) - - `backend/app/api/health/router.py` (new) - - `backend/app/services/health_service.py` (new) - - `frontend/src/pages/ProjectHealthPage.tsx` (new) diff --git a/openspec/changes/archive/2026-01-04-add-project-health-dashboard/specs/resource-management/spec.md b/openspec/changes/archive/2026-01-04-add-project-health-dashboard/specs/resource-management/spec.md deleted file mode 100644 index 7e28d3e..0000000 --- a/openspec/changes/archive/2026-01-04-add-project-health-dashboard/specs/resource-management/spec.md +++ /dev/null @@ -1,28 +0,0 @@ -## MODIFIED Requirements - -### Requirement: Multi-Project Health Dashboard -系統 SHALL 提供多專案健康看板,讓主管一覽所有專案狀態。 - -#### Scenario: 專案健康總覽 -- **GIVEN** 主管負責多個專案 -- **WHEN** 主管開啟健康看板 -- **THEN** 顯示所有專案的進度、風險指標、延遲任務數 -- **AND** 可依風險程度排序 - -#### Scenario: 專案延遲警示 -- **GIVEN** 專案有任務超過截止日期 -- **WHEN** 主管查看健康看板 -- **THEN** 該專案標示為延遲狀態 -- **AND** 顯示延遲任務數量與影響 - -#### Scenario: 專案健康 API -- **GIVEN** 後端系統運行中 -- **WHEN** 客戶端請求 `GET /api/projects/health` -- **THEN** 系統回傳所有可存取專案的健康數據 -- **AND** 包含 `total_tasks`, `completed_tasks`, `overdue_tasks`, `blocked_tasks`, `risk_score` - -#### Scenario: 單一專案健康詳情 -- **GIVEN** 主管需要查看特定專案詳情 -- **WHEN** 客戶端請求 `GET /api/projects/{id}/health` -- **THEN** 系統回傳該專案的詳細健康數據 -- **AND** 包含任務分類統計與風險評估 diff --git a/openspec/changes/archive/2026-01-04-add-project-health-dashboard/tasks.md b/openspec/changes/archive/2026-01-04-add-project-health-dashboard/tasks.md deleted file mode 100644 index a57f6e6..0000000 --- a/openspec/changes/archive/2026-01-04-add-project-health-dashboard/tasks.md +++ /dev/null @@ -1,35 +0,0 @@ -# Tasks: add-project-health-dashboard - -## Phase 1: Backend - Database & Model - -- [x] 1.1 Create ProjectHealth model (`backend/app/models/project_health.py`) -- [x] 1.2 Create Alembic migration for `pjctrl_project_health` table -- [x] 1.3 Create ProjectHealth schemas (`backend/app/schemas/project_health.py`) - -## Phase 2: Backend - Service & API - -- [x] 2.1 Create HealthService class (`backend/app/services/health_service.py`) - - Calculate risk score based on overdue/blocked tasks - - Aggregate project statistics -- [x] 2.2 Create health router (`backend/app/api/health/router.py`) -- [x] 2.3 Implement `GET /api/projects/health` - List all projects health -- [x] 2.4 Implement `GET /api/projects/{id}/health` - Single project health -- [x] 2.5 Register health router in main.py - -## Phase 3: Backend - Testing - -- [x] 3.1 Unit tests for HealthService -- [x] 3.2 API endpoint tests - -## Phase 4: Frontend - UI Components - -- [x] 4.1 Create ProjectHealthPage.tsx -- [x] 4.2 Create ProjectHealthCard component -- [x] 4.3 Add route to App.tsx -- [x] 4.4 Add navigation link in Layout - -## Validation Criteria - -- Risk score correctly reflects overdue and blocked tasks -- Dashboard shows all accessible projects -- Color-coded status indicators (green/yellow/red) diff --git a/openspec/changes/archive/2026-01-04-add-rate-limiting/proposal.md b/openspec/changes/archive/2026-01-04-add-rate-limiting/proposal.md deleted file mode 100644 index 1f83f2e..0000000 --- a/openspec/changes/archive/2026-01-04-add-rate-limiting/proposal.md +++ /dev/null @@ -1,18 +0,0 @@ -# Change: Add Rate Limiting for API Security - -## Why -Login endpoint and other sensitive APIs lack rate limiting protection, making them vulnerable to brute force attacks and DoS attempts. This is a critical security gap identified in the code review (HIGH-003). - -## What Changes -- Add slowapi dependency for rate limiting -- Implement rate limiting middleware -- Apply rate limits to login endpoint (5 requests/minute) -- Apply rate limits to other sensitive endpoints -- Return proper 429 Too Many Requests responses - -## Impact -- Affected specs: user-auth -- Affected code: - - `backend/requirements.txt` - add slowapi - - `backend/app/main.py` - initialize limiter - - `backend/app/api/auth/router.py` - apply rate limits diff --git a/openspec/changes/archive/2026-01-04-add-rate-limiting/specs/user-auth/spec.md b/openspec/changes/archive/2026-01-04-add-rate-limiting/specs/user-auth/spec.md deleted file mode 100644 index b68614c..0000000 --- a/openspec/changes/archive/2026-01-04-add-rate-limiting/specs/user-auth/spec.md +++ /dev/null @@ -1,20 +0,0 @@ -## ADDED Requirements - -### Requirement: API Rate Limiting -The system SHALL implement rate limiting to protect against brute force attacks and DoS attempts. - -#### Scenario: Login rate limit enforcement -- **GIVEN** a client IP has made 5 login attempts within 1 minute -- **WHEN** the client attempts another login -- **THEN** the system returns HTTP 429 Too Many Requests -- **AND** the response includes a Retry-After header - -#### Scenario: Rate limit window reset -- **GIVEN** a client has exceeded the rate limit -- **WHEN** the rate limit window expires (1 minute) -- **THEN** the client can make new requests - -#### Scenario: Rate limit per IP -- **GIVEN** rate limiting is IP-based -- **WHEN** different IPs make requests -- **THEN** each IP has its own rate limit counter diff --git a/openspec/changes/archive/2026-01-04-add-rate-limiting/tasks.md b/openspec/changes/archive/2026-01-04-add-rate-limiting/tasks.md deleted file mode 100644 index cf1026f..0000000 --- a/openspec/changes/archive/2026-01-04-add-rate-limiting/tasks.md +++ /dev/null @@ -1,16 +0,0 @@ -# Tasks: Add Rate Limiting - -## 1. Backend Implementation -- [x] 1.1 Add slowapi to requirements.txt -- [x] 1.2 Create rate limiter configuration in `app/core/rate_limiter.py` -- [x] 1.3 Initialize limiter in main.py with exception handlers -- [x] 1.4 Apply @limiter.limit("5/minute") to login endpoint -- [x] 1.5 Apply appropriate limits to password reset and registration endpoints (if exist) - N/A, no such endpoints exist - -## 2. Testing -- [x] 2.1 Write test for rate limit enforcement -- [x] 2.2 Verify 429 response format matches API standards -- [x] 2.3 Test rate limit reset after window expires - covered by memory storage reset in test fixtures - -## 3. Documentation -- [x] 3.1 Update API documentation with rate limit information - inline comments in code diff --git a/openspec/changes/archive/2026-01-04-add-resource-management-ui/proposal.md b/openspec/changes/archive/2026-01-04-add-resource-management-ui/proposal.md deleted file mode 100644 index 17fe961..0000000 --- a/openspec/changes/archive/2026-01-04-add-resource-management-ui/proposal.md +++ /dev/null @@ -1,23 +0,0 @@ -# Change: Add Resource Management UI - -## Why -HIGH-006: The Resource Management module backend API exists but there is no frontend UI. Managers cannot visualize team workload distribution, making capacity planning difficult. - -Issues addressed: -- HIGH-006: 資源管理模組前端 UI 未開發 - -## What Changes -- Create WorkloadPage.tsx with workload heatmap visualization -- Create WorkloadHeatmap.tsx component for color-coded user load display -- Create WorkloadUserDetail.tsx component for detailed task breakdown -- Add /workload route to App.tsx router -- Add navigation link in Dashboard or sidebar - -## Impact -- Affected specs: resource-management -- Affected code: - - `frontend/src/pages/WorkloadPage.tsx` - new page - - `frontend/src/components/WorkloadHeatmap.tsx` - new component - - `frontend/src/components/WorkloadUserDetail.tsx` - new component - - `frontend/src/services/workload.ts` - new API service - - `frontend/src/App.tsx` - add route diff --git a/openspec/changes/archive/2026-01-04-add-resource-management-ui/specs/resource-management/spec.md b/openspec/changes/archive/2026-01-04-add-resource-management-ui/specs/resource-management/spec.md deleted file mode 100644 index ec4dae2..0000000 --- a/openspec/changes/archive/2026-01-04-add-resource-management-ui/specs/resource-management/spec.md +++ /dev/null @@ -1,26 +0,0 @@ -## ADDED Requirements - -### Requirement: Workload Heatmap UI -The system SHALL provide a visual workload heatmap interface for managers. - -#### Scenario: View workload heatmap -- **GIVEN** user is logged in as manager or admin -- **WHEN** user navigates to /workload page -- **THEN** system displays a heatmap showing all accessible users' workload -- **AND** each user cell is color-coded by load level (green/yellow/red) - -#### Scenario: Navigate between weeks -- **GIVEN** user is viewing the workload page -- **WHEN** user clicks previous/next week buttons -- **THEN** the heatmap updates to show that week's workload data - -#### Scenario: View user workload details -- **GIVEN** user is viewing the workload heatmap -- **WHEN** user clicks on a specific user's cell -- **THEN** a modal/drawer opens showing that user's task breakdown -- **AND** tasks show title, project, time estimate, and due date - -#### Scenario: Filter by department -- **GIVEN** user is a system admin -- **WHEN** user selects a department from the filter -- **THEN** the heatmap shows only users from that department diff --git a/openspec/changes/archive/2026-01-04-add-resource-management-ui/tasks.md b/openspec/changes/archive/2026-01-04-add-resource-management-ui/tasks.md deleted file mode 100644 index 897ba36..0000000 --- a/openspec/changes/archive/2026-01-04-add-resource-management-ui/tasks.md +++ /dev/null @@ -1,29 +0,0 @@ -# Tasks: Add Resource Management UI - -## 1. API Service -- [x] 1.1 Create workload.ts service with API calls for heatmap, user detail, and my workload - -## 2. Components -- [x] 2.1 Create WorkloadHeatmap.tsx component - - Display users in a grid/table - - Color-coded load levels (green=normal, yellow=warning, red=overloaded) - - Show allocated/capacity hours and percentage -- [x] 2.2 Create WorkloadUserDetail.tsx component - - Show user's task list for the selected week - - Display task title, project, time estimate, due date - -## 3. Page -- [x] 3.1 Create WorkloadPage.tsx - - Week selector (navigate between weeks) - - Department filter (for admins) - Note: Basic implementation, can be enhanced - - Integrate WorkloadHeatmap component - - Click user to show WorkloadUserDetail in modal/drawer - -## 4. Integration -- [x] 4.1 Add /workload route to App.tsx -- [x] 4.2 Add navigation link in Layout sidebar - -## 5. Testing -- [x] 5.1 Verify heatmap loads correctly -- [x] 5.2 Verify user detail modal shows tasks -- [x] 5.3 Verify week navigation works diff --git a/openspec/changes/archive/2026-01-04-add-schedule-triggers/proposal.md b/openspec/changes/archive/2026-01-04-add-schedule-triggers/proposal.md deleted file mode 100644 index 0ba3010..0000000 --- a/openspec/changes/archive/2026-01-04-add-schedule-triggers/proposal.md +++ /dev/null @@ -1,16 +0,0 @@ -# Change: Add Schedule Triggers - -## Why -MED-008: The `Trigger Conditions` requirement includes time-based triggers (cron expressions), but the execution logic is not implemented. The scheduler setup exists for weekly reports but schedule-type triggers cannot be executed. - -## What Changes -- Implement cron expression parsing for schedule triggers -- Add scheduler job for evaluating schedule triggers -- Support deadline reminder triggers - -## Impact -- Affected specs: automation -- Affected code: - - `backend/app/services/trigger_service.py` (modify) - - `backend/app/services/trigger_scheduler.py` (new) - - `backend/app/scheduler.py` (modify) diff --git a/openspec/changes/archive/2026-01-04-add-schedule-triggers/specs/automation/spec.md b/openspec/changes/archive/2026-01-04-add-schedule-triggers/specs/automation/spec.md deleted file mode 100644 index d155779..0000000 --- a/openspec/changes/archive/2026-01-04-add-schedule-triggers/specs/automation/spec.md +++ /dev/null @@ -1,31 +0,0 @@ -## MODIFIED Requirements - -### Requirement: Trigger Conditions -系統 SHALL 支援多種觸發條件類型。 - -#### Scenario: 欄位變更條件 -- **GIVEN** 觸發器設定為「當 Status 欄位變更為特定值」 -- **WHEN** 任務的 Status 欄位變更為該值 -- **THEN** 觸發器被觸發 - -#### Scenario: 時間條件 -- **GIVEN** 觸發器設定為「每週五下午 4:00」 -- **WHEN** 系統時間達到設定時間 -- **THEN** 觸發器被觸發 - -#### Scenario: 複合條件 -- **GIVEN** 觸發器設定為「當 Status = 完成 且 Priority = 高」 -- **WHEN** 任務同時滿足兩個條件 -- **THEN** 觸發器被觸發 - -#### Scenario: Cron 表達式觸發 -- **GIVEN** 觸發器設定為 cron 表達式 (如 `0 9 * * 1` 每週一早上 9 點) -- **WHEN** 系統時間匹配 cron 表達式 -- **THEN** 系統評估並執行該觸發器 -- **AND** 記錄執行結果至 trigger_logs - -#### Scenario: 截止日期提醒 -- **GIVEN** 觸發器設定為「截止日前 N 天提醒」 -- **WHEN** 任務距離截止日剩餘 N 天 -- **THEN** 系統發送提醒通知給任務指派者 -- **AND** 每個任務每個提醒設定只觸發一次 diff --git a/openspec/changes/archive/2026-01-04-add-schedule-triggers/tasks.md b/openspec/changes/archive/2026-01-04-add-schedule-triggers/tasks.md deleted file mode 100644 index 0064022..0000000 --- a/openspec/changes/archive/2026-01-04-add-schedule-triggers/tasks.md +++ /dev/null @@ -1,26 +0,0 @@ -# Tasks: add-schedule-triggers - -## Phase 1: Backend - Cron Support - -- [x] 1.1 Add croniter dependency to requirements.txt -- [x] 1.2 Create TriggerSchedulerService (`backend/app/services/trigger_scheduler.py`) -- [x] 1.3 Implement cron expression validation in trigger creation -- [x] 1.4 Implement `evaluate_schedule_triggers()` method - -## Phase 2: Backend - Scheduler Integration - -- [x] 2.1 Add scheduled job to evaluate schedule triggers (every minute) -- [x] 2.2 Implement deadline reminder logic (check tasks N days before due) -- [x] 2.3 Update trigger logs for schedule trigger executions - -## Phase 3: Backend - Testing - -- [x] 3.1 Unit tests for cron expression parsing -- [x] 3.2 Unit tests for deadline reminder logic -- [x] 3.3 Integration tests for schedule trigger execution - -## Validation Criteria - -- [x] Cron expressions are validated on trigger creation -- [x] Schedule triggers execute at specified times -- [x] Deadline reminders sent N days before task due date diff --git a/openspec/changes/archive/2026-01-04-add-watermark-feature/proposal.md b/openspec/changes/archive/2026-01-04-add-watermark-feature/proposal.md deleted file mode 100644 index 9d46cf5..0000000 --- a/openspec/changes/archive/2026-01-04-add-watermark-feature/proposal.md +++ /dev/null @@ -1,16 +0,0 @@ -# Change: Add Watermark Feature - -## Why -MED-009: The `Dynamic Watermarking` requirement exists in the spec but is not implemented. Downloaded files do not contain user watermarks, making it impossible to trace document leaks. - -## What Changes -- Create WatermarkService for image and PDF watermarking -- Integrate watermark generation into download flow -- Support configurable watermark content (name, employee ID, timestamp) - -## Impact -- Affected specs: document-management -- Affected code: - - `backend/app/services/watermark_service.py` (new) - - `backend/app/api/attachments/router.py` (modify download endpoint) - - `backend/requirements.txt` (add Pillow, PyMuPDF) diff --git a/openspec/changes/archive/2026-01-04-add-watermark-feature/specs/document-management/spec.md b/openspec/changes/archive/2026-01-04-add-watermark-feature/specs/document-management/spec.md deleted file mode 100644 index 6db0c96..0000000 --- a/openspec/changes/archive/2026-01-04-add-watermark-feature/specs/document-management/spec.md +++ /dev/null @@ -1,37 +0,0 @@ -## MODIFIED Requirements - -### Requirement: Dynamic Watermarking -系統 SHALL 在下載時自動為檔案加上使用者浮水印。 - -#### Scenario: 圖片浮水印 -- **GIVEN** 使用者下載圖片類型附件 (PNG, JPG, JPEG) -- **WHEN** 系統處理下載請求 -- **THEN** 自動加上包含使用者姓名、工號、下載時間的浮水印 -- **AND** 浮水印位置不影響主要內容 - -#### Scenario: PDF 浮水印 -- **GIVEN** 使用者下載 PDF 類型附件 -- **WHEN** 系統處理下載請求 -- **THEN** 每頁加上浮水印 -- **AND** 浮水印透明度適中 - -#### Scenario: 浮水印內容 -- **GIVEN** 需要加上浮水印 -- **WHEN** 系統生成浮水印 -- **THEN** 浮水印包含: - - 使用者姓名 - - 使用者工號 - - 下載日期時間 - - 機密等級標示(如適用) - -#### Scenario: 不支援的檔案類型 -- **GIVEN** 使用者下載非圖片/PDF 類型附件 -- **WHEN** 系統處理下載請求 -- **THEN** 直接提供原始檔案下載 -- **AND** 不嘗試加上浮水印 - -#### Scenario: 浮水印服務異常處理 -- **GIVEN** 浮水印生成過程發生錯誤 -- **WHEN** 系統無法完成浮水印處理 -- **THEN** 記錄錯誤日誌 -- **AND** 提供原始檔案下載(降級處理) diff --git a/openspec/changes/archive/2026-01-04-add-watermark-feature/tasks.md b/openspec/changes/archive/2026-01-04-add-watermark-feature/tasks.md deleted file mode 100644 index 12db1cc..0000000 --- a/openspec/changes/archive/2026-01-04-add-watermark-feature/tasks.md +++ /dev/null @@ -1,27 +0,0 @@ -# Tasks: add-watermark-feature - -## Phase 1: Backend - Dependencies & Service - -- [x] 1.1 Add Pillow and PyMuPDF (fitz) to requirements.txt -- [x] 1.2 Create WatermarkService class (`backend/app/services/watermark_service.py`) -- [x] 1.3 Implement `add_image_watermark(image_path, user, output_path)` method -- [x] 1.4 Implement `add_pdf_watermark(pdf_path, user, output_path)` method - -## Phase 2: Backend - Integration - -- [x] 2.1 Modify download endpoint to apply watermark -- [x] 2.2 Add watermark configuration (enable/disable per project) -- [x] 2.3 Handle unsupported file types gracefully (skip watermark) - -## Phase 3: Backend - Testing - -- [x] 3.1 Unit tests for image watermarking -- [x] 3.2 Unit tests for PDF watermarking -- [x] 3.3 Integration tests for download with watermark - -## Validation Criteria - -- Downloaded images contain visible watermark with user info -- Downloaded PDFs have watermark on each page -- Watermark includes: user name, employee ID, download timestamp -- Non-image/PDF files download without modification diff --git a/openspec/changes/archive/2026-01-04-enhance-frontend-ux/proposal.md b/openspec/changes/archive/2026-01-04-enhance-frontend-ux/proposal.md deleted file mode 100644 index 2bc19f0..0000000 --- a/openspec/changes/archive/2026-01-04-enhance-frontend-ux/proposal.md +++ /dev/null @@ -1,26 +0,0 @@ -# Change: Enhance Frontend UX with Kanban View and Component Integration - -## Why -The current frontend only implements a basic list view for tasks and has several components that are built but not integrated. This limits user productivity and leaves implemented features inaccessible. - -Issues addressed: -- HIGH-005: Only list view exists, need Kanban view -- HIGH-007: Comments, Attachments, Triggers components exist but aren't integrated -- HIGH-008: Task creation lacks assignee selection and time estimation - -## What Changes -- Add Kanban board view with drag-and-drop status changes -- Add view toggle between List and Kanban views -- Integrate TaskAttachments component into task detail -- Integrate Comments component into task detail -- Add assignee dropdown to task creation/editing -- Add due date and time estimate fields to task form -- Create task detail modal/drawer component - -## Impact -- Affected specs: task-management -- Affected code: - - `frontend/src/pages/Tasks.tsx` - add view toggle, enhance create modal - - `frontend/src/components/KanbanBoard.tsx` - new component - - `frontend/src/components/TaskDetailModal.tsx` - new component - - `frontend/src/services/api.ts` - add user list endpoint call diff --git a/openspec/changes/archive/2026-01-04-enhance-frontend-ux/specs/task-management/spec.md b/openspec/changes/archive/2026-01-04-enhance-frontend-ux/specs/task-management/spec.md deleted file mode 100644 index 7d2e0d0..0000000 --- a/openspec/changes/archive/2026-01-04-enhance-frontend-ux/specs/task-management/spec.md +++ /dev/null @@ -1,58 +0,0 @@ -## ADDED Requirements - -### Requirement: Kanban View -The system SHALL provide a Kanban board view for tasks with drag-and-drop status management. - -#### Scenario: View Kanban board -- **GIVEN** user is on the Tasks page -- **WHEN** user selects Kanban view -- **THEN** tasks are displayed in columns grouped by status -- **AND** each column header shows the status name and task count - -#### Scenario: Drag task to change status -- **GIVEN** user is viewing the Kanban board -- **WHEN** user drags a task card to a different status column -- **THEN** the task status is updated via API -- **AND** the card moves to the new column -- **AND** other users viewing the board see the update - -#### Scenario: View toggle persistence -- **GIVEN** user switches to Kanban view -- **WHEN** user navigates away and returns -- **THEN** the Kanban view is still selected - -### Requirement: Task Detail Modal -The system SHALL provide a task detail modal with comments and attachments. - -#### Scenario: Open task detail -- **GIVEN** user is viewing tasks in any view -- **WHEN** user clicks on a task -- **THEN** a modal opens showing task details -- **AND** the modal includes comments section -- **AND** the modal includes attachments section - -#### Scenario: Edit task in modal -- **GIVEN** user has task detail modal open -- **WHEN** user modifies task fields and saves -- **THEN** the task is updated via API -- **AND** the task list/board reflects the changes - -### Requirement: Task Assignment UI -The system SHALL allow assigning tasks to users during creation and editing. - -#### Scenario: Assign task during creation -- **GIVEN** user is creating a new task -- **WHEN** user selects an assignee from the dropdown -- **THEN** the task is created with the selected assignee - -#### Scenario: Change task assignee -- **GIVEN** user has task detail modal open -- **WHEN** user changes the assignee -- **THEN** the task assignee is updated -- **AND** the new assignee receives a notification - -#### Scenario: Set due date and time estimate -- **GIVEN** user is creating or editing a task -- **WHEN** user sets due date and time estimate -- **THEN** the values are saved with the task -- **AND** the task appears on the appropriate date in calendar view diff --git a/openspec/changes/archive/2026-01-04-enhance-frontend-ux/tasks.md b/openspec/changes/archive/2026-01-04-enhance-frontend-ux/tasks.md deleted file mode 100644 index 3f565e9..0000000 --- a/openspec/changes/archive/2026-01-04-enhance-frontend-ux/tasks.md +++ /dev/null @@ -1,27 +0,0 @@ -# Tasks: Enhance Frontend UX - -## 1. Kanban View Implementation -- [x] 1.1 Create KanbanBoard.tsx component with column layout -- [x] 1.2 Implement drag-and-drop using native HTML5 drag API -- [x] 1.3 Call PATCH /tasks/{id}/status on drop -- [x] 1.4 Add view toggle (List/Kanban) to Tasks page header -- [x] 1.5 Persist view preference in localStorage - -## 2. Task Detail Modal -- [x] 2.1 Create TaskDetailModal.tsx component -- [x] 2.2 Integrate existing Comments component -- [x] 2.3 Integrate existing TaskAttachments component -- [x] 2.4 Add task editing capability within modal -- [x] 2.5 Wire up modal opening on task row/card click - -## 3. Task Assignment UI -- [x] 3.1 Add user search/dropdown component for assignee -- [x] 3.2 Integrate assignee field in task create modal -- [x] 3.3 Integrate assignee field in task detail modal -- [x] 3.4 Add due date picker component -- [x] 3.5 Add time estimate input fields - -## 4. Testing -- [x] 4.1 Test Kanban drag-and-drop functionality - QA reviewed -- [x] 4.2 Verify task assignment updates correctly - QA reviewed -- [x] 4.3 Test comments and attachments integration - QA reviewed diff --git a/openspec/changes/archive/2026-01-05-add-calendar-view/proposal.md b/openspec/changes/archive/2026-01-05-add-calendar-view/proposal.md deleted file mode 100644 index 222d948..0000000 --- a/openspec/changes/archive/2026-01-05-add-calendar-view/proposal.md +++ /dev/null @@ -1,91 +0,0 @@ -# Proposal: Add Calendar View - -**Change ID:** `add-calendar-view` -**Issue Reference:** `FEAT-004` (issues.md) -**Status:** Draft -**Author:** Claude -**Date:** 2026-01-05 - -## Summary - -實作行事曆視角,以月/週/日視圖呈現任務截止日期,方便使用者規劃工作時程。 - -## Motivation - -行事曆視角提供直覺的日期導向任務規劃: -- 快速掌握本週/本月截止的任務 -- 識別特定日期的工作量 -- 避免任務截止日期過度集中 -- 與個人行事曆習慣一致 - -目前系統需要在列表或看板中逐一檢視任務日期,無法一覽全局。 - -## Scope - -### In Scope -- Frontend: Calendar 元件(月視圖、週視圖、日視圖) -- Frontend: 任務依截止日期顯示在行事曆上 -- Frontend: 點擊任務開啟詳情 Modal -- Frontend: 拖拉任務更改截止日期 -- Frontend: 篩選器(依指派者、狀態、優先級) - -### Out of Scope -- 與外部行事曆(Google Calendar、Outlook)同步 -- 重複任務功能 -- 行事曆分享 - -## Design Decisions - -### 前端函式庫選擇 -建議使用 **FullCalendar**: - -| 函式庫 | 優點 | 缺點 | -|--------|------|------| -| FullCalendar | 功能完整、React 支援好、拖拉支援 | 需要 premium 功能要付費 | -| react-big-calendar | 開源、輕量 | 功能較少 | -| @schedule-x/react | 現代設計 | 社群較小 | - -**建議**: 使用 FullCalendar(MIT 授權核心功能足夠) - -### 視圖模式 -| 視圖 | 說明 | -|------|------| -| Month | 月曆格式,每格顯示當日任務(最多 3 個,其餘 "+N more")| -| Week | 週視圖,每格顯示當日所有任務 | -| Day | 日視圖,當天任務完整列表 | - -### 任務顯示規則 -- 依 due_date 顯示在對應日期 -- 任務卡片顯示:標題、狀態顏色標記、優先級圖示 -- 已完成任務可選顯示/隱藏 -- 已過期未完成任務特殊標記(紅色邊框) - -### 拖拉更新日期 -- 拖拉任務至其他日期更新 due_date -- 使用現有 PUT /api/tasks/{id} API -- 樂觀更新,失敗時回滾 - -### API 使用 -``` -# 使用現有 API,加入日期範圍篩選 -GET /api/projects/{project_id}/tasks?due_after=2026-01-01&due_before=2026-01-31 -``` - -## Affected Specs - -| Spec | Change Type | -|------|-------------| -| task-management | MODIFIED (Multiple Views requirement 行事曆細節) | - -## Risks & Mitigations - -| Risk | Mitigation | -|------|------------| -| 單日任務過多影響顯示 | 月視圖最多顯示 3 個,其餘收合 | -| 效能問題 | 只載入當前視圖月份的任務 | -| 與甘特圖日期衝突 | 行事曆編輯 due_date,甘特圖可編輯 start_date + due_date | - -## Dependencies - -- FullCalendar(或其他行事曆函式庫) -- 現有 Task API 的 due_date 篩選支援 diff --git a/openspec/changes/archive/2026-01-05-add-calendar-view/specs/task-management/spec.md b/openspec/changes/archive/2026-01-05-add-calendar-view/specs/task-management/spec.md deleted file mode 100644 index 7e302a2..0000000 --- a/openspec/changes/archive/2026-01-05-add-calendar-view/specs/task-management/spec.md +++ /dev/null @@ -1,51 +0,0 @@ -## MODIFIED Requirements - -### Requirement: Multiple Views -系統 SHALL 支援多維視角:看板 (Kanban)、甘特圖 (Gantt)、列表 (List)、行事曆 (Calendar)。 - -#### Scenario: 行事曆視角 -- **GIVEN** 使用者選擇行事曆視角 -- **WHEN** 系統載入專案任務 -- **THEN** 任務依截止日期顯示在行事曆上 - -#### Scenario: 行事曆視圖切換 -- **GIVEN** 使用者正在查看行事曆 -- **WHEN** 使用者切換視圖模式(月、週、日) -- **THEN** 行事曆相應調整顯示格式 -- **AND** 任務正確顯示在對應日期 - -#### Scenario: 月視圖任務顯示 -- **GIVEN** 使用者選擇月視圖 -- **WHEN** 某日有超過 3 個任務 -- **THEN** 顯示前 3 個任務 -- **AND** 顯示 "+N more" 連結可展開查看全部 - -#### Scenario: 點擊任務查看詳情 -- **GIVEN** 使用者正在查看行事曆 -- **WHEN** 使用者點擊任務 -- **THEN** 系統開啟任務詳情 Modal -- **AND** 可在 Modal 中編輯任務 - -#### Scenario: 拖拉調整截止日期 -- **GIVEN** 使用者正在查看行事曆 -- **WHEN** 使用者拖拉任務至其他日期 -- **THEN** 系統更新任務的 due_date -- **AND** 任務顯示在新日期 - -#### Scenario: 已過期任務標示 -- **GIVEN** 任務的 due_date 已過期 -- **WHEN** 任務狀態不是「已完成」 -- **THEN** 任務顯示特殊標記(紅色邊框或背景) -- **AND** 提醒使用者注意 - -#### Scenario: 日期範圍載入 -- **GIVEN** 使用者查看行事曆 -- **WHEN** 系統載入任務 -- **THEN** 只載入當前視圖日期範圍內的任務 -- **AND** 切換月份時動態載入對應任務 - -#### Scenario: 行事曆篩選 -- **GIVEN** 使用者正在查看行事曆 -- **WHEN** 使用者設定篩選條件(指派者、狀態、優先級) -- **THEN** 行事曆只顯示符合條件的任務 -- **AND** 篩選條件在視圖切換時保留 diff --git a/openspec/changes/archive/2026-01-05-add-calendar-view/tasks.md b/openspec/changes/archive/2026-01-05-add-calendar-view/tasks.md deleted file mode 100644 index 9e61769..0000000 --- a/openspec/changes/archive/2026-01-05-add-calendar-view/tasks.md +++ /dev/null @@ -1,80 +0,0 @@ -# Tasks: Add Calendar View - -## Backend Tasks - -### 1. Add date range filter to Tasks API -- [x] Modify GET /api/projects/{project_id}/tasks to accept `due_after` and `due_before` query params -- [x] Add filter logic to query -- [x] Add API tests -- **驗證**: 可依日期範圍篩選任務 ✅ - -## Frontend Tasks - -### 2. Install and configure FullCalendar -- [x] Install @fullcalendar/react and related packages -- [x] Install @fullcalendar/daygrid (month view) -- [x] Install @fullcalendar/timegrid (week/day view) -- [x] Install @fullcalendar/interaction (drag & drop) -- **驗證**: 套件安裝成功 ✅ - -### 3. Create CalendarView component -- [x] Create `frontend/src/components/CalendarView.tsx` -- [x] Configure calendar with month/week/day views -- [x] Style calendar to match application theme -- [x] Add view toggle buttons -- **驗證**: 行事曆正確渲染 ✅ - -### 4. Load and display tasks on calendar -- [x] Fetch tasks from API with date range -- [x] Transform tasks to FullCalendar event format -- [x] Display task title, status color, priority icon -- [x] Handle overdue tasks (special styling) -- **驗證**: 任務正確顯示在對應日期 ✅ - -### 5. Implement task click to open detail modal -- [x] Handle event click in calendar -- [x] Open TaskDetailModal with selected task -- [x] Refresh calendar on task update -- **驗證**: 點擊任務開啟詳情 ✅ - -### 6. Implement drag-to-change date -- [x] Enable drag & drop in FullCalendar -- [x] Handle event drop to get new date -- [x] Call PUT /api/tasks/{id} with new due_date -- [x] Show optimistic update, rollback on error -- **驗證**: 拖拉更新日期可正常運作 ✅ - -### 7. Add filter controls -- [x] Add assignee filter dropdown -- [x] Add status filter (show/hide completed) -- [x] Add priority filter -- [x] Persist filter in URL or localStorage -- **驗證**: 篩選器正確過濾任務 ✅ - -### 8. Integrate Calendar view into Tasks page -- [x] Add "Calendar" option to view toggle -- [x] Store view preference in localStorage -- [x] Handle view switching -- **驗證**: 可在 List/Kanban/Gantt/Calendar 之間切換 ✅ - -## Task Dependencies - -``` -[1] Date Range Filter API - ↓ -[2] FullCalendar Setup → [3] CalendarView Component - ↓ - [4] Task Display - ↓ - [5] Click → Modal - ↓ - [6] Drag to Change Date - ↓ - [7] Filters - ↓ - [8] View Integration -``` - -- Task 1 (Backend) 可平行於 Tasks 2-3 (Frontend) 開發 -- Task 4 需要 Task 1 完成(需要日期範圍 API) -- Tasks 5-8 為循序開發 diff --git a/openspec/changes/archive/2026-01-05-add-custom-fields/proposal.md b/openspec/changes/archive/2026-01-05-add-custom-fields/proposal.md deleted file mode 100644 index 1a1e320..0000000 --- a/openspec/changes/archive/2026-01-05-add-custom-fields/proposal.md +++ /dev/null @@ -1,83 +0,0 @@ -# Proposal: Add Custom Fields - -**Change ID:** `add-custom-fields` -**Issue Reference:** `FEAT-001` (issues.md) -**Status:** Draft -**Author:** Claude -**Date:** 2026-01-05 - -## Summary - -實作自定義欄位功能,允許專案管理者為任務新增自定義欄位,包含文字、數字、下拉選單、日期、人員標籤和公式等類型。 - -## Motivation - -目前系統僅支援固定的任務欄位(標題、描述、狀態、優先級等)。不同專案可能需要追蹤特定資料,例如: -- 半導體製程:封裝類型、機台編號、預計良率 -- 軟體開發:Sprint 編號、Story Points、影響版本 -- 行銷活動:活動代碼、預算類別、目標受眾 - -自定義欄位讓專案管理者可以根據需求彈性擴展任務資料結構。 - -## Scope - -### In Scope -- Backend: CustomField 和 TaskCustomValue models -- Backend: Custom fields CRUD API endpoints -- Backend: Formula field 計算引擎 -- Frontend: Custom fields 管理 UI(專案設定頁面) -- Frontend: Task form 動態渲染自定義欄位 -- Frontend: Task list/kanban 顯示自定義欄位值 - -### Out of Scope -- 跨專案共用欄位定義 -- 自定義欄位匯入/匯出 -- 複雜公式函數(僅支援基本數學運算) - -## Design Decisions - -### 欄位類型 -| Type | 說明 | 儲存格式 | -|------|------|----------| -| text | 單行文字 | VARCHAR | -| number | 數字 | DECIMAL | -| dropdown | 下拉選單 | VARCHAR (選項儲存於 options JSON) | -| date | 日期 | DATE | -| person | 人員標籤 | UUID (FK -> users) | -| formula | 公式計算 | DECIMAL (計算結果) | - -### 公式欄位 -- 支援基本數學運算:`+`, `-`, `*`, `/` -- 可引用其他數字欄位:`{field_name}` -- 可引用任務內建欄位:`{original_estimate}`, `{time_spent}` -- 範例公式:`{time_spent} / {original_estimate} * 100` - -### API 設計 -``` -POST /api/projects/{project_id}/custom-fields # 新增欄位定義 -GET /api/projects/{project_id}/custom-fields # 列出所有欄位 -PUT /api/custom-fields/{field_id} # 更新欄位定義 -DELETE /api/custom-fields/{field_id} # 刪除欄位(含所有值) - -# 欄位值透過現有 task API 操作 -PUT /api/tasks/{task_id} # body 包含 custom_values -``` - -## Affected Specs - -| Spec | Change Type | -|------|-------------| -| task-management | MODIFIED (Custom Fields requirement 實作細節) | - -## Risks & Mitigations - -| Risk | Mitigation | -|------|------------| -| 公式欄位循環引用 | 建立欄位時驗證公式不引用自己或形成循環 | -| 大量自定義欄位影響效能 | 限制每專案最多 20 個自定義欄位 | -| 刪除欄位遺失資料 | 刪除前顯示確認對話框,說明將刪除所有相關值 | - -## Dependencies - -- 無外部依賴 -- 需要現有 tasks API 支援 diff --git a/openspec/changes/archive/2026-01-05-add-custom-fields/specs/task-management/spec.md b/openspec/changes/archive/2026-01-05-add-custom-fields/specs/task-management/spec.md deleted file mode 100644 index e36d504..0000000 --- a/openspec/changes/archive/2026-01-05-add-custom-fields/specs/task-management/spec.md +++ /dev/null @@ -1,54 +0,0 @@ -## MODIFIED Requirements - -### Requirement: Custom Fields -系統 SHALL 支援自定義欄位,包含下拉選單、公式、人員標籤等類型。 - -#### Scenario: 新增自定義欄位 -- **GIVEN** 專案管理者需要追蹤特定資料(如:封裝類型、機台編號、預計良率) -- **WHEN** 管理者在專案中新增自定義欄位 -- **THEN** 系統建立欄位定義並套用至該專案所有任務 - -#### Scenario: 編輯自定義欄位 -- **GIVEN** 專案已有自定義欄位 -- **WHEN** 管理者修改欄位名稱或選項 -- **THEN** 系統更新欄位定義 -- **AND** 現有任務的欄位值保持不變 - -#### Scenario: 刪除自定義欄位 -- **GIVEN** 專案已有自定義欄位且有任務包含該欄位的值 -- **WHEN** 管理者刪除該欄位 -- **THEN** 系統顯示確認對話框說明將刪除所有相關值 -- **AND** 確認後刪除欄位定義及所有任務的該欄位值 - -#### Scenario: 公式欄位計算 -- **GIVEN** 任務包含公式類型的自定義欄位 -- **WHEN** 相依欄位的值發生變更 -- **THEN** 系統自動重新計算公式欄位的值 - -#### Scenario: 公式欄位循環引用檢查 -- **GIVEN** 管理者建立公式欄位 -- **WHEN** 公式引用自己或形成循環引用 -- **THEN** 系統拒絕建立並顯示錯誤訊息 - -#### Scenario: 人員標籤欄位 -- **GIVEN** 任務包含人員標籤類型的自定義欄位 -- **WHEN** 使用者選擇人員 -- **THEN** 系統驗證人員存在並建立關聯 -- **AND** 被標籤的人員可收到相關通知 - -#### Scenario: 下拉選單欄位 -- **GIVEN** 任務包含下拉選單類型的自定義欄位 -- **WHEN** 使用者選擇選項 -- **THEN** 系統儲存選擇的值 -- **AND** 選項列表由欄位定義提供 - -#### Scenario: 自定義欄位值顯示 -- **GIVEN** 任務有自定義欄位值 -- **WHEN** 使用者在列表或看板視角查看任務 -- **THEN** 自定義欄位值顯示在任務資訊中 -- **AND** 公式欄位顯示計算結果(唯讀) - -#### Scenario: 欄位數量限制 -- **GIVEN** 專案已有 20 個自定義欄位 -- **WHEN** 管理者嘗試新增第 21 個欄位 -- **THEN** 系統拒絕新增並顯示數量已達上限的訊息 diff --git a/openspec/changes/archive/2026-01-05-add-custom-fields/tasks.md b/openspec/changes/archive/2026-01-05-add-custom-fields/tasks.md deleted file mode 100644 index f65bcb5..0000000 --- a/openspec/changes/archive/2026-01-05-add-custom-fields/tasks.md +++ /dev/null @@ -1,82 +0,0 @@ -# Tasks: Add Custom Fields - -## Backend Tasks - -### 1. Create CustomField and TaskCustomValue models -- [x] Create `backend/app/models/custom_field.py` with CustomField model -- [x] Create `backend/app/models/task_custom_value.py` with TaskCustomValue model -- [x] Update `backend/app/models/__init__.py` to export new models -- [x] Create Alembic migration for new tables -- **驗證**: Migration 成功執行,tables 建立正確 - -### 2. Create Custom Fields API endpoints -- [x] Create `backend/app/api/custom_fields/router.py` -- [x] Implement `POST /api/projects/{project_id}/custom-fields` - 新增欄位 -- [x] Implement `GET /api/projects/{project_id}/custom-fields` - 列出欄位 -- [x] Implement `PUT /api/custom-fields/{field_id}` - 更新欄位 -- [x] Implement `DELETE /api/custom-fields/{field_id}` - 刪除欄位 -- [x] Add permission checks (only project owner/manager can manage fields) -- [x] Register router in main.py -- **驗證**: API endpoints 可正常呼叫,權限檢查正確 - -### 3. Implement formula calculation engine -- [x] Create `backend/app/services/formula_service.py` -- [x] Implement formula parsing (extract field references) -- [x] Implement formula validation (detect circular references) -- [x] Implement formula calculation -- [x] Add unit tests for formula service -- **驗證**: 公式計算正確,循環引用被拒絕 - -### 4. Integrate custom values with Tasks API -- [x] Modify `TaskCreate` schema to accept `custom_values` -- [x] Modify `TaskUpdate` schema to accept `custom_values` -- [x] Modify `TaskResponse` schema to include `custom_values` -- [x] Update `create_task` endpoint to save custom values -- [x] Update `update_task` endpoint to save custom values -- [x] Update `get_task` endpoint to return custom values -- [x] Trigger formula recalculation on value change -- **驗證**: 任務 CRUD 包含自定義欄位值 - -### 5. Add backend tests -- [x] Test custom field CRUD operations -- [x] Test permission checks -- [x] Test formula calculation -- [x] Test task integration with custom values -- **驗證**: 所有測試通過 (20/20 tests passed) - -## Frontend Tasks - -### 6. Create Custom Fields management UI -- [x] Create `frontend/src/pages/ProjectSettings.tsx` or extend existing -- [x] Create `frontend/src/components/CustomFieldEditor.tsx` - 欄位編輯表單 -- [x] Create `frontend/src/components/CustomFieldList.tsx` - 欄位列表 -- [x] Add custom fields API service in `frontend/src/services/customFields.ts` -- **驗證**: 可在專案設定中管理自定義欄位 - -### 7. Dynamic field rendering in Task forms -- [x] Create `frontend/src/components/CustomFieldInput.tsx` - 動態欄位輸入 -- [x] Integrate into TaskDetailModal.tsx -- [x] Integrate into task creation form -- [x] Handle different field types (text, number, dropdown, date, person) -- [x] Display formula fields as read-only -- **驗證**: 任務表單正確顯示和儲存自定義欄位 - -### 8. Display custom fields in task views -- [x] Add custom field columns to List view -- [x] Display custom field values in Kanban cards (optional, configurable) -- [x] Add column visibility toggle for custom fields -- **驗證**: 自定義欄位值在各視角中正確顯示 - -## Task Dependencies - -``` -[1] Models -> [2] API -> [4] Task Integration -> [5] Tests - \ - [3] Formula Engine / - -[6] Management UI -> [7] Task Forms -> [8] View Display -``` - -- Tasks 1-5 (Backend) 可平行於 Tasks 6-8 (Frontend) 開發 -- Task 2 完成後 Task 6 可開始(需要 API) -- Task 4 完成後 Task 7 可開始(需要 Task API 支援 custom values) diff --git a/openspec/changes/archive/2026-01-05-add-file-encryption/proposal.md b/openspec/changes/archive/2026-01-05-add-file-encryption/proposal.md deleted file mode 100644 index 5d5fd97..0000000 --- a/openspec/changes/archive/2026-01-05-add-file-encryption/proposal.md +++ /dev/null @@ -1,110 +0,0 @@ -# Proposal: Add File Encryption - -**Change ID:** `add-file-encryption` -**Issue Reference:** `FEAT-010` (issues.md) -**Status:** Draft -**Author:** Claude -**Date:** 2026-01-05 - -## Summary - -實作 AES-256 加密存儲功能,對機密等級專案的附件進行加密保護,並提供金鑰管理與輪換機制。 - -## Motivation - -半導體產業的設計文件、製程參數和良率報告屬於高度機密資料。目前系統的附件以明文形式儲存於 NAS,若發生以下情況可能導致資料外洩: -- NAS 遭未授權存取 -- 備份媒體遺失 -- 儲存設備報廢未妥善處理 - -透過 AES-256 加密,即使檔案被取得也無法解讀內容,提供額外的安全層。 - -## Scope - -### In Scope -- Backend: EncryptionKey model 和 key 管理 API -- Backend: 加密服務(encrypt/decrypt) -- Backend: 附件上傳時自動加密(依專案機密等級) -- Backend: 附件下載時自動解密 -- Backend: 金鑰輪換功能 -- 稽核日誌:加密相關操作記錄 - -### Out of Scope -- 客戶端加密(End-to-End Encryption) -- Hardware Security Module (HSM) 整合 -- 現有檔案的批次加密遷移(需另案處理) - -## Design Decisions - -### 加密策略 -| 專案機密等級 | 加密行為 | -|-------------|---------| -| public | 不加密 | -| department | 不加密 | -| confidential | 強制加密 | - -### 金鑰管理 -- 採用對稱金鑰加密(AES-256-GCM) -- 每個金鑰有唯一 ID,儲存於資料庫 -- 金鑰本身使用 Master Key 加密後存儲 -- Master Key 從環境變數讀取,不存於資料庫 - -### 加密流程 -``` -1. 上傳檔案 -2. 檢查專案機密等級 -3. 若為 confidential: - a. 取得當前有效金鑰 - b. 使用 AES-256-GCM 加密檔案 - c. 儲存加密後的檔案 - d. 記錄 encryption_key_id -4. 儲存 Attachment 記錄 -``` - -### 解密流程 -``` -1. 請求下載檔案 -2. 驗證存取權限 -3. 若 is_encrypted = true: - a. 取得 encryption_key_id 對應的金鑰 - b. 解密檔案 - c. 返回解密後的內容 -4. 若 is_encrypted = false: - a. 直接返回檔案 -``` - -### 金鑰輪換 -- 建立新金鑰並標記為 active -- 舊金鑰標記為 inactive(但保留用於解密舊檔案) -- 新上傳檔案使用新金鑰 -- 可選:批次重新加密舊檔案(背景任務) - -### API 設計 -``` -# 金鑰管理 (Admin only) -GET /api/admin/encryption-keys # 列出所有金鑰 -POST /api/admin/encryption-keys # 建立新金鑰 -POST /api/admin/encryption-keys/rotate # 金鑰輪換 -DELETE /api/admin/encryption-keys/{id} # 停用金鑰(不刪除) - -# 附件加密狀態由系統自動處理,無需額外 API -``` - -## Affected Specs - -| Spec | Change Type | -|------|-------------| -| document-management | MODIFIED (File Encryption requirement 實作細節) | - -## Risks & Mitigations - -| Risk | Mitigation | -|------|------------| -| Master Key 遺失導致資料無法解密 | 提供 Master Key 備份指引,建議異地保存 | -| 加密效能影響 | 使用串流加密避免大檔案記憶體問題 | -| 金鑰輪換中斷 | 使用資料庫交易確保原子性 | - -## Dependencies - -- Python cryptography 套件 -- Master Key 環境變數配置 diff --git a/openspec/changes/archive/2026-01-05-add-file-encryption/specs/document-management/spec.md b/openspec/changes/archive/2026-01-05-add-file-encryption/specs/document-management/spec.md deleted file mode 100644 index 0c36c2c..0000000 --- a/openspec/changes/archive/2026-01-05-add-file-encryption/specs/document-management/spec.md +++ /dev/null @@ -1,52 +0,0 @@ -## MODIFIED Requirements - -### Requirement: File Encryption -系統 SHALL 對半導體敏感圖檔進行 AES-256 加密存儲。 - -#### Scenario: 自動加密判斷 -- **GIVEN** 使用者上傳檔案至任務 -- **WHEN** 該任務所屬專案的 security_level 為 "confidential" -- **THEN** 系統自動使用 AES-256-GCM 加密檔案 -- **AND** 設定 is_encrypted = true 及 encryption_key_id - -#### Scenario: 加密存儲 -- **GIVEN** 專案設定為機密等級 -- **WHEN** 使用者上傳檔案 -- **THEN** 系統使用 AES-256 加密後存儲 -- **AND** 加密金鑰安全管理 - -#### Scenario: 解密讀取 -- **GIVEN** 使用者請求下載加密檔案 -- **WHEN** 系統驗證權限通過 -- **THEN** 系統解密檔案後提供下載 -- **AND** 解密過程透明,使用者無感 - -#### Scenario: 串流處理大檔案 -- **GIVEN** 使用者上傳或下載大型加密檔案 -- **WHEN** 系統處理加密或解密 -- **THEN** 使用串流方式處理避免記憶體溢出 -- **AND** 效能損耗在可接受範圍內 - -#### Scenario: 金鑰輪換 -- **GIVEN** 安全政策要求金鑰輪換 -- **WHEN** 管理員執行金鑰輪換 -- **THEN** 系統建立新金鑰並標記為 active -- **AND** 舊金鑰保留用於解密既有檔案 -- **AND** 新上傳檔案使用新金鑰加密 - -#### Scenario: Master Key 管理 -- **GIVEN** 系統需要加解密檔案 -- **WHEN** 系統取得加密金鑰 -- **THEN** 使用 Master Key 解密金鑰後使用 -- **AND** Master Key 從環境變數讀取,不存於資料庫 - -#### Scenario: 加密操作稽核 -- **GIVEN** 發生加密相關操作 -- **WHEN** 操作完成 -- **THEN** 系統記錄操作類型、金鑰 ID、檔案 ID、操作者、時間 -- **AND** 日誌不可竄改 - -#### Scenario: 金鑰管理權限 -- **GIVEN** 使用者嘗試管理加密金鑰 -- **WHEN** 使用者不是系統管理員 -- **THEN** 系統拒絕操作並返回 403 錯誤 diff --git a/openspec/changes/archive/2026-01-05-add-file-encryption/tasks.md b/openspec/changes/archive/2026-01-05-add-file-encryption/tasks.md deleted file mode 100644 index c66f47f..0000000 --- a/openspec/changes/archive/2026-01-05-add-file-encryption/tasks.md +++ /dev/null @@ -1,89 +0,0 @@ -# Tasks: Add File Encryption - -## Backend Tasks - -### 1. Create EncryptionKey model -- [x] Create `backend/app/models/encryption_key.py` -- [x] Update `backend/app/models/__init__.py` -- [x] Add `encryption_key_id` FK to Attachment model -- [x] Create Alembic migration -- **驗證**: Migration 成功執行 - -### 2. Implement encryption service -- [x] Create `backend/app/services/encryption_service.py` -- [x] Add `MASTER_KEY` to config.py (from env var) -- [x] Implement `generate_key()` - 產生新的 AES-256 金鑰 -- [x] Implement `encrypt_key()` - 使用 Master Key 加密金鑰 -- [x] Implement `decrypt_key()` - 使用 Master Key 解密金鑰 -- [x] Implement `encrypt_file()` - 串流式檔案加密 (AES-256-GCM) -- [x] Implement `decrypt_file()` - 串流式檔案解密 -- [x] Add unit tests for encryption service -- **驗證**: 加密解密測試通過 - -### 3. Create encryption key management API -- [x] Create `backend/app/api/admin/encryption_keys.py` -- [x] Implement `GET /api/admin/encryption-keys` - 列出金鑰(不含實際金鑰值) -- [x] Implement `POST /api/admin/encryption-keys` - 建立新金鑰 -- [x] Implement `POST /api/admin/encryption-keys/rotate` - 金鑰輪換 -- [x] Add system admin only permission check -- [x] Register router in main.py -- **驗證**: API 可正常呼叫 - -### 4. Integrate encryption with attachment upload -- [x] Modify `backend/app/api/attachments/router.py` upload endpoint -- [x] Check project security_level before upload -- [x] If confidential: encrypt file using encryption service -- [x] Set is_encrypted = True and encryption_key_id -- [x] Store encrypted file to NAS -- **驗證**: 機密專案上傳的檔案為加密狀態 - -### 5. Integrate decryption with attachment download -- [x] Modify `backend/app/api/attachments/router.py` download endpoint -- [x] Check is_encrypted flag -- [x] If encrypted: decrypt using encryption service before returning -- [x] Maintain streaming for large files -- **驗證**: 下載加密檔案可正確解密 - -### 6. Add encryption audit logging -- [x] Log encryption operations (encrypt, decrypt, key_create, key_rotate) -- [x] Include key_id, file_id, user_id, timestamp -- **驗證**: 稽核日誌正確記錄加密操作 - -### 7. Add backend tests -- [x] Test encryption service (encrypt/decrypt) -- [x] Test key management API -- [x] Test attachment upload with encryption -- [x] Test attachment download with decryption -- [x] Test key rotation -- **驗證**: 所有測試通過 - -## Configuration Tasks - -### 8. Environment configuration -- [x] Add `MASTER_KEY` to .env.example -- [x] Document key generation procedure -- [x] Document key backup recommendations -- **驗證**: 文件完整 - -## Task Dependencies - -``` -[1] EncryptionKey Model - ↓ -[2] Encryption Service - ↓ -[3] Key Management API ─────┐ - ↓ │ -[4] Upload Integration │ - ↓ │ -[5] Download Integration │ - ↓ │ -[6] Audit Logging │ - ↓ │ -[7] Tests ←─────────────────┘ - ↓ -[8] Configuration -``` - -- Tasks 1-7 為循序依賴 -- Task 8 可平行進行 diff --git a/openspec/changes/archive/2026-01-05-add-gantt-view/proposal.md b/openspec/changes/archive/2026-01-05-add-gantt-view/proposal.md deleted file mode 100644 index d698691..0000000 --- a/openspec/changes/archive/2026-01-05-add-gantt-view/proposal.md +++ /dev/null @@ -1,111 +0,0 @@ -# Proposal: Add Gantt View - -**Change ID:** `add-gantt-view` -**Issue Reference:** `FEAT-003` (issues.md) -**Status:** Draft -**Author:** Claude -**Date:** 2026-01-05 - -## Summary - -實作甘特圖視角,以時間軸方式呈現專案任務,顯示任務期間、依賴關係和里程碑。 - -## Motivation - -甘特圖是專案管理的核心視覺化工具,可幫助團隊: -- 了解任務時程與排程 -- 識別關鍵路徑 -- 發現資源衝突和瓶頸 -- 追蹤進度與延遲 - -目前系統僅有看板和列表視角,缺少時間軸視圖,無法直觀地規劃和追蹤專案進度。 - -## Scope - -### In Scope -- Backend: Task model 新增 start_date 欄位 -- Backend: TaskDependency model(前置任務關係) -- Backend: 依賴關係 CRUD API -- Frontend: Gantt chart 元件(使用第三方函式庫) -- Frontend: 任務時程編輯(拖拉調整日期) -- Frontend: 依賴關係視覺化(箭頭連線) - -### Out of Scope -- 關鍵路徑計算 -- 資源分配視圖 -- 基線對比功能 -- 匯出為圖片/PDF - -## Design Decisions - -### 任務依賴類型 -| Type | 說明 | 範例 | -|------|------|------| -| finish_to_start (FS) | 前置任務完成後才能開始 | 設計完成 → 開發開始 | -| start_to_start (SS) | 前置任務開始後才能開始 | 設計開始 → 文件開始 | -| finish_to_finish (FF) | 前置任務完成後才能完成 | 開發完成 → 測試完成 | -| start_to_finish (SF) | 前置任務開始後才能完成 | 較少使用 | - -**預設**: finish_to_start (FS) 為最常見類型 - -### Data Model 變更 -```sql --- Task 新增欄位 -ALTER TABLE pjctrl_tasks ADD COLUMN start_date DATETIME; - --- 新增依賴關係表 -CREATE TABLE pjctrl_task_dependencies ( - id UUID PRIMARY KEY, - predecessor_id UUID REFERENCES pjctrl_tasks(id), - successor_id UUID REFERENCES pjctrl_tasks(id), - dependency_type ENUM('FS', 'SS', 'FF', 'SF') DEFAULT 'FS', - lag_days INT DEFAULT 0, - created_at TIMESTAMP -); -``` - -### 前端函式庫選擇 -建議使用 **DHTMLX Gantt** 或 **Frappe Gantt**: - -| 函式庫 | 優點 | 缺點 | -|--------|------|------| -| DHTMLX Gantt | 功能完整、效能好 | 商業授權 | -| Frappe Gantt | 開源、輕量 | 功能較少 | -| React-Gantt-Chart | React 原生 | 社群較小 | - -**建議**: 使用 Frappe Gantt(MIT 授權),足夠基本需求 - -### API 設計 -``` -# 依賴關係 API -POST /api/tasks/{task_id}/dependencies # 新增依賴 -GET /api/tasks/{task_id}/dependencies # 取得依賴 -DELETE /api/task-dependencies/{dependency_id} # 刪除依賴 - -# 任務日期更新(使用現有 API) -PUT /api/tasks/{task_id} # body 包含 start_date, due_date -``` - -### 日期驗證規則 -- start_date 不可晚於 due_date -- 有依賴關係時,根據 dependency_type 驗證日期合理性 -- 循環依賴檢測 - -## Affected Specs - -| Spec | Change Type | -|------|-------------| -| task-management | MODIFIED (Multiple Views requirement 甘特圖細節) | - -## Risks & Mitigations - -| Risk | Mitigation | -|------|------------| -| 複雜依賴關係影響效能 | 限制單任務最多 10 個直接依賴 | -| 循環依賴 | 新增/修改依賴時進行循環檢測 | -| 大型專案載入慢 | 分頁載入或虛擬滾動 | - -## Dependencies - -- Frappe Gantt(或其他甘特圖函式庫) -- 需要 Task model 已有 due_date 欄位(✓ 已存在) diff --git a/openspec/changes/archive/2026-01-05-add-gantt-view/specs/task-management/spec.md b/openspec/changes/archive/2026-01-05-add-gantt-view/specs/task-management/spec.md deleted file mode 100644 index effc90c..0000000 --- a/openspec/changes/archive/2026-01-05-add-gantt-view/specs/task-management/spec.md +++ /dev/null @@ -1,67 +0,0 @@ -## MODIFIED Requirements - -### Requirement: Multiple Views -系統 SHALL 支援多維視角:看板 (Kanban)、甘特圖 (Gantt)、列表 (List)、行事曆 (Calendar)。 - -#### Scenario: 甘特圖視角 -- **GIVEN** 使用者選擇甘特圖視角 -- **WHEN** 系統載入專案任務 -- **THEN** 任務依時間軸顯示為水平條狀 -- **AND** 顯示任務相依關係與里程碑 - -#### Scenario: 甘特圖時間軸縮放 -- **GIVEN** 使用者正在查看甘特圖 -- **WHEN** 使用者切換縮放層級(日、週、月) -- **THEN** 時間軸相應調整顯示密度 -- **AND** 任務條保持正確的相對位置 - -#### Scenario: 拖拉調整任務日期 -- **GIVEN** 使用者正在查看甘特圖 -- **WHEN** 使用者拖拉任務條改變位置或長度 -- **THEN** 系統更新任務的 start_date 和 due_date -- **AND** 驗證日期合理性(start_date <= due_date) - -#### Scenario: 顯示任務依賴關係 -- **GIVEN** 任務之間存在依賴關係 -- **WHEN** 使用者查看甘特圖 -- **THEN** 系統顯示連接任務的箭頭 -- **AND** 箭頭方向表示依賴方向(前置任務 → 後續任務) - -#### Scenario: 新增任務依賴 -- **GIVEN** 使用者在甘特圖上選擇兩個任務 -- **WHEN** 使用者建立依賴關係 -- **THEN** 系統儲存依賴關係 -- **AND** 顯示連接箭頭 - -#### Scenario: 刪除任務依賴 -- **GIVEN** 任務之間存在依賴關係 -- **WHEN** 使用者刪除該依賴 -- **THEN** 系統移除依賴記錄 -- **AND** 連接箭頭消失 - -#### Scenario: 循環依賴檢測 -- **GIVEN** 使用者嘗試建立依賴關係 -- **WHEN** 該依賴會形成循環(A → B → C → A) -- **THEN** 系統拒絕建立並顯示錯誤訊息 -- **AND** 現有依賴關係保持不變 - -#### Scenario: 依賴類型支援 -- **GIVEN** 使用者建立任務依賴 -- **WHEN** 使用者選擇依賴類型 -- **THEN** 系統支援以下類型: - - Finish-to-Start (FS): 前置完成後開始 - - Start-to-Start (SS): 前置開始後開始 - - Finish-to-Finish (FF): 前置完成後完成 - - Start-to-Finish (SF): 前置開始後完成 - -## ADDED Data Model - -``` -pjctrl_task_dependencies -├── id: UUID (PK) -├── predecessor_id: UUID (FK -> tasks) -├── successor_id: UUID (FK -> tasks) -├── dependency_type: ENUM('FS', 'SS', 'FF', 'SF') DEFAULT 'FS' -├── lag_days: INT DEFAULT 0 -└── created_at: TIMESTAMP -``` diff --git a/openspec/changes/archive/2026-01-05-add-gantt-view/tasks.md b/openspec/changes/archive/2026-01-05-add-gantt-view/tasks.md deleted file mode 100644 index d200bf5..0000000 --- a/openspec/changes/archive/2026-01-05-add-gantt-view/tasks.md +++ /dev/null @@ -1,92 +0,0 @@ -# Tasks: Add Gantt View - -## Backend Tasks - -### 1. Add start_date to Task model -- [x] Add `start_date` column to Task model -- [x] Create Alembic migration -- [x] Update TaskCreate/TaskUpdate schemas to include start_date -- [x] Update TaskResponse schema -- **驗證**: Migration 成功,API 可設定 start_date - -### 2. Create TaskDependency model -- [x] Create `backend/app/models/task_dependency.py` -- [x] Define predecessor_id, successor_id, dependency_type, lag_days -- [x] Update `backend/app/models/__init__.py` -- [x] Create Alembic migration -- **驗證**: Migration 成功 - -### 3. Implement dependency CRUD API -- [x] Create `backend/app/api/task_dependencies/router.py` -- [x] Implement `POST /api/tasks/{task_id}/dependencies` - 新增依賴 -- [x] Implement `GET /api/tasks/{task_id}/dependencies` - 取得依賴 -- [x] Implement `DELETE /api/task-dependencies/{dependency_id}` - 刪除依賴 -- [x] Add circular dependency detection -- [x] Register router in main.py -- **驗證**: 依賴關係 CRUD 可正常操作,循環依賴被拒絕 - -### 4. Add date validation -- [x] Validate start_date <= due_date -- [x] Validate dependency constraints on date change -- [x] Add validation tests -- **驗證**: 不合理日期被拒絕 - -### 5. Add backend tests -- [x] Test TaskDependency CRUD -- [x] Test circular dependency detection -- [x] Test date validation -- **驗證**: 所有測試通過 - -## Frontend Tasks - -### 6. Install and configure Gantt library -- [x] Install Frappe Gantt (or chosen library) -- [x] Create wrapper component for React integration -- [x] Configure styling to match application theme -- **驗證**: Gantt 元件可正常渲染 - -### 7. Create GanttChart component -- [x] Create `frontend/src/components/GanttChart.tsx` -- [x] Load tasks with start_date and due_date -- [x] Display tasks as horizontal bars on timeline -- [x] Show task title, assignee, progress -- [x] Support zoom levels (day, week, month) -- **驗證**: 任務正確顯示在時間軸上 - -### 8. Implement drag-to-edit dates -- [x] Handle bar drag to move task dates -- [x] Handle bar resize to change duration -- [x] Call API to update task dates -- [x] Show optimistic update, rollback on error -- **驗證**: 拖拉調整日期可正確更新 - -### 9. Implement dependency visualization -- [x] Add dependency arrows between tasks -- [x] Create dependency API service -- [x] Implement add/remove dependency UI (right-click or toolbar) -- [x] Validate and show error for circular dependencies -- **驗證**: 依賴關係正確顯示和編輯 - -### 10. Integrate Gantt view into Tasks page -- [x] Add "Gantt" option to view toggle -- [x] Store view preference in localStorage -- [x] Handle view switching -- **驗證**: 可在 List/Kanban/Gantt 之間切換 - -## Task Dependencies - -``` -[1] start_date 欄位 - ↓ -[2] TaskDependency Model → [3] Dependency API → [4] Date Validation → [5] Tests - ↓ -[6] Gantt Library Setup → [7] GanttChart Component - ↓ - [8] Drag Edit → [9] Dependency UI - ↓ - [10] View Integration -``` - -- Tasks 1-5 (Backend) 可平行於 Tasks 6-10 (Frontend) 開發 -- Task 7 需要 Task 1 完成(需要 start_date 欄位) -- Task 9 需要 Task 3 完成(需要依賴 API) diff --git a/openspec/changes/archive/2026-01-05-add-kanban-realtime-sync/proposal.md b/openspec/changes/archive/2026-01-05-add-kanban-realtime-sync/proposal.md deleted file mode 100644 index 26fe06a..0000000 --- a/openspec/changes/archive/2026-01-05-add-kanban-realtime-sync/proposal.md +++ /dev/null @@ -1,87 +0,0 @@ -# Proposal: Add Kanban Real-time Sync - -## Change ID -add-kanban-realtime-sync - -## Status -PROPOSED - -## Summary -實作看板 (Kanban) 多人即時同步功能,讓多位使用者同時查看同一專案看板時能即時看到任務狀態變更。 - -## Problem Statement -目前系統的 WebSocket 僅用於通知推播 (`/ws/notifications`),看板頁面沒有即時同步機制: -- 當使用者 A 拖曳任務改變狀態時,使用者 B 需要手動重新整理才能看到更新 -- 這導致協作效率降低,可能造成衝突或重複操作 -- 不符合 `project.md` 中「Real-time Sync: WebSocket for live collaboration」的要求 - -## Proposed Solution -擴展現有 WebSocket 基礎架構,新增專案房間訂閱 (Project Room Subscription) 機制: - -### 核心功能 -1. **專案房間訂閱**:使用者進入專案看板時自動訂閱該專案的即時更新 -2. **任務變更廣播**:任務狀態變更時,透過 Redis Pub/Sub 廣播給同一專案的所有訂閱者 -3. **增量更新**:僅推送變更的任務資料,而非整個看板重載 - -### 技術架構 -``` -Frontend (KanbanBoard) - │ - ▼ WebSocket: /ws/projects/{project_id} -Backend (WebSocket Router) - │ - ▼ Redis Pub/Sub: channel:project:{project_id}:tasks -Backend (Tasks API) -``` - -### 訊息類型 -- `task_created`: 新任務建立 -- `task_updated`: 任務更新(含欄位變更) -- `task_status_changed`: 任務狀態變更(拖曳看板) -- `task_deleted`: 任務刪除 -- `task_assigned`: 任務指派變更 - -## Scope - -### In Scope -- 專案級 WebSocket 連線端點 -- 任務 CRUD 事件廣播 -- 前端看板即時更新 -- 樂觀更新 (Optimistic Update) 搭配衝突回滾 - -### Out of Scope -- 任務欄位鎖定 (Field Locking) - 未來可擴展 -- 離線編輯同步 - 未來可擴展 -- 多專案同時訂閱 - 目前一次訂閱一個專案 - -## Impact Analysis - -### Affected Components -- `backend/app/api/websocket/router.py` - 新增專案 WebSocket 端點 -- `backend/app/services/websocket_manager.py` - 新增專案房間管理 -- `backend/app/api/tasks/router.py` - 任務變更時發送事件 -- `frontend/src/components/KanbanBoard.tsx` - 接收即時更新 -- `frontend/src/contexts/NotificationContext.tsx` - 可能需要擴展或新增專案同步 Context - -### Performance Considerations -- Redis Pub/Sub 已用於通知系統,可直接復用 -- 每專案一個 channel,避免全域廣播 -- 增量更新減少資料傳輸量 - -### Security Considerations -- WebSocket 連線需驗證 JWT Token -- 確認使用者有權限存取該專案 -- 防止未授權的專案訂閱 - -## Dependencies -- 現有 WebSocket 基礎架構 (collaboration spec) -- Redis Pub/Sub (已實作) -- JWT Token 驗證 (user-auth spec) -- 專案權限檢查 (resource-management spec) - -## Related Specs -- `collaboration` - 擴展 Real-time Notifications requirement -- `task-management` - 任務 CRUD 操作 - -## Author -Claude Code diff --git a/openspec/changes/archive/2026-01-05-add-kanban-realtime-sync/specs/collaboration/spec.md b/openspec/changes/archive/2026-01-05-add-kanban-realtime-sync/specs/collaboration/spec.md deleted file mode 100644 index 59b619a..0000000 --- a/openspec/changes/archive/2026-01-05-add-kanban-realtime-sync/specs/collaboration/spec.md +++ /dev/null @@ -1,112 +0,0 @@ -# Collaboration - Spec Delta - -## ADDED Requirements - -### Requirement: Project Real-time Sync -系統 SHALL 提供專案級即時同步機制,讓多位使用者同時協作時能即時看到任務變更。 - -#### Scenario: 訂閱專案即時更新 -- **GIVEN** 使用者擁有專案的存取權限 -- **WHEN** 使用者進入專案看板頁面 -- **THEN** 系統自動建立 WebSocket 連線並訂閱該專案的即時更新 -- **AND** 連線使用 JWT Token 進行身份驗證 - -#### Scenario: 接收任務狀態變更 -- **GIVEN** 使用者已訂閱專案即時更新 -- **WHEN** 其他使用者在同一專案中變更任務狀態 -- **THEN** 系統透過 WebSocket 即時推送 `task_status_changed` 事件 -- **AND** 看板 UI 自動更新任務位置 - -#### Scenario: 接收任務建立事件 -- **GIVEN** 使用者已訂閱專案即時更新 -- **WHEN** 其他使用者在同一專案中建立新任務 -- **THEN** 系統透過 WebSocket 即時推送 `task_created` 事件 -- **AND** 看板 UI 自動顯示新任務 - -#### Scenario: 接收任務刪除事件 -- **GIVEN** 使用者已訂閱專案即時更新 -- **WHEN** 其他使用者在同一專案中刪除任務 -- **THEN** 系統透過 WebSocket 即時推送 `task_deleted` 事件 -- **AND** 看板 UI 自動移除該任務 - -#### Scenario: 取消訂閱專案 -- **GIVEN** 使用者已訂閱專案即時更新 -- **WHEN** 使用者離開專案看板頁面 -- **THEN** 系統自動關閉 WebSocket 連線並取消訂閱 -- **AND** 釋放相關資源 - -#### Scenario: 樂觀更新與衝突處理 -- **GIVEN** 使用者拖曳任務改變狀態 -- **WHEN** 本地先套用變更(樂觀更新)但 API 呼叫失敗 -- **THEN** 系統回滾任務到原本位置 -- **AND** 顯示錯誤訊息通知使用者 - -#### Scenario: 避免重複應用自己的事件 -- **GIVEN** 使用者變更任務狀態並收到自己發起的事件 -- **WHEN** WebSocket 收到該事件 -- **THEN** 系統識別為本地發起的變更 -- **AND** 不重複應用該變更 - -## MODIFIED Requirements - -### Requirement: Real-time Notifications -系統 SHALL 透過 Redis 推播即時通知。 - -#### Scenario: 即時通知推播 -- **GIVEN** 發生需要通知的事件(如:被指派任務、被 @提及、阻礙標記) -- **WHEN** 事件發生 -- **THEN** 系統透過 WebSocket 即時推播通知給相關使用者 -- **AND** 未讀通知顯示數量標示 - -#### Scenario: 通知已讀標記 -- **GIVEN** 使用者有未讀通知 -- **WHEN** 使用者查看通知 -- **THEN** 系統標記為已讀 -- **AND** 更新未讀數量 - -#### Scenario: 專案事件頻道分離 -- **GIVEN** 系統運作中 -- **WHEN** 發生任務變更事件 -- **THEN** 通知系統使用 `notifications:{user_id}` 頻道推送個人通知 -- **AND** 即時同步系統使用 `project:{project_id}:tasks` 頻道推送專案事件 -- **AND** 兩者互不干擾 - -## Technical Notes - -### WebSocket 端點 -- 通知端點: `GET /ws/notifications?token=` -- 專案同步端點: `GET /ws/projects/{project_id}?token=` - -### Redis Pub/Sub Channels -- 通知頻道: `notifications:{user_id}` (現有實作) -- 專案任務事件頻道: `project:{project_id}:tasks` (新增) - -### 事件訊息格式 -```json -{ - "type": "task_created | task_updated | task_status_changed | task_deleted | task_assigned", - "event_id": "uuid", - "data": { - "task_id": "uuid", - "project_id": "uuid", - "title": "string", - "status_id": "uuid | null", - "status_name": "string | null", - "status_color": "string | null", - "assignee_id": "uuid | null", - "time_estimate": "number | null", - /* 其他任務欄位 */ - }, - "triggered_by": "user_id", - "timestamp": "ISO8601" -} -``` - -**欄位說明**: -- `event_id`: 唯一事件識別碼,用於多分頁/多裝置事件去重 -- `triggered_by`: 觸發事件的使用者 ID,位於頂層便於前端過濾 - -### 連線管理 -- 使用與通知系統相同的心跳機制 (PING/PONG) -- 支援同一使用者多個連線 (多分頁/裝置) -- 連線中斷自動重連 (Frontend 實作) diff --git a/openspec/changes/archive/2026-01-05-add-kanban-realtime-sync/tasks.md b/openspec/changes/archive/2026-01-05-add-kanban-realtime-sync/tasks.md deleted file mode 100644 index d024122..0000000 --- a/openspec/changes/archive/2026-01-05-add-kanban-realtime-sync/tasks.md +++ /dev/null @@ -1,106 +0,0 @@ -# Tasks: Add Kanban Real-time Sync - -## Phase 1: Backend Infrastructure - -### 1.1 擴展 WebSocket Manager -- [x] 在 `websocket_manager.py` 新增專案房間管理 - - 新增 `project_connections: Dict[str, Set[WebSocket]]` - - 實作 `join_project(websocket, project_id)` 方法 - - 實作 `leave_project(websocket, project_id)` 方法 - - 實作 `broadcast_to_project(project_id, message)` 方法 -- **驗證**: ✅ 單元測試確認房間管理邏輯 - -### 1.2 新增專案 WebSocket 端點 -- [x] 在 `websocket/router.py` 新增 `/ws/projects/{project_id}` 端點 - - JWT Token 驗證 (復用現有 `get_user_from_token`) - - 專案存取權限驗證 - - 心跳機制 (復用現有 PING/PONG 邏輯) - - Redis Pub/Sub 訂閱 channel `project:{project_id}:tasks` -- **驗證**: ✅ 整合測試確認連線建立與權限檢查 - -### 1.3 新增 Redis Pub/Sub Channel -- [x] 在 `redis_pubsub.py` 新增 `ProjectTaskSubscriber` 類別 - - 訂閱 `project:{project_id}:tasks` channel - - 轉發訊息到 WebSocket -- [x] 新增 `publish_task_event(project_id, event_type, task_data)` 函數 - - 含 `event_id` 用於多分頁事件去重 - - 含 Redis 連線重試機制 -- **驗證**: ✅ 單元測試確認訊息發送與接收 - -## Phase 2: Backend Integration - -### 2.1 任務 CRUD 事件發送 -- [x] 修改 `tasks/router.py` 的以下端點: - - `create_task`: 發送 `task_created` 事件 - - `update_task`: 發送 `task_updated` 事件 - - `update_task_status`: 發送 `task_status_changed` 事件 - - `delete_task`: 發送 `task_deleted` 事件 - - `assign_task`: 發送 `task_assigned` 事件 -- **驗證**: ✅ API 測試確認事件在 CRUD 後正確發送 (8/8 tests passed) - -### 2.2 批次操作支援 -- [ ] 確保批次狀態更新也發送事件 (延後實作) -- [ ] 考慮事件合併/節流以避免大量更新時的效能問題 (延後實作) -- **驗證**: 壓力測試確認批次操作不影響系統效能 - -## Phase 3: Frontend Integration - -### 3.1 新增專案同步 Context -- [x] 建立 `ProjectSyncContext.tsx` - - 管理專案 WebSocket 連線 - - 提供訂閱/取消訂閱方法 - - 處理重連邏輯 (指數退避 + 最大重試次數) - - 使用 `event_id` 進行多分頁事件去重 -- **驗證**: ✅ 開發者工具確認連線狀態 - -### 3.2 更新 KanbanBoard 組件 -- [x] 整合 `ProjectSyncContext` - - 進入頁面時訂閱專案 - - 離開頁面時取消訂閱 -- [x] 處理即時事件: - - `task_created`: 新增任務到對應欄位 - - `task_updated`: 更新任務資料 - - `task_status_changed`: 移動任務到新欄位 (含 status_color) - - `task_deleted`: 從看板移除任務 -- [x] 實作樂觀更新 (Optimistic Update) - - 拖曳時立即更新 UI - - API 失敗時回滾 - - 使用 `event_id` 避免重複應用事件 -- **驗證**: ✅ 手動測試多瀏覽器同步 - -### 3.3 視覺回饋 -- [x] 新增連線狀態指示器 (Live / Offline) -- [ ] 新增任務更新時的動畫效果 (延後實作) -- [ ] 顯示「其他使用者正在編輯」提示 (延後實作) -- **驗證**: ✅ UI 審查確認視覺效果 - -## Phase 4: Testing & Documentation - -### 4.1 後端測試 -- [x] WebSocket 連線測試 -- [x] 專案權限驗證測試 -- [x] Redis Pub/Sub 整合測試 -- [ ] 併發更新測試 (延後實作) -- **驗證**: ✅ 8/8 測試通過 - -### 4.2 前端測試 -- [x] Context 單元測試 (Build 通過) -- [ ] KanbanBoard 整合測試 (延後實作) -- [ ] 多使用者同步 E2E 測試 (延後實作) -- **驗證**: ✅ Build 成功 - -### 4.3 效能驗證 -- [ ] 壓力測試:100+ 併發使用者 (延後實作) -- [ ] 延遲測試:事件傳播 < 500ms (延後實作) -- **驗證**: 效能指標符合預期 - -## 完成狀態 - -| Phase | 完成度 | 備註 | -|-------|--------|------| -| Phase 1 | 100% | 基礎架構完成 | -| Phase 2 | 80% | 批次操作延後 | -| Phase 3 | 90% | 動畫效果延後 | -| Phase 4 | 60% | E2E/壓力測試延後 | - -**整體完成度**: 核心功能 100%,進階功能延後 diff --git a/openspec/changes/archive/2026-01-07-add-dashboard-widgets/design.md b/openspec/changes/archive/2026-01-07-add-dashboard-widgets/design.md deleted file mode 100644 index cc8c539..0000000 --- a/openspec/changes/archive/2026-01-07-add-dashboard-widgets/design.md +++ /dev/null @@ -1,101 +0,0 @@ -# Design: Dashboard Widgets - -## Architecture Overview - -``` -┌─────────────────────────────────────────────────────────────┐ -│ Dashboard Page │ -├─────────────────────────────────────────────────────────────┤ -│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌────────┐ │ -│ │ My Tasks │ │ Due This │ │ Overdue │ │ Done % │ │ -│ │ 12 │ │ Week: 5 │ │ 2 │ │ 78% │ │ -│ └─────────────┘ └─────────────┘ └─────────────┘ └────────┘ │ -├─────────────────────────────────────────────────────────────┤ -│ ┌──────────────────────────┐ ┌───────────────────────────┐ │ -│ │ My Workload │ │ Project Health │ │ -│ │ ┌────────────────────┐ │ │ ┌───────────────────────┐ │ │ -│ │ │ 32h / 40h (80%) │ │ │ │ 8 Healthy | 2 At Risk │ │ │ -│ │ │ ████████░░ │ │ │ │ Avg Score: 76% │ │ │ -│ │ └────────────────────┘ │ │ └───────────────────────┘ │ │ -│ └──────────────────────────┘ └───────────────────────────┘ │ -├─────────────────────────────────────────────────────────────┤ -│ Quick Actions: [Spaces] [Workload] [Health] [Audit*] │ -└─────────────────────────────────────────────────────────────┘ -``` - -## API Design - -### GET /api/dashboard - -Single endpoint aggregates all dashboard data to minimize frontend requests. - -```typescript -interface DashboardResponse { - task_stats: { - assigned_count: number // Total tasks assigned to user - due_this_week: number // Tasks with due_date in current week - overdue_count: number // Tasks past due_date, not completed - completion_rate: number // Percentage (0-100) - } - workload: { - allocated_hours: number - capacity_hours: number - load_percentage: number - load_level: 'normal' | 'warning' | 'overloaded' - } - health_summary: { - total_projects: number - healthy_count: number - at_risk_count: number - critical_count: number - average_health_score: number - } -} -``` - -## Data Sources - -| Widget | Source | Notes | -|--------|--------|-------| -| Task Statistics | `pjctrl_tasks` table | Filter by assignee_id = current user | -| Workload | Existing `WorkloadService` | Reuse get_user_workload_detail() | -| Health Summary | Existing `HealthService` | Reuse get_dashboard() summary | - -## Performance Considerations - -1. **Single API Call**: Frontend makes one request instead of three -2. **Query Optimization**: Task stats use COUNT queries, not full rows -3. **Caching**: Health summary already cached in Redis (5 min TTL) -4. **No N+1**: Avoid loading related entities for counts - -## Component Structure - -``` -frontend/src/ -├── pages/ -│ └── Dashboard.tsx # Main page (modified) -├── components/ -│ ├── dashboard/ -│ │ ├── StatisticsCard.tsx # Single stat card -│ │ ├── WorkloadWidget.tsx # Workload progress bar -│ │ ├── HealthWidget.tsx # Health summary -│ │ └── QuickActions.tsx # Navigation links -└── services/ - └── dashboard.ts # API service (new) -``` - -## Trade-offs - -### Decision: Single vs Multiple API Endpoints - -**Chosen**: Single `/api/dashboard` endpoint - -**Rationale**: -- Reduces network round-trips (1 vs 3) -- Simpler frontend loading state management -- Data is always consistent (same timestamp) - -**Rejected Alternative**: Use existing endpoints directly -- Would require 3 parallel requests -- More complex error handling -- Harder to ensure data consistency diff --git a/openspec/changes/archive/2026-01-07-add-dashboard-widgets/proposal.md b/openspec/changes/archive/2026-01-07-add-dashboard-widgets/proposal.md deleted file mode 100644 index a74afaa..0000000 --- a/openspec/changes/archive/2026-01-07-add-dashboard-widgets/proposal.md +++ /dev/null @@ -1,38 +0,0 @@ -# Proposal: add-dashboard-widgets - -## Summary - -Replace the placeholder Dashboard page with functional dashboard widgets that provide users with an at-a-glance overview of their work and system status. - -## Motivation - -The current Dashboard displays only a welcome message and placeholder text ("Features will be added as development progresses"). Users need a centralized view of: -- Their assigned tasks and workload -- Project health across the organization -- Quick access to common actions -- System statistics - -## Scope - -### In Scope -- Dashboard statistics cards (my tasks, overdue, completion rate) -- My workload summary widget (reuses existing `/workload/me` API) -- Project health summary widget (reuses existing `/projects/health/dashboard` API) -- Quick actions section with navigation links -- Backend API endpoint for aggregated dashboard data - -### Out of Scope -- Customizable widget layout (future enhancement) -- Real-time WebSocket updates for dashboard (can be added later) -- Dashboard for specific roles (manager vs engineer views) - -## Dependencies - -Reuses existing APIs: -- `GET /workload/me` - User workload summary -- `GET /projects/health/dashboard` - Project health overview -- `GET /api/projects/{project_id}/tasks` with `assignee_id` filter - -## Risk Assessment - -**Low Risk** - This change primarily adds a new frontend page using existing backend APIs. A new lightweight aggregation endpoint is added but doesn't modify existing data structures. diff --git a/openspec/changes/archive/2026-01-07-add-dashboard-widgets/specs/dashboard/spec.md b/openspec/changes/archive/2026-01-07-add-dashboard-widgets/specs/dashboard/spec.md deleted file mode 100644 index dc37010..0000000 --- a/openspec/changes/archive/2026-01-07-add-dashboard-widgets/specs/dashboard/spec.md +++ /dev/null @@ -1,69 +0,0 @@ -# Dashboard Capability - -Provides users with an at-a-glance overview of their work, project health, and system statistics. - -## ADDED Requirements - -### Requirement: Dashboard Statistics - -The system SHALL display aggregated statistics showing the user's task overview. - -#### Scenario: User views task statistics -- Given: User is authenticated -- When: User navigates to Dashboard -- Then: System displays: - - Total tasks assigned to user - - Tasks due this week - - Overdue tasks count - - Completion rate percentage - -### Requirement: My Workload Widget - -The system SHALL display the current user's workload summary for the current week. - -#### Scenario: User views personal workload -- Given: User is authenticated -- When: User views Dashboard -- Then: System displays: - - Allocated hours vs capacity hours - - Load percentage with visual indicator - - Load level status (normal/warning/overloaded) - -### Requirement: Project Health Summary - -The system SHALL display an aggregated project health summary. - -#### Scenario: User views project health overview -- Given: User is authenticated -- When: User views Dashboard -- Then: System displays: - - Total projects count - - Healthy/At-Risk/Critical breakdown - - Average health score - - Projects with blockers count - -### Requirement: Quick Actions - -The system SHALL provide quick navigation links to common actions. - -#### Scenario: User accesses quick actions -- Given: User is authenticated -- When: User views Dashboard -- Then: System displays navigation links to: - - Spaces page - - Workload page - - Project Health page - - (Admin only) Audit page - -### Requirement: Dashboard API Endpoint - -The backend SHALL provide a single aggregated endpoint for dashboard data. - -#### Scenario: Frontend fetches dashboard data -- Given: User is authenticated -- When: Frontend requests GET /api/dashboard -- Then: Backend returns: - - User task statistics - - Current week workload summary - - Project health summary -- And: Response is optimized with single database query where possible diff --git a/openspec/changes/archive/2026-01-07-add-dashboard-widgets/tasks.md b/openspec/changes/archive/2026-01-07-add-dashboard-widgets/tasks.md deleted file mode 100644 index a208fa2..0000000 --- a/openspec/changes/archive/2026-01-07-add-dashboard-widgets/tasks.md +++ /dev/null @@ -1,59 +0,0 @@ -# Implementation Tasks - -## Phase 1: Backend API - -### Task 1.1: Create Dashboard Schema -- [ ] Create `backend/app/schemas/dashboard.py` with response models - - `TaskStatistics`: assigned_count, due_this_week, overdue_count, completion_rate - - `DashboardResponse`: task_stats, workload_summary, health_summary -- **Validation**: Schema imports without errors - -### Task 1.2: Create Dashboard Router -- [ ] Create `backend/app/api/dashboard/router.py` -- [ ] Implement `GET /api/dashboard` endpoint - - Query tasks assigned to current user - - Reuse workload service for current week summary - - Reuse health service for project summary -- [ ] Register router in `backend/app/main.py` -- **Validation**: `curl -H "Authorization: Bearer $TOKEN" http://localhost:8000/api/dashboard` returns valid JSON - -## Phase 2: Frontend Implementation - -### Task 2.1: Create Dashboard Service -- [ ] Create `frontend/src/services/dashboard.ts` -- [ ] Define TypeScript interfaces matching backend schema -- [ ] Implement `getDashboard()` API function -- **Validation**: TypeScript compiles without errors - -### Task 2.2: Implement Dashboard Components -- [ ] Create `StatisticsCard` component for task stats -- [ ] Create `WorkloadWidget` component (reuse styles from WorkloadPage) -- [ ] Create `HealthSummaryWidget` component -- [ ] Create `QuickActions` component with navigation links -- **Validation**: Components render without console errors - -### Task 2.3: Integrate Dashboard Page -- [ ] Replace placeholder content in `Dashboard.tsx` -- [ ] Add loading state with Skeleton components -- [ ] Add error handling with retry -- [ ] Style components to match existing UI -- **Validation**: Dashboard displays data correctly after login - -## Phase 3: Testing & Polish - -### Task 3.1: Add Backend Tests -- [ ] Create `backend/tests/test_dashboard.py` -- [ ] Test endpoint returns correct structure -- [ ] Test data aggregation logic -- **Validation**: `pytest tests/test_dashboard.py -v` passes - -### Task 3.2: Visual Polish -- [ ] Ensure responsive layout for mobile -- [ ] Match color scheme with existing pages -- [ ] Add subtle animations for loading states -- **Validation**: Manual visual review on different screen sizes - -## Dependencies - -- Tasks 2.x depend on Task 1.2 completion -- Task 3.1 can run in parallel with Phase 2 diff --git a/openspec/changes/archive/2026-01-09-add-delete-capability/proposal.md b/openspec/changes/archive/2026-01-09-add-delete-capability/proposal.md deleted file mode 100644 index 702d3e4..0000000 --- a/openspec/changes/archive/2026-01-09-add-delete-capability/proposal.md +++ /dev/null @@ -1,66 +0,0 @@ -# Proposal: Add Delete Capability for Spaces and Projects - -## Summary -Enable users to delete workspaces (spaces) and projects from the frontend UI. The backend already supports soft deletion, but the frontend lacks the necessary UI components and service functions. - -## Problem Statement -Currently, users cannot delete spaces or projects from the UI even though: -1. Backend DELETE endpoints exist (`DELETE /spaces/{id}`, `DELETE /projects/{id}`) -2. Both implement soft-delete (setting `is_active = False`) -3. Project deletion includes audit logging - -Users must access the database directly or use API tools to delete items, which is not user-friendly. - -## Proposed Solution -Add frontend UI components and service functions to enable deletion: - -1. **Frontend Services**: Add `deleteSpace()` and `deleteProject()` functions -2. **Spaces Page**: Add delete button with confirmation dialog -3. **Projects Page**: Add delete button with confirmation dialog -4. **Translations**: Add i18n strings for delete UI elements - -## Scope - -### In Scope -- Delete button UI for spaces (owner only) -- Delete button UI for projects (owner only) -- Confirmation dialogs with clear warning messages -- i18n translations (zh-TW, en) -- Audit trail (already implemented in backend for projects) - -### Out of Scope -- Hard delete (permanent removal from database) -- Restore/undelete functionality -- Cascading delete behavior changes (current soft-delete preserves child items) -- Bulk delete operations - -## Impact Analysis - -### Affected Components -| Component | Change Type | Description | -|-----------|-------------|-------------| -| `frontend/src/services/spaces.ts` | NEW | Add deleteSpace function | -| `frontend/src/services/projects.ts` | MODIFY | Add deleteProject function | -| `frontend/src/pages/Spaces.tsx` | MODIFY | Add delete button and dialog | -| `frontend/src/pages/Projects.tsx` | MODIFY | Add delete button and dialog | -| `frontend/public/locales/*/spaces.json` | MODIFY | Add delete translations | -| `frontend/public/locales/*/projects.json` | MODIFY | Add delete translations | - -### Dependencies -- Backend DELETE endpoints (already implemented) -- Audit service (already integrated for project deletion) -- ToastContext for success/error notifications (already available) - -## Risks & Mitigations - -| Risk | Likelihood | Impact | Mitigation | -|------|------------|--------|------------| -| Accidental deletion | Medium | High | Require typed confirmation for spaces with projects | -| Permission confusion | Low | Medium | Clear "owner only" messaging | - -## Success Criteria -1. Space owner can delete empty spaces with confirmation -2. Space owner can delete spaces with projects (with strong warning) -3. Project owner can delete projects with confirmation -4. All deletions are logged in audit trail -5. UI shows appropriate error messages for non-owners diff --git a/openspec/changes/archive/2026-01-09-add-delete-capability/specs/task-management/spec.md b/openspec/changes/archive/2026-01-09-add-delete-capability/specs/task-management/spec.md deleted file mode 100644 index 906c920..0000000 --- a/openspec/changes/archive/2026-01-09-add-delete-capability/specs/task-management/spec.md +++ /dev/null @@ -1,64 +0,0 @@ -# Task Management - Delete Capability Delta - -## ADDED Requirements - -### Requirement: Space Deletion -系統 SHALL 允許空間擁有者刪除空間(軟刪除)。 - -#### Scenario: 刪除空白空間 -- **GIVEN** 使用者是空間的擁有者 -- **AND** 空間內沒有任何專案 -- **WHEN** 使用者點擊刪除按鈕並確認 -- **THEN** 系統將空間標記為已刪除 (is_active = false) -- **AND** 空間不再顯示於列表中 -- **AND** 顯示成功通知 - -#### Scenario: 刪除含專案的空間 -- **GIVEN** 使用者是空間的擁有者 -- **AND** 空間內包含一個或多個專案 -- **WHEN** 使用者點擊刪除按鈕 -- **THEN** 系統顯示警告對話框,說明包含 N 個專案 -- **AND** 要求使用者輸入空間名稱以確認刪除 -- **WHEN** 使用者正確輸入名稱並確認 -- **THEN** 系統將空間標記為已刪除 -- **AND** 空間內的專案同時被軟刪除 -- **AND** 顯示成功通知 - -#### Scenario: 非擁有者無法刪除空間 -- **GIVEN** 使用者不是空間的擁有者 -- **WHEN** 使用者嘗試刪除空間 -- **THEN** 系統拒絕操作 -- **AND** 顯示權限不足的錯誤訊息 - -### Requirement: Project Deletion -系統 SHALL 允許專案擁有者刪除專案(軟刪除),並記錄於稽核日誌。 - -#### Scenario: 刪除專案 -- **GIVEN** 使用者是專案的擁有者 -- **WHEN** 使用者點擊刪除按鈕 -- **THEN** 系統顯示確認對話框,說明專案內的任務數量 -- **WHEN** 使用者確認刪除 -- **THEN** 系統將專案標記為已刪除 (is_active = false) -- **AND** 專案不再顯示於空間的專案列表中 -- **AND** 系統記錄刪除事件至稽核日誌 -- **AND** 顯示成功通知 - -#### Scenario: 非擁有者無法刪除專案 -- **GIVEN** 使用者不是專案的擁有者 -- **WHEN** 使用者嘗試刪除專案 -- **THEN** 系統拒絕操作 -- **AND** 顯示權限不足的錯誤訊息 - -#### Scenario: 刪除專案的稽核記錄 -- **GIVEN** 專案擁有者刪除專案 -- **WHEN** 刪除操作完成 -- **THEN** 稽核日誌記錄以下資訊: - - event_type: "project.delete" - - resource_type: "project" - - action: DELETE - - user_id: 執行刪除的使用者 - - resource_id: 被刪除的專案 ID - - changes: [{ field: "is_active", old_value: true, new_value: false }] - -## Cross-references -- Relates to: `audit-trail` spec (project deletion triggers audit event) diff --git a/openspec/changes/archive/2026-01-09-add-delete-capability/tasks.md b/openspec/changes/archive/2026-01-09-add-delete-capability/tasks.md deleted file mode 100644 index 7072f40..0000000 --- a/openspec/changes/archive/2026-01-09-add-delete-capability/tasks.md +++ /dev/null @@ -1,55 +0,0 @@ -# Tasks: Add Delete Capability - -## Task List - -### Phase 1: Frontend Services -- [x] **T1**: Create `frontend/src/services/spaces.ts` with CRUD operations including `deleteSpace()` - - Note: Integrated directly into Spaces.tsx following existing pattern (uses api.delete directly) -- [x] **T2**: Add `deleteProject()` function to projects service (or create if missing) - - Note: Integrated directly into Projects.tsx following existing pattern (uses api.delete directly) - -### Phase 2: Spaces Page UI -- [x] **T3**: Add delete button to space cards in Spaces.tsx (visible to owner only) -- [x] **T4**: Implement delete confirmation dialog for spaces -- [x] **T5**: Handle delete success/error with toast notifications -- [x] **T6**: Add spaces delete translations (zh-TW, en) - - Note: Translations already existed in locale files - -### Phase 3: Projects Page UI -- [x] **T7**: Add delete button to project cards in Projects.tsx (visible to owner only) -- [x] **T8**: Implement delete confirmation dialog for projects -- [x] **T9**: Handle delete success/error with toast notifications -- [x] **T10**: Add projects delete translations (zh-TW, en) - - Note: Translations already existed in locale files - -### Phase 4: Verification -- [x] **T11**: Test space deletion flow (empty space, space with projects) - - TypeScript compilation passed -- [x] **T12**: Test project deletion flow - - TypeScript compilation passed -- [x] **T13**: Verify audit trail entries for deletions - - Backend already implements audit logging for project deletions -- [x] **T14**: Verify permission checks (non-owner cannot delete) - - Frontend only shows delete button to owner or system admin - -## Dependencies -``` -T1 ──┬──> T3 ──> T4 ──> T5 ──> T6 - │ -T2 ──┴──> T7 ──> T8 ──> T9 ──> T10 - │ -T11, T12, T13, T14 ─────────────┘ -``` - -## Parallelizable Work -- T1 and T2 can run in parallel -- T3-T6 (Spaces) and T7-T10 (Projects) can run in parallel after T1/T2 - -## Verification Checklist -- [x] Space delete button only visible to owner -- [x] Project delete button only visible to owner -- [x] Confirmation dialog shows for both -- [x] Delete refreshes list correctly -- [x] Toast notifications work for success/error -- [x] Translations complete for zh-TW and en -- [x] Audit log captures project deletions diff --git a/openspec/changes/archive/2026-01-10-add-api-enhancements/proposal.md b/openspec/changes/archive/2026-01-10-add-api-enhancements/proposal.md deleted file mode 100644 index e1745f9..0000000 --- a/openspec/changes/archive/2026-01-10-add-api-enhancements/proposal.md +++ /dev/null @@ -1,18 +0,0 @@ -# Change: Add API Enhancements and Future-Proofing - -## Why -To improve API maintainability and prepare for future growth: standardize API response format for consistent client handling, add API versioning for backwards compatibility, enhance health check for better monitoring, and add project templates for improved user productivity. - -## What Changes -- Implement standardized API response wrapper -- Add API version prefix (/api/v1/) -- Enhance health check with database and Redis status -- Add project template feature for creating projects from templates - -## Impact -- Affected specs: dashboard -- Affected code: - - `backend/app/main.py` - API versioning, health check - - `backend/app/core/response.py` - Response wrapper (new) - - `backend/app/models/project_template.py` - Template model (new) - - `backend/app/api/projects/router.py` - Template endpoints diff --git a/openspec/changes/archive/2026-01-10-add-api-enhancements/specs/dashboard/spec.md b/openspec/changes/archive/2026-01-10-add-api-enhancements/specs/dashboard/spec.md deleted file mode 100644 index 774cfa6..0000000 --- a/openspec/changes/archive/2026-01-10-add-api-enhancements/specs/dashboard/spec.md +++ /dev/null @@ -1,57 +0,0 @@ -## ADDED Requirements - -### Requirement: Standardized API Response Format -The system SHALL return all API responses in a consistent standardized format. - -#### Scenario: Successful API response -- **WHEN** API request succeeds -- **THEN** response includes success=true -- **THEN** response includes data field with result -- **THEN** response includes timestamp field - -#### Scenario: Error API response -- **WHEN** API request fails -- **THEN** response includes success=false -- **THEN** response includes error_code field -- **THEN** response includes message field with description - -### Requirement: API Versioning -The system SHALL support API versioning to enable backwards compatibility during upgrades. - -#### Scenario: Versioned API endpoint -- **WHEN** client calls /api/v1/tasks -- **THEN** system routes to current version implementation -- **THEN** response works with v1 client expectations - -#### Scenario: Legacy API route -- **WHEN** client calls /api/tasks (unversioned) -- **THEN** system routes to default version -- **THEN** response includes deprecation warning header - -### Requirement: Comprehensive Health Check -The system SHALL provide detailed health check endpoints for monitoring. - -#### Scenario: All systems healthy -- **WHEN** health check is called and all dependencies are available -- **THEN** response includes status=healthy -- **THEN** response includes checks object with database=ok, redis=ok - -#### Scenario: Partial system failure -- **WHEN** health check is called and Redis is unavailable -- **THEN** response includes status=degraded -- **THEN** response includes checks object with database=ok, redis=error - -### Requirement: Project Templates -The system SHALL support project templates to standardize project creation. - -#### Scenario: Create project from template -- **WHEN** user creates project selecting a template -- **THEN** system copies TaskStatus definitions from template -- **THEN** system copies CustomField definitions from template -- **THEN** project is created with predefined structure - -#### Scenario: Save project as template -- **WHEN** user saves existing project as template -- **THEN** system creates template with project's TaskStatus definitions -- **THEN** system creates template with project's CustomField definitions -- **THEN** template is available for future project creation diff --git a/openspec/changes/archive/2026-01-10-add-api-enhancements/tasks.md b/openspec/changes/archive/2026-01-10-add-api-enhancements/tasks.md deleted file mode 100644 index d3d34bc..0000000 --- a/openspec/changes/archive/2026-01-10-add-api-enhancements/tasks.md +++ /dev/null @@ -1,35 +0,0 @@ -# Tasks: Add API Enhancements - -## 1. API Response Standardization -- [x] 1.1 Create response wrapper utility class -- [x] 1.2 Define standard response structure {success, data, message, timestamp} -- [x] 1.3 Create error response format with error_code field -- [x] 1.4 Apply wrapper to sample endpoints for validation (deferred - breaking change, use incrementally) -- [x] 1.5 Document response format in API docs (documented in code comments) - -## 2. API Versioning -- [x] 2.1 Add /api/v1/ prefix to all routes -- [x] 2.2 Update frontend API base URL configuration -- [x] 2.3 Keep /api/ routes as alias during transition -- [x] 2.4 Add deprecation headers to old routes - -## 3. Enhanced Health Check -- [x] 3.1 Add database connectivity check -- [x] 3.2 Add Redis connectivity check -- [x] 3.3 Add scheduler status check -- [x] 3.4 Return detailed status object with check results -- [x] 3.5 Add /health/ready and /health/live endpoints - -## 4. Project Templates -- [x] 4.1 Create ProjectTemplate model with fields -- [x] 4.2 Create database migration -- [x] 4.3 Add template CRUD API endpoints -- [x] 4.4 Implement "create project from template" endpoint -- [x] 4.5 Copy TaskStatus and CustomField definitions from template -- [x] 4.6 Add frontend template selection in project creation - -## 5. Testing -- [x] 5.1 Test standardized response format -- [x] 5.2 Test API version routing -- [x] 5.3 Test enhanced health check scenarios -- [x] 5.4 Test project template creation flow diff --git a/openspec/changes/archive/2026-01-10-add-backend-reliability/proposal.md b/openspec/changes/archive/2026-01-10-add-backend-reliability/proposal.md deleted file mode 100644 index 4d26c9d..0000000 --- a/openspec/changes/archive/2026-01-10-add-backend-reliability/proposal.md +++ /dev/null @@ -1,18 +0,0 @@ -# Change: Add Backend Reliability Improvements - -## Why -Several backend reliability issues need addressing: database connection pool may be undersized for production load, Redis publish failures have no fallback mechanism, tasks with active blockers can be deleted without warning, and NAS storage configuration needs validation. - -## What Changes -- Optimize database connection pool configuration -- Add local queue fallback for Redis publish failures -- Add pre-deletion check for active blockers on tasks -- Validate NAS storage path configuration on startup - -## Impact -- Affected specs: document-management -- Affected code: - - `backend/app/core/database.py` - Connection pool config - - `backend/app/services/notification_service.py` - Redis fallback - - `backend/app/api/tasks/router.py` - Blocker check - - `backend/app/services/file_storage_service.py` - NAS validation diff --git a/openspec/changes/archive/2026-01-10-add-backend-reliability/specs/document-management/spec.md b/openspec/changes/archive/2026-01-10-add-backend-reliability/specs/document-management/spec.md deleted file mode 100644 index fef67d2..0000000 --- a/openspec/changes/archive/2026-01-10-add-backend-reliability/specs/document-management/spec.md +++ /dev/null @@ -1,46 +0,0 @@ -## ADDED Requirements - -### Requirement: Storage Path Validation -The system SHALL validate file storage configuration on startup to ensure reliability. - -#### Scenario: Valid NAS storage path -- **WHEN** application starts with valid UPLOAD_DIR configuration -- **THEN** system verifies path exists and is writable -- **THEN** system logs confirmation of storage configuration - -#### Scenario: Invalid storage path -- **WHEN** application starts with invalid or inaccessible UPLOAD_DIR -- **THEN** system logs error with specific issue (not found, not writable) -- **THEN** system falls back to local storage with warning - -#### Scenario: Storage health check -- **WHEN** health check endpoint is called -- **THEN** response includes storage availability status -- **THEN** response includes available disk space if accessible - -### Requirement: Notification Delivery Reliability -The system SHALL ensure notification delivery even during temporary Redis failures. - -#### Scenario: Redis temporarily unavailable -- **WHEN** Redis publish fails due to connection error -- **THEN** system queues message in local memory -- **WHEN** Redis connection recovers -- **THEN** system retries queued messages - -#### Scenario: Queue overflow prevention -- **WHEN** local message queue exceeds maximum size -- **THEN** oldest messages are dropped -- **THEN** system logs warning about dropped messages - -### Requirement: Task Deletion Safety -The system SHALL warn users when deleting tasks with unresolved blockers. - -#### Scenario: Delete task with active blockers -- **WHEN** user attempts to delete task with unresolved blockers -- **THEN** system returns warning with blocker count -- **THEN** user must confirm or use force_delete flag - -#### Scenario: Force delete with blockers -- **WHEN** user force deletes task with blockers -- **THEN** system auto-resolves all blockers with "task deleted" reason -- **THEN** system proceeds with task deletion diff --git a/openspec/changes/archive/2026-01-10-add-backend-reliability/tasks.md b/openspec/changes/archive/2026-01-10-add-backend-reliability/tasks.md deleted file mode 100644 index 9403869..0000000 --- a/openspec/changes/archive/2026-01-10-add-backend-reliability/tasks.md +++ /dev/null @@ -1,31 +0,0 @@ -# Tasks: Add Backend Reliability Improvements - -## 1. Database Connection Pool -- [x] 1.1 Add pool_size=10 configuration -- [x] 1.2 Add max_overflow=20 configuration -- [x] 1.3 Add pool_timeout=30 configuration -- [x] 1.4 Add environment variable overrides for pool settings -- [x] 1.5 Log connection pool statistics periodically - -## 2. Redis Fallback Mechanism -- [x] 2.1 Create in-memory queue for failed Redis publishes -- [x] 2.2 Implement background retry for queued messages -- [x] 2.3 Add max queue size limit to prevent memory issues -- [x] 2.4 Log Redis failures and recovery events - -## 3. Blocker Deletion Check -- [x] 3.1 Add check for unresolved blockers before task deletion -- [x] 3.2 Return warning response with blocker count -- [x] 3.3 Add force_delete parameter to bypass check -- [x] 3.4 Auto-resolve blockers when force deleting - -## 4. NAS Storage Validation -- [x] 4.1 Validate UPLOAD_DIR path exists on startup -- [x] 4.2 Check write permissions on storage directory -- [x] 4.3 Log warning if using local storage instead of NAS -- [x] 4.4 Add health check endpoint for storage status - -## 5. Testing -- [x] 5.1 Test under connection pool exhaustion -- [x] 5.2 Test Redis disconnect and recovery -- [x] 5.3 Test blocker deletion scenarios diff --git a/openspec/changes/archive/2026-01-10-add-concurrency-reliability/proposal.md b/openspec/changes/archive/2026-01-10-add-concurrency-reliability/proposal.md deleted file mode 100644 index 3596d49..0000000 --- a/openspec/changes/archive/2026-01-10-add-concurrency-reliability/proposal.md +++ /dev/null @@ -1,17 +0,0 @@ -# Change: Add Concurrency Handling and Reliability Improvements - -## Why -Multiple users editing the same task simultaneously can lead to lost updates (last-write-wins). Trigger execution failures are not retried, causing permanent failures on transient errors. Soft-delete restore operation does not cascade to child tasks, leading to data inconsistency. - -## What Changes -- Implement optimistic locking using version field for task updates -- Add retry mechanism with exponential backoff for trigger execution -- Implement cascade restore for soft-deleted tasks and their children - -## Impact -- Affected specs: task-management, automation -- Affected code: - - `backend/app/models/task.py` - Add version field - - `backend/app/api/tasks/router.py` - Optimistic locking logic - - `backend/app/services/trigger_scheduler.py` - Retry mechanism - - `backend/app/api/tasks/router.py` - Cascade restore diff --git a/openspec/changes/archive/2026-01-10-add-concurrency-reliability/specs/automation/spec.md b/openspec/changes/archive/2026-01-10-add-concurrency-reliability/specs/automation/spec.md deleted file mode 100644 index b8d9bfd..0000000 --- a/openspec/changes/archive/2026-01-10-add-concurrency-reliability/specs/automation/spec.md +++ /dev/null @@ -1,21 +0,0 @@ -## ADDED Requirements - -### Requirement: Trigger Execution Retry -The system SHALL retry failed trigger executions with exponential backoff to handle transient failures. - -#### Scenario: Trigger succeeds after retry -- **WHEN** trigger execution fails due to transient error -- **THEN** system retries after 1 second delay -- **WHEN** retry succeeds -- **THEN** trigger is marked as successful in execution log - -#### Scenario: Trigger exhausts retries -- **WHEN** trigger execution fails 3 consecutive times -- **THEN** system marks trigger execution as permanently failed -- **THEN** system sends alert notification to system administrators -- **THEN** execution log contains all retry attempts with error details - -#### Scenario: Non-retryable error -- **WHEN** trigger fails with validation or permission error (4xx) -- **THEN** system does not retry and marks as failed immediately -- **THEN** error is logged with appropriate categorization diff --git a/openspec/changes/archive/2026-01-10-add-concurrency-reliability/specs/task-management/spec.md b/openspec/changes/archive/2026-01-10-add-concurrency-reliability/specs/task-management/spec.md deleted file mode 100644 index b743625..0000000 --- a/openspec/changes/archive/2026-01-10-add-concurrency-reliability/specs/task-management/spec.md +++ /dev/null @@ -1,30 +0,0 @@ -## ADDED Requirements - -### Requirement: Optimistic Locking for Task Updates -The system SHALL use optimistic locking to prevent concurrent update conflicts on tasks. - -#### Scenario: Concurrent update detected -- **WHEN** user A and user B both load task at version 1 -- **WHEN** user A saves changes, incrementing version to 2 -- **WHEN** user B attempts to save with version 1 -- **THEN** system returns 409 Conflict error -- **THEN** error message instructs user to refresh and retry - -#### Scenario: Sequential updates succeed -- **WHEN** user loads task at version N -- **WHEN** user saves changes with correct version N -- **THEN** system accepts update and increments version to N+1 - -### Requirement: Soft Delete Cascade Restore -The system SHALL restore child tasks when parent task is restored from soft delete. - -#### Scenario: Parent task restored with children -- **WHEN** soft-deleted parent task is restored -- **THEN** system identifies child tasks deleted at same timestamp -- **THEN** system recursively restores all matching child tasks -- **THEN** audit log records restoration of parent and children - -#### Scenario: Selective restore without children -- **WHEN** user explicitly requests restore without cascade -- **THEN** only parent task is restored -- **THEN** child tasks remain in deleted state diff --git a/openspec/changes/archive/2026-01-10-add-concurrency-reliability/tasks.md b/openspec/changes/archive/2026-01-10-add-concurrency-reliability/tasks.md deleted file mode 100644 index 2f59659..0000000 --- a/openspec/changes/archive/2026-01-10-add-concurrency-reliability/tasks.md +++ /dev/null @@ -1,29 +0,0 @@ -# Tasks: Add Concurrency Handling and Reliability Improvements - -## 1. Optimistic Locking -- [x] 1.1 Add `version` integer field to Task model with default=1 -- [x] 1.2 Create database migration for version field -- [x] 1.3 Include version in TaskResponse schema -- [x] 1.4 Modify update_task to accept and validate version -- [x] 1.5 Return 409 Conflict when version mismatch detected -- [x] 1.6 Auto-increment version on successful update -- [x] 1.7 Update frontend to send version with update requests -- [x] 1.8 Handle 409 Conflict in frontend with user notification - -## 2. Trigger Retry Mechanism -- [x] 2.1 Add retry configuration (max_retries=3, base_delay=1s) -- [x] 2.2 Implement exponential backoff (1s, 2s, 4s) -- [x] 2.3 Log each retry attempt with attempt number -- [x] 2.4 Mark trigger as permanently failed after max retries -- [x] 2.5 Send alert notification when trigger exhausts retries - -## 3. Soft Delete Cascade Restore -- [x] 3.1 Modify restore_task to find all child tasks deleted at same time -- [x] 3.2 Recursively restore child tasks with matching deleted_at timestamp -- [x] 3.3 Add option to restore only parent vs cascade restore -- [x] 3.4 Log restore operations in audit trail - -## 4. Testing -- [x] 4.1 Test concurrent updates with version conflict -- [x] 4.2 Test trigger retry on transient failure -- [x] 4.3 Test cascade restore of parent with children diff --git a/openspec/changes/archive/2026-01-10-add-cycle-detection/proposal.md b/openspec/changes/archive/2026-01-10-add-cycle-detection/proposal.md deleted file mode 100644 index 00e327a..0000000 --- a/openspec/changes/archive/2026-01-10-add-cycle-detection/proposal.md +++ /dev/null @@ -1,18 +0,0 @@ -# Change: Add Cycle Detection for Task Dependencies and Formula Fields - -## Why -The system currently lacks detection for circular references in task dependencies and custom field formulas. This can lead to infinite loops during Gantt chart rendering or formula calculation, potentially causing application crashes or stack overflow errors. - -## What Changes -- Implement cycle detection algorithm (DFS/BFS) for task dependencies -- Add cycle detection for formula field references -- Return descriptive error when cycle is detected -- Prevent saving of configurations that would create cycles - -## Impact -- Affected specs: task-management, automation -- Affected code: - - `backend/app/services/dependency_service.py` - Task dependency validation - - `backend/app/services/formula_service.py` - Formula reference validation - - `backend/app/api/task-dependencies/router.py` - API validation - - `backend/app/api/custom-fields/router.py` - Field validation diff --git a/openspec/changes/archive/2026-01-10-add-cycle-detection/specs/automation/spec.md b/openspec/changes/archive/2026-01-10-add-cycle-detection/specs/automation/spec.md deleted file mode 100644 index a093b3c..0000000 --- a/openspec/changes/archive/2026-01-10-add-cycle-detection/specs/automation/spec.md +++ /dev/null @@ -1,18 +0,0 @@ -## ADDED Requirements - -### Requirement: Formula Field Cycle Prevention -The system SHALL detect and prevent circular references in custom field formulas to avoid infinite calculation loops. - -#### Scenario: Formula self-reference rejected -- **WHEN** user creates a formula field that references itself -- **THEN** system rejects with 400 Bad Request -- **THEN** error message indicates self-reference is not allowed - -#### Scenario: Formula circular reference chain rejected -- **WHEN** user creates formula where Field A references Field B and Field B references Field A -- **THEN** system rejects with 400 Bad Request -- **THEN** error message includes the reference cycle path - -#### Scenario: Valid formula references accepted -- **WHEN** user creates formula referencing other fields without cycles -- **THEN** system saves the formula and calculates values correctly diff --git a/openspec/changes/archive/2026-01-10-add-cycle-detection/specs/task-management/spec.md b/openspec/changes/archive/2026-01-10-add-cycle-detection/specs/task-management/spec.md deleted file mode 100644 index 41d5349..0000000 --- a/openspec/changes/archive/2026-01-10-add-cycle-detection/specs/task-management/spec.md +++ /dev/null @@ -1,19 +0,0 @@ -## ADDED Requirements - -### Requirement: Task Dependency Cycle Prevention -The system SHALL detect and prevent circular dependencies between tasks to ensure Gantt charts can be properly rendered. - -#### Scenario: Direct circular dependency rejected -- **WHEN** user attempts to create dependency where Task A depends on Task B and Task B depends on Task A -- **THEN** system rejects the operation with 400 Bad Request -- **THEN** error message includes the cycle path (e.g., "Circular dependency detected: A -> B -> A") - -#### Scenario: Indirect circular dependency rejected -- **WHEN** user attempts to create dependency that would form a cycle (A -> B -> C -> A) -- **THEN** system rejects the operation with 400 Bad Request -- **THEN** error message includes the full cycle path - -#### Scenario: Valid dependency chain accepted -- **WHEN** user creates dependencies forming a valid DAG (directed acyclic graph) -- **THEN** system accepts and saves the dependencies -- **THEN** Gantt chart renders correctly with proper task ordering diff --git a/openspec/changes/archive/2026-01-10-add-cycle-detection/tasks.md b/openspec/changes/archive/2026-01-10-add-cycle-detection/tasks.md deleted file mode 100644 index 41c23cf..0000000 --- a/openspec/changes/archive/2026-01-10-add-cycle-detection/tasks.md +++ /dev/null @@ -1,23 +0,0 @@ -# Tasks: Add Cycle Detection - -## 1. Task Dependency Cycle Detection -- [x] 1.1 Implement DFS-based cycle detection algorithm in dependency_service.py -- [x] 1.2 Add validation hook when creating/updating task dependencies -- [x] 1.3 Return 400 Bad Request with cycle path details when detected -- [x] 1.4 Add cycle detection check in bulk dependency operations - -## 2. Formula Field Cycle Detection -- [x] 2.1 Parse formula field references to build dependency graph -- [x] 2.2 Implement cycle detection for formula field references -- [x] 2.3 Add validation when saving custom field formulas -- [x] 2.4 Return descriptive error showing the cycle path - -## 3. Frontend Feedback -- [x] 3.1 Display user-friendly error message when cycle detected -- [x] 3.2 Optionally highlight the problematic dependencies in UI - -## 4. Testing -- [x] 4.1 Add unit tests for cycle detection algorithm -- [x] 4.2 Test direct circular dependency (A -> B -> A) -- [x] 4.3 Test indirect circular dependency (A -> B -> C -> A) -- [x] 4.4 Test formula field circular references diff --git a/openspec/changes/archive/2026-01-10-add-frontend-improvements/proposal.md b/openspec/changes/archive/2026-01-10-add-frontend-improvements/proposal.md deleted file mode 100644 index 0dbd5d7..0000000 --- a/openspec/changes/archive/2026-01-10-add-frontend-improvements/proposal.md +++ /dev/null @@ -1,18 +0,0 @@ -# Change: Add Frontend Responsive Design and i18n Completion - -## Why -The current frontend has usability issues on mobile and tablet devices - sidebar doesn't collapse properly, tables overflow, and some UI elements are not touch-friendly. Additionally, internationalization (i18n) is incomplete with some hardcoded Chinese text remaining in components. - -## What Changes -- Implement responsive sidebar with collapsible behavior on small screens -- Add responsive table handling with horizontal scroll or card view -- Complete i18n translation coverage for all UI text -- Improve touch targets for mobile usability - -## Impact -- Affected specs: dashboard -- Affected code: - - `frontend/src/components/Layout.tsx` - Responsive sidebar - - `frontend/src/components/*.tsx` - Responsive styles - - `frontend/src/i18n/locales/*.json` - Translation files - - `frontend/src/pages/*.tsx` - Replace hardcoded text diff --git a/openspec/changes/archive/2026-01-10-add-frontend-improvements/specs/dashboard/spec.md b/openspec/changes/archive/2026-01-10-add-frontend-improvements/specs/dashboard/spec.md deleted file mode 100644 index 698e7a6..0000000 --- a/openspec/changes/archive/2026-01-10-add-frontend-improvements/specs/dashboard/spec.md +++ /dev/null @@ -1,39 +0,0 @@ -## ADDED Requirements - -### Requirement: Responsive Layout -The system SHALL provide a responsive user interface that adapts to different screen sizes for optimal usability. - -#### Scenario: Mobile sidebar behavior -- **WHEN** user accesses application on mobile device (width < 768px) -- **THEN** sidebar is hidden by default -- **THEN** hamburger menu button is visible in header -- **WHEN** user taps hamburger menu -- **THEN** sidebar slides in from left with backdrop overlay - -#### Scenario: Table responsive behavior -- **WHEN** user views task list on small screen -- **THEN** table displays with horizontal scroll or switches to card layout -- **THEN** all essential information remains accessible - -#### Scenario: Touch-friendly interactions -- **WHEN** user interacts with application on touch device -- **THEN** all interactive elements have minimum 44x44 pixel tap targets -- **THEN** sufficient spacing prevents accidental taps - -### Requirement: Complete Internationalization -The system SHALL support complete internationalization with no hardcoded text strings. - -#### Scenario: Language switching -- **WHEN** user changes language preference -- **THEN** all UI text updates to selected language -- **THEN** no untranslated strings remain visible - -#### Scenario: Date and time localization -- **WHEN** dates and times are displayed -- **THEN** format follows user's locale preference -- **THEN** relative times (e.g., "2 hours ago") are properly translated - -#### Scenario: New component text -- **WHEN** new UI components are added -- **THEN** all text strings use i18n translation keys -- **THEN** translations exist for all supported locales diff --git a/openspec/changes/archive/2026-01-10-add-frontend-improvements/tasks.md b/openspec/changes/archive/2026-01-10-add-frontend-improvements/tasks.md deleted file mode 100644 index 888ec3d..0000000 --- a/openspec/changes/archive/2026-01-10-add-frontend-improvements/tasks.md +++ /dev/null @@ -1,30 +0,0 @@ -# Tasks: Add Frontend Improvements - -## 1. Responsive Sidebar -- [x] 1.1 Add hamburger menu button for mobile screens -- [x] 1.2 Implement slide-out sidebar behavior on small screens -- [x] 1.3 Add overlay backdrop when sidebar is open on mobile -- [x] 1.4 Persist sidebar state preference in local storage - -## 2. Responsive Tables -- [x] 2.1 Add horizontal scroll wrapper for wide tables -- [x] 2.2 Implement card view alternative for task lists on mobile -- [x] 2.3 Make table columns prioritized (hide less important on small screens) -- [x] 2.4 Ensure touch-friendly row actions - -## 3. Touch-Friendly UI -- [x] 3.1 Increase tap target sizes to minimum 44x44 pixels -- [x] 3.2 Add proper spacing between interactive elements -- [x] 3.3 Improve drag-and-drop for touch devices on Kanban - -## 4. i18n Completion -- [x] 4.1 Audit all components for hardcoded Chinese text -- [x] 4.2 Extract remaining strings to translation files -- [x] 4.3 Add missing translations for en and zh-TW locales -- [x] 4.4 Ensure date/time formatting uses locale settings - -## 5. Testing -- [x] 5.1 Test on mobile viewport (375px width) -- [x] 5.2 Test on tablet viewport (768px width) -- [x] 5.3 Test language switching functionality -- [x] 5.4 Verify all text is properly translated diff --git a/openspec/changes/archive/2026-01-10-add-input-validation-security/proposal.md b/openspec/changes/archive/2026-01-10-add-input-validation-security/proposal.md deleted file mode 100644 index 44b9a5b..0000000 --- a/openspec/changes/archive/2026-01-10-add-input-validation-security/proposal.md +++ /dev/null @@ -1,17 +0,0 @@ -# Change: Add Input Validation and Security Enhancements - -## Why -Current API endpoints lack comprehensive input validation, exposing the system to potential DoS attacks, database overflow errors, and security vulnerabilities. Additionally, WebSocket authentication tokens are exposed in query parameters which may be logged. - -## What Changes -- Add length validation to all Pydantic schema string fields -- Add numeric range validation for decimal/integer fields -- Enhance WebSocket token handling to avoid query parameter exposure -- Strengthen file path traversal protection in file storage service - -## Impact -- Affected specs: user-auth -- Affected code: - - `backend/app/schemas/*.py` - All schema files - - `backend/app/api/websocket/router.py` - WebSocket authentication - - `backend/app/services/file_storage_service.py` - Path validation diff --git a/openspec/changes/archive/2026-01-10-add-input-validation-security/specs/user-auth/spec.md b/openspec/changes/archive/2026-01-10-add-input-validation-security/specs/user-auth/spec.md deleted file mode 100644 index a78edc9..0000000 --- a/openspec/changes/archive/2026-01-10-add-input-validation-security/specs/user-auth/spec.md +++ /dev/null @@ -1,37 +0,0 @@ -## ADDED Requirements - -### Requirement: Input Length Validation -The system SHALL enforce maximum length limits on all user-provided string inputs to prevent DoS attacks and database overflow. - -#### Scenario: Task title exceeds maximum length -- **WHEN** user submits a task with title longer than 500 characters -- **THEN** system returns 422 Validation Error with descriptive message - -#### Scenario: Description field within limits -- **WHEN** user submits content with description under 10000 characters -- **THEN** system accepts the input and processes normally - -### Requirement: Secure WebSocket Authentication -The system SHALL authenticate WebSocket connections without exposing tokens in URL query parameters. - -#### Scenario: WebSocket connection with token in first message -- **WHEN** client connects to WebSocket endpoint -- **THEN** server waits for authentication message containing JWT token -- **THEN** server validates token before accepting further messages - -#### Scenario: WebSocket connection timeout without authentication -- **WHEN** client connects but does not send authentication within 10 seconds -- **THEN** server closes the connection with appropriate error code - -### Requirement: Path Traversal Protection -The system SHALL prevent file path traversal attacks by validating all file paths resolve within the designated storage directory. - -#### Scenario: Path traversal attempt detected -- **WHEN** request contains file path with "../" or absolute path outside storage -- **THEN** system rejects request and logs security warning -- **THEN** system returns 403 Forbidden error - -#### Scenario: Valid file path within storage -- **WHEN** request contains valid relative file path -- **THEN** system resolves path and verifies it is within storage directory -- **THEN** system processes file operation normally diff --git a/openspec/changes/archive/2026-01-10-add-input-validation-security/tasks.md b/openspec/changes/archive/2026-01-10-add-input-validation-security/tasks.md deleted file mode 100644 index 41a8f89..0000000 --- a/openspec/changes/archive/2026-01-10-add-input-validation-security/tasks.md +++ /dev/null @@ -1,24 +0,0 @@ -# Tasks: Add Input Validation and Security Enhancements - -## 1. Schema Input Validation -- [x] 1.1 Add max_length validation to TaskBase schema (title: 500, description: 10000) -- [x] 1.2 Add max_length validation to ProjectBase schema -- [x] 1.3 Add max_length validation to SpaceBase schema -- [x] 1.4 Add max_length validation to CommentBase schema -- [x] 1.5 Add max_length validation to all other schema string fields -- [x] 1.6 Add numeric range validation (ge=0, le=max_value) for decimal fields - -## 2. WebSocket Token Security -- [x] 2.1 Implement WebSocket authentication via first message instead of query parameter -- [x] 2.2 Update frontend WebSocket connection to send token in first message -- [x] 2.3 Add server log filtering to mask sensitive query parameters as fallback (N/A - token no longer in query params) - -## 3. File Path Security -- [x] 3.1 Add explicit path traversal validation in file_storage_service.py -- [x] 3.2 Ensure resolved path is within base directory -- [x] 3.3 Add logging for path traversal attempts - -## 4. Testing -- [x] 4.1 Add unit tests for input validation edge cases -- [x] 4.2 Add security tests for path traversal attempts -- [x] 4.3 Test WebSocket authentication flow diff --git a/openspec/changes/archive/2026-01-10-add-permission-enhancements/proposal.md b/openspec/changes/archive/2026-01-10-add-permission-enhancements/proposal.md deleted file mode 100644 index 1a7b922..0000000 --- a/openspec/changes/archive/2026-01-10-add-permission-enhancements/proposal.md +++ /dev/null @@ -1,16 +0,0 @@ -# Change: Add Permission Enhancements for Manager and Cross-Department Access - -## Why -Department managers cannot view their subordinates' workload data, preventing effective resource allocation decisions. Additionally, cross-department project collaboration is hindered because project members from other departments may be denied access. - -## What Changes -- Enable department managers to view workload data of their department members -- Add project membership model to support cross-department collaboration -- Enhance access control to check project membership in addition to department - -## Impact -- Affected specs: resource-management -- Affected code: - - `backend/app/api/workload/router.py` - Manager access logic - - `backend/app/middleware/auth.py` - Access control checks - - `backend/app/models/` - New project_members model (optional) diff --git a/openspec/changes/archive/2026-01-10-add-permission-enhancements/specs/resource-management/spec.md b/openspec/changes/archive/2026-01-10-add-permission-enhancements/specs/resource-management/spec.md deleted file mode 100644 index df04359..0000000 --- a/openspec/changes/archive/2026-01-10-add-permission-enhancements/specs/resource-management/spec.md +++ /dev/null @@ -1,31 +0,0 @@ -## ADDED Requirements - -### Requirement: Manager Workload Visibility -The system SHALL allow department managers to view workload data for all members within their department. - -#### Scenario: Manager views department member workload -- **WHEN** a department manager requests workload data for a user in their department -- **THEN** system returns the workload data for that user - -#### Scenario: Manager denied access to other department workload -- **WHEN** a department manager requests workload data for a user in a different department -- **THEN** system returns 403 Forbidden error - -#### Scenario: Regular user cannot view others' workload -- **WHEN** a non-manager user requests workload data for another user -- **THEN** system returns 403 Forbidden error - -### Requirement: Cross-Department Project Membership -The system SHALL support explicit project membership to enable cross-department collaboration. - -#### Scenario: Add cross-department member to project -- **WHEN** project owner adds a user from another department as project member -- **THEN** user gains access to the project regardless of department - -#### Scenario: Project member accesses cross-department project -- **WHEN** a project member from another department accesses project resources -- **THEN** system grants access based on project membership - -#### Scenario: Non-member denied access despite same department -- **WHEN** a user not in project membership list attempts to access confidential project -- **THEN** system denies access unless user is in the project's department diff --git a/openspec/changes/archive/2026-01-10-add-permission-enhancements/tasks.md b/openspec/changes/archive/2026-01-10-add-permission-enhancements/tasks.md deleted file mode 100644 index 1aa872c..0000000 --- a/openspec/changes/archive/2026-01-10-add-permission-enhancements/tasks.md +++ /dev/null @@ -1,19 +0,0 @@ -# Tasks: Add Permission Enhancements - -## 1. Manager Workload Access -- [x] 1.1 Add role-based check in workload router for department managers -- [x] 1.2 Allow managers to query workload for users in their department -- [x] 1.3 Add `is_department_manager` flag or role to user model if not exists -- [x] 1.4 Update workload API documentation - -## 2. Cross-Department Project Access -- [x] 2.1 Create ProjectMember model for explicit project membership -- [x] 2.2 Add database migration for project_members table -- [x] 2.3 Update check_project_access() to include project membership check -- [x] 2.4 Add API endpoints to manage project members -- [x] 2.5 Update frontend to display and manage project members - -## 3. Testing -- [x] 3.1 Add tests for manager viewing subordinate workload -- [x] 3.2 Add tests for cross-department project member access -- [x] 3.3 Test access denied for non-members and non-managers diff --git a/openspec/changes/archive/2026-01-10-add-rate-limiting-expansion/proposal.md b/openspec/changes/archive/2026-01-10-add-rate-limiting-expansion/proposal.md deleted file mode 100644 index 6c0d96e..0000000 --- a/openspec/changes/archive/2026-01-10-add-rate-limiting-expansion/proposal.md +++ /dev/null @@ -1,17 +0,0 @@ -# Change: Expand Rate Limiting to Sensitive API Endpoints - -## Why -Currently only the login endpoint has rate limiting. Other sensitive operations (task creation, report generation, bulk operations) can be abused through excessive requests, potentially causing service degradation or enabling brute-force attacks. - -## What Changes -- Apply rate limiting to task creation/update endpoints -- Apply rate limiting to report generation endpoints -- Apply rate limiting to bulk operation endpoints -- Add configurable rate limit tiers based on endpoint sensitivity - -## Impact -- Affected specs: user-auth -- Affected code: - - `backend/app/api/tasks/router.py` - Task rate limits - - `backend/app/api/reports/router.py` - Report rate limits - - `backend/app/core/config.py` - Rate limit configuration diff --git a/openspec/changes/archive/2026-01-10-add-rate-limiting-expansion/specs/user-auth/spec.md b/openspec/changes/archive/2026-01-10-add-rate-limiting-expansion/specs/user-auth/spec.md deleted file mode 100644 index 3e6331f..0000000 --- a/openspec/changes/archive/2026-01-10-add-rate-limiting-expansion/specs/user-auth/spec.md +++ /dev/null @@ -1,25 +0,0 @@ -## ADDED Requirements - -### Requirement: Comprehensive API Rate Limiting -The system SHALL enforce rate limits on all sensitive API endpoints to prevent abuse and ensure service availability. - -#### Scenario: Task creation rate limit exceeded -- **WHEN** user exceeds 60 task creation requests per minute -- **THEN** system returns 429 Too Many Requests -- **THEN** response includes Retry-After header - -#### Scenario: Report generation rate limit exceeded -- **WHEN** user exceeds 5 report generation requests per minute -- **THEN** system returns 429 Too Many Requests -- **THEN** response includes rate limit headers - -#### Scenario: Rate limit headers provided -- **WHEN** user makes any rate-limited API request -- **THEN** response includes X-RateLimit-Limit header -- **THEN** response includes X-RateLimit-Remaining header -- **THEN** response includes X-RateLimit-Reset header - -#### Scenario: Rate limit window reset -- **WHEN** rate limit window expires -- **THEN** user can make requests again up to the limit -- **THEN** X-RateLimit-Remaining resets to maximum diff --git a/openspec/changes/archive/2026-01-10-add-rate-limiting-expansion/tasks.md b/openspec/changes/archive/2026-01-10-add-rate-limiting-expansion/tasks.md deleted file mode 100644 index a020d8c..0000000 --- a/openspec/changes/archive/2026-01-10-add-rate-limiting-expansion/tasks.md +++ /dev/null @@ -1,30 +0,0 @@ -# Tasks: Expand Rate Limiting - -## 1. Configuration -- [x] 1.1 Define rate limit tiers in config (standard, sensitive, heavy) -- [x] 1.2 Standard: 60/minute, Sensitive: 20/minute, Heavy: 5/minute -- [x] 1.3 Add environment variable overrides for rate limits - -## 2. Task API Rate Limiting -- [x] 2.1 Apply "standard" rate limit to POST /api/tasks -- [x] 2.2 Apply "standard" rate limit to PATCH /api/tasks/{id} -- [x] 2.3 Apply "heavy" rate limit to bulk task operations - -## 3. Report API Rate Limiting -- [x] 3.1 Apply "heavy" rate limit to POST /api/reports/generate -- [x] 3.2 Apply "sensitive" rate limit to report export endpoints - -## 4. Other Sensitive Endpoints -- [x] 4.1 Apply "sensitive" rate limit to password change endpoint (N/A - uses external auth) -- [x] 4.2 Apply "sensitive" rate limit to attachment upload -- [x] 4.3 Apply "standard" rate limit to comment creation - -## 5. Response Headers -- [x] 5.1 Include X-RateLimit-Limit header in responses -- [x] 5.2 Include X-RateLimit-Remaining header -- [x] 5.3 Include X-RateLimit-Reset header - -## 6. Testing -- [x] 6.1 Test rate limit enforcement -- [x] 6.2 Test rate limit reset after window -- [x] 6.3 Verify 429 Too Many Requests response diff --git a/openspec/changes/archive/2026-01-11-add-error-resilience/proposal.md b/openspec/changes/archive/2026-01-11-add-error-resilience/proposal.md deleted file mode 100644 index bef39d9..0000000 --- a/openspec/changes/archive/2026-01-11-add-error-resilience/proposal.md +++ /dev/null @@ -1,19 +0,0 @@ -# Change: Add Frontend Error Resilience - -## Why - -QA review identified that the frontend lacks React Error Boundaries. When a render error occurs in any component, the entire application crashes with a white screen, providing no recovery path for users. - -## What Changes - -- Add React Error Boundary components around major application sections -- Implement graceful degradation with user-friendly error messages -- Add error reporting mechanism to capture frontend crashes - -## Impact - -- Affected specs: `dashboard` -- Affected code: - - `frontend/src/components/ErrorBoundary.tsx` - New component - - `frontend/src/App.tsx` - Wrap routes with Error Boundaries - - `frontend/src/pages/` - Section-level boundaries diff --git a/openspec/changes/archive/2026-01-11-add-error-resilience/specs/dashboard/spec.md b/openspec/changes/archive/2026-01-11-add-error-resilience/specs/dashboard/spec.md deleted file mode 100644 index 9b6044a..0000000 --- a/openspec/changes/archive/2026-01-11-add-error-resilience/specs/dashboard/spec.md +++ /dev/null @@ -1,24 +0,0 @@ -## ADDED Requirements - -### Requirement: Error Boundary Protection -The frontend application SHALL gracefully handle component render errors without crashing the entire application. - -#### Scenario: Component error contained -- **WHEN** a render error occurs in a dashboard widget -- **THEN** only that widget SHALL display an error state -- **AND** other widgets SHALL continue to function normally - -#### Scenario: User-friendly error display -- **WHEN** a component fails to render -- **THEN** users SHALL see a friendly error message -- **AND** users SHALL have an option to retry or report the issue - -#### Scenario: Error logging -- **WHEN** a render error is caught by an Error Boundary -- **THEN** the error details SHALL be logged for debugging -- **AND** error context (component stack) SHALL be captured - -#### Scenario: Recovery option -- **WHEN** a user sees an error fallback UI -- **AND** the user clicks "Retry" -- **THEN** the failed component SHALL attempt to re-render diff --git a/openspec/changes/archive/2026-01-11-add-error-resilience/tasks.md b/openspec/changes/archive/2026-01-11-add-error-resilience/tasks.md deleted file mode 100644 index cc36486..0000000 --- a/openspec/changes/archive/2026-01-11-add-error-resilience/tasks.md +++ /dev/null @@ -1,14 +0,0 @@ -## 1. Error Boundary Implementation -- [x] 1.1 Create base ErrorBoundary component with fallback UI -- [x] 1.2 Add error logging/reporting to ErrorBoundary -- [x] 1.3 Create user-friendly error fallback designs - -## 2. Application Integration -- [x] 2.1 Wrap main App routes with top-level Error Boundary -- [x] 2.2 Add section-level boundaries around Dashboard, Tasks, Projects -- [x] 2.3 Add component-level boundaries for complex widgets - -## 3. Testing -- [x] 3.1 Write tests for ErrorBoundary component -- [x] 3.2 Add integration tests that verify graceful degradation -- [x] 3.3 Test error recovery flow diff --git a/openspec/changes/archive/2026-01-11-add-trigger-conditions-weekly-subscription/design.md b/openspec/changes/archive/2026-01-11-add-trigger-conditions-weekly-subscription/design.md deleted file mode 100644 index 1bd864f..0000000 --- a/openspec/changes/archive/2026-01-11-add-trigger-conditions-weekly-subscription/design.md +++ /dev/null @@ -1,45 +0,0 @@ -## Context -需要支援 Trigger 複合條件(AND-only)、群組通知目標,以及「手動訂閱」的週報收件人規則,並同步更新前端操作介面。 - -## Goals / Non-Goals -- Goals: - - 支援多條件 AND-only 觸發器條件 - - 支援 `due_date`/`start_date`/`custom_fields`(含 formula)與 `before/after/in` 運算子 - - 支援群組通知目標與去重、排除觸發者 - - 週報改為手動訂閱且收件人為專案成員(含跨部門) - - 前端同版支援條件編輯與訂閱開關 -- Non-Goals: - - 不實作 OR/巢狀條件樹 - - 不新增 Email 通知通道 - - 不改動排程時間(維持週五 16:00) - -## Decisions -- 條件結構 - - `field_change` 觸發器支援兩種條件格式: - - Legacy: `{ field, operator, value }` - - Composite: `{ logic: "and", rules: [ { field, operator, value, field_id? } ] }` - - `in` 在日期欄位採 `{ start, end }`,且包含邊界。 - - `in` 在文字/下拉/人員欄位採用陣列值。 - - `before/after` 用於日期欄位;`before/after` 也可用於數值類(number/formula)。 -- 觸發時機 - - 觸發器於任務/自訂欄位更新時評估。 - - 規則需同時滿足且至少有一個規則所屬欄位在此次更新中變更,避免無關更新重複觸發。 -- 通知目標解析 - - 支援 `assignee`/`creator`/`project_owner`/`project_members`/`department:`/`role:`/`user:`。 - - `project_members` 包含 owner。 - - `department`/`role` 解析為組織內所有符合的使用者。 - - 排除觸發者本人並對收件人去重。 -- 週報訂閱 - - 使用 `pjctrl_scheduled_reports` 作為訂閱資料;一位使用者一筆 weekly 訂閱。 - - 週報僅發送給已訂閱使用者。 - - 週報內容包含使用者為 owner 或 project member 的所有專案。 - -## Risks / Trade-offs -- `department`/`role` 可能觸及非專案成員,需確保用戶理解通知範圍。 -- 自訂欄位(formula)計算可能帶來額外成本,需避免 N+1 查詢。 - -## Migration Plan -- 無資料庫 schema 變更。 - -## Open Questions -- 無(已確認 AND-only、日期區間 inclusive、role 自由輸入)。 diff --git a/openspec/changes/archive/2026-01-11-add-trigger-conditions-weekly-subscription/proposal.md b/openspec/changes/archive/2026-01-11-add-trigger-conditions-weekly-subscription/proposal.md deleted file mode 100644 index ab7f1b7..0000000 --- a/openspec/changes/archive/2026-01-11-add-trigger-conditions-weekly-subscription/proposal.md +++ /dev/null @@ -1,17 +0,0 @@ -# Change: Trigger Composite Conditions, Group Notifications, Weekly Report Subscriptions - -## Why -目前觸發器僅支援單一條件與單一通知對象,無法滿足複合條件與群組通知需求;週報收件人規則亦需改為手動訂閱並涵蓋專案成員。 - -## What Changes -- 新增 AND-only 複合條件結構,支援 `due_date`/`start_date`/`custom_fields`(含 formula),並加入 `before/after/in` 運算子(日期 `in` 採用區間且包含邊界)。 -- 通知目標擴充為 `project_members`/`department:`/`role:`/`user:`,並加入去重與排除觸發者規則。 -- 週報改為「手動訂閱」機制,僅發送給已訂閱的使用者;週報內容涵蓋使用者為 owner 或 project member 的所有專案。 -- 前端同步支援複合條件編輯、群組通知目標、MySettings 週報訂閱開關。 - -## Impact -- Affected specs: `automation` -- Affected code: - - Backend: `backend/app/schemas/trigger.py`, `backend/app/services/trigger_service.py`, `backend/app/api/triggers/router.py`, `backend/app/services/report_service.py`, `backend/app/api/reports/router.py` - - Frontend: `frontend/src/components/TriggerForm.tsx`, `frontend/src/components/TriggerList.tsx`, `frontend/src/services/triggers.ts`, `frontend/src/pages/MySettings.tsx` - - Tests: trigger conditions/group notifications/weekly report subscription coverage diff --git a/openspec/changes/archive/2026-01-11-add-trigger-conditions-weekly-subscription/specs/automation/spec.md b/openspec/changes/archive/2026-01-11-add-trigger-conditions-weekly-subscription/specs/automation/spec.md deleted file mode 100644 index fddc9af..0000000 --- a/openspec/changes/archive/2026-01-11-add-trigger-conditions-weekly-subscription/specs/automation/spec.md +++ /dev/null @@ -1,117 +0,0 @@ -## MODIFIED Requirements -### Requirement: Trigger Conditions -系統 SHALL 支援多種觸發條件類型,包含欄位變更、時間條件、以及 AND-only 複合條件。欄位變更條件 SHALL 支援 `status_id`、`assignee_id`、`priority`、`due_date`、`start_date` 與 `custom_fields`(含 formula),並支援 `equals`、`not_equals`、`changed_to`、`changed_from`、`before`、`after`、`in` 運算子。日期欄位的 `in` SHALL 使用 `{start, end}` 區間且包含邊界。 - -#### Scenario: 欄位變更條件 -- **GIVEN** 觸發器設定為「當 Status 欄位變更為特定值」 -- **WHEN** 任務的 Status 欄位變更為該值 -- **THEN** 觸發器被觸發 - -#### Scenario: 時間條件 -- **GIVEN** 觸發器設定為「每週五下午 4:00」 -- **WHEN** 系統時間達到設定時間 -- **THEN** 觸發器被觸發 - -#### Scenario: 複合條件 -- **GIVEN** 觸發器設定為「當 Status = 完成 且 Priority = 高」 -- **WHEN** 任務同時滿足兩個條件 -- **THEN** 觸發器被觸發 - -#### Scenario: 日期區間條件 -- **GIVEN** 觸發器設定為「due_date in {start, end}」且區間為含邊界 -- **WHEN** 任務的 due_date 落在該區間內 -- **THEN** 觸發器被觸發 - -#### Scenario: 自訂欄位(公式)條件 -- **GIVEN** 觸發器設定為「custom_fields(公式欄位) equals 目標值」 -- **WHEN** 任務的公式欄位計算值符合目標值 -- **THEN** 觸發器被觸發 - -#### Scenario: Cron 表達式觸發 -- **GIVEN** 觸發器設定為 cron 表達式 (如 `0 9 * * 1` 每週一早上 9 點) -- **WHEN** 系統時間匹配 cron 表達式 -- **THEN** 系統評估並執行該觸發器 -- **AND** 記錄執行結果至 trigger_logs - -#### Scenario: 截止日期提醒 -- **GIVEN** 觸發器設定為「截止日前 N 天提醒」 -- **WHEN** 任務距離截止日剩餘 N 天 -- **THEN** 系統發送提醒通知給任務指派者 -- **AND** 每個任務每個提醒設定只觸發一次 - -### Requirement: Trigger Actions -系統 SHALL 支援多種觸發動作類型。通知動作 SHALL 支援單人與群組目標(`assignee`、`creator`、`project_owner`、`project_members`、`department:`、`role:`、`user:`),並對收件人去重且排除觸發者本人。 - -#### Scenario: 發送通知動作 -- **GIVEN** 觸發器動作設定為發送通知 -- **WHEN** 觸發器被觸發 -- **THEN** 系統發送通知給指定對象 -- **AND** 通知內容可使用變數(如任務名稱、指派者) - -#### Scenario: 群組通知目標 -- **GIVEN** 觸發器通知目標為 `project_members` 或 `department:` 或 `role:` -- **WHEN** 觸發器被觸發 -- **THEN** 系統通知所有對應成員 -- **AND** 去除重複收件人 -- **AND** 排除觸發者本人 - -#### Scenario: 更新欄位動作 -- **GIVEN** 觸發器動作設定為更新欄位 -- **WHEN** 觸發器被觸發 -- **THEN** 系統自動更新指定欄位的值 - -#### Scenario: 指派任務動作 -- **GIVEN** 觸發器動作設定為自動指派 -- **WHEN** 觸發器被觸發 -- **THEN** 系統自動將任務指派給指定人員 - -### Requirement: Automated Weekly Report -系統 SHALL 每週五下午 4:00 自動彙整完整任務清單,發送給已訂閱的專案成員(含跨部門成員)。週報內容 SHALL 以收件人為 owner 或 project member 的專案為範圍。 - -#### Scenario: 週報內容完整清單 -- **GIVEN** 週報生成中 -- **WHEN** 系統彙整資料 -- **THEN** 週報包含各專案的: - - 本週已完成任務清單(含 completed_at, assignee_name) - - 進行中任務清單(含 assignee_name, due_date) - - 逾期任務警示(含 due_date, days_overdue) - - 阻礙中任務清單(含 blocker_reason, blocked_since) - - 下週預計完成任務(含 due_date, assignee_name) -- **AND** 不設任務數量上限 - -#### Scenario: 週報收件人範圍 -- **GIVEN** 使用者為專案成員且已開啟週報訂閱 -- **WHEN** 週報排程執行 -- **THEN** 使用者收到週報 - -#### Scenario: 阻礙任務識別 -- **GIVEN** 任務有未解除的 Blocker 記錄 -- **WHEN** 週報查詢阻礙任務 -- **THEN** 系統查詢 Blocker 表 resolved_at IS NULL 的任務 -- **AND** 顯示阻礙原因與開始時間 - -#### Scenario: 下週預計任務 -- **GIVEN** 任務的 due_date 在下週範圍內 -- **WHEN** 週報查詢下週預計任務 -- **THEN** 系統篩選 due_date >= 下週一 且 < 下週日 -- **AND** 排除已完成狀態的任務 - -## ADDED Requirements -### Requirement: Weekly Report Subscription -系統 SHALL 提供週報訂閱管理功能,讓使用者手動開啟或關閉週報。 - -#### Scenario: 開啟週報訂閱 -- **GIVEN** 使用者尚未訂閱週報 -- **WHEN** 使用者在 MySettings 開啟週報訂閱 -- **THEN** 系統建立或啟用該使用者的 weekly 訂閱 - -#### Scenario: 關閉週報訂閱 -- **GIVEN** 使用者已訂閱週報 -- **WHEN** 使用者在 MySettings 關閉週報訂閱 -- **THEN** 系統停用該使用者的 weekly 訂閱 -- **AND** 後續排程不再發送週報 - -#### Scenario: 未訂閱預設行為 -- **GIVEN** 使用者未開啟週報訂閱 -- **WHEN** 週報排程執行 -- **THEN** 使用者不會收到週報 diff --git a/openspec/changes/archive/2026-01-11-add-trigger-conditions-weekly-subscription/tasks.md b/openspec/changes/archive/2026-01-11-add-trigger-conditions-weekly-subscription/tasks.md deleted file mode 100644 index 6302d17..0000000 --- a/openspec/changes/archive/2026-01-11-add-trigger-conditions-weekly-subscription/tasks.md +++ /dev/null @@ -1,22 +0,0 @@ -## 1. Backend -- [x] 1.1 Extend trigger schemas/validation to accept composite conditions and new operators/fields -- [x] 1.2 Implement composite condition evaluation (AND-only) with operator semantics and custom field/formula support -- [x] 1.3 Extend notify target resolution for group targets, dedup recipients, and exclude triggerer -- [x] 1.4 Evaluate triggers on due_date/start_date/custom_field updates -- [x] 1.5 Add weekly report subscription API (get/update) and enforce manual subscription -- [x] 1.6 Include project members (and owner) in weekly report project scope - -## 2. Frontend -- [x] 2.1 Update trigger API types for composite conditions and group targets -- [x] 2.2 Update TriggerForm/TriggerList to build AND-only rule lists with date range + custom field inputs -- [x] 2.3 Add MySettings weekly report subscription toggle (with API integration) -- [x] 2.4 Add i18n strings for new trigger/weekly report UI - -## 3. Tests -- [x] 3.1 Backend tests for composite conditions (status+priority, due_date range) -- [x] 3.2 Backend tests for custom field (formula) conditions -- [x] 3.3 Backend tests for group notification targeting (department/role/project_members) with dedup/exclude -- [x] 3.4 Backend tests for weekly report subscription and project-member scope - -## 4. Validation -- [x] 4.1 Run targeted pytest in conda and report results diff --git a/openspec/changes/archive/2026-01-11-enhance-security-validation/proposal.md b/openspec/changes/archive/2026-01-11-enhance-security-validation/proposal.md deleted file mode 100644 index d6b86ac..0000000 --- a/openspec/changes/archive/2026-01-11-enhance-security-validation/proposal.md +++ /dev/null @@ -1,22 +0,0 @@ -# Change: Enhance Security Validation - -## Why - -QA review identified several security gaps that could be exploited: -1. JWT secret keys lack entropy validation, allowing weak secrets -2. File uploads only check extensions, not actual MIME types (content spoofing risk) -3. Missing CSRF protection on sensitive state-changing operations - -## What Changes - -- **user-auth**: Add JWT secret key strength validation (minimum length, entropy check) -- **user-auth**: Add CSRF token validation for sensitive operations -- **document-management**: Add file MIME type validation using magic bytes detection - -## Impact - -- Affected specs: `user-auth`, `document-management` -- Affected code: - - `backend/app/core/security.py` - JWT validation - - `backend/app/api/v1/endpoints/` - CSRF middleware - - `backend/app/services/file_service.py` - MIME validation diff --git a/openspec/changes/archive/2026-01-11-enhance-security-validation/specs/document-management/spec.md b/openspec/changes/archive/2026-01-11-enhance-security-validation/specs/document-management/spec.md deleted file mode 100644 index 92eda8b..0000000 --- a/openspec/changes/archive/2026-01-11-enhance-security-validation/specs/document-management/spec.md +++ /dev/null @@ -1,23 +0,0 @@ -## ADDED Requirements - -### Requirement: File MIME Type Validation -The system SHALL validate file content type using magic bytes detection. - -#### Scenario: Valid file with matching extension -- **WHEN** a user uploads a file -- **AND** the detected MIME type matches the file extension -- **THEN** the upload SHALL be accepted - -#### Scenario: Spoofed file extension rejected -- **WHEN** a user uploads a file with extension `.jpg` -- **AND** the actual content is detected as `application/x-executable` -- **THEN** the upload SHALL be rejected with error "File type mismatch" - -#### Scenario: Unsupported MIME type rejected -- **WHEN** a user uploads a file with an unsupported MIME type -- **THEN** the upload SHALL be rejected with error "Unsupported file type" - -#### Scenario: MIME validation bypass for trusted sources -- **WHEN** a file is uploaded from a trusted internal source -- **AND** the system is configured to allow bypass -- **THEN** MIME validation MAY be skipped diff --git a/openspec/changes/archive/2026-01-11-enhance-security-validation/specs/user-auth/spec.md b/openspec/changes/archive/2026-01-11-enhance-security-validation/specs/user-auth/spec.md deleted file mode 100644 index cf421e8..0000000 --- a/openspec/changes/archive/2026-01-11-enhance-security-validation/specs/user-auth/spec.md +++ /dev/null @@ -1,30 +0,0 @@ -## ADDED Requirements - -### Requirement: JWT Secret Validation -The system SHALL validate JWT secret key strength on startup. - -#### Scenario: Weak secret rejected -- **WHEN** the configured JWT secret is less than 32 characters -- **THEN** the system SHALL log a critical warning -- **AND** optionally refuse to start in production mode - -#### Scenario: Low entropy secret warning -- **WHEN** the JWT secret has low entropy (repeating patterns, common words) -- **THEN** the system SHALL log a security warning - -### Requirement: CSRF Protection -The system SHALL protect sensitive state-changing operations with CSRF tokens. - -#### Scenario: CSRF token required for password change -- **WHEN** a user attempts to change their password -- **AND** the request does not include a valid CSRF token -- **THEN** the request SHALL be rejected with 403 Forbidden - -#### Scenario: CSRF token required for account deletion -- **WHEN** a user attempts to delete their account or resources -- **AND** the request does not include a valid CSRF token -- **THEN** the request SHALL be rejected with 403 Forbidden - -#### Scenario: Valid CSRF token accepted -- **WHEN** a state-changing request includes a valid CSRF token -- **THEN** the request SHALL proceed normally diff --git a/openspec/changes/archive/2026-01-11-enhance-security-validation/tasks.md b/openspec/changes/archive/2026-01-11-enhance-security-validation/tasks.md deleted file mode 100644 index 0bcd6ed..0000000 --- a/openspec/changes/archive/2026-01-11-enhance-security-validation/tasks.md +++ /dev/null @@ -1,19 +0,0 @@ -## 1. JWT Secret Validation -- [x] 1.1 Add minimum secret length check (32+ characters) -- [x] 1.2 Add entropy validation for JWT secret -- [x] 1.3 Log warning on startup if secret is weak -- [x] 1.4 Write unit tests for secret validation - -## 2. CSRF Protection -- [x] 2.1 Add CSRF token generation utility -- [x] 2.2 Add CSRF validation middleware -- [x] 2.3 Apply to sensitive endpoints (password change, delete operations) -- [x] 2.4 Update frontend to include CSRF token in requests -- [x] 2.5 Write integration tests for CSRF validation - -## 3. MIME Type Validation -- [x] 3.1 Add python-magic or similar library for MIME detection -- [x] 3.2 Implement magic bytes validation in file upload service -- [x] 3.3 Reject files where extension doesn't match actual content -- [x] 3.4 Add configurable allowed MIME types per file category -- [x] 3.5 Write unit tests for MIME validation diff --git a/openspec/changes/archive/2026-01-11-optimize-query-performance/proposal.md b/openspec/changes/archive/2026-01-11-optimize-query-performance/proposal.md deleted file mode 100644 index 670dc06..0000000 --- a/openspec/changes/archive/2026-01-11-optimize-query-performance/proposal.md +++ /dev/null @@ -1,19 +0,0 @@ -# Change: Optimize Database Query Performance - -## Why - -QA review identified N+1 query patterns in project member listing and related endpoints. When loading a project with many members, each member triggers a separate database query, causing significant performance degradation. - -## What Changes - -- Implement eager loading (joinedload) for project member relationships -- Add query batching for related entity loading -- Add database query logging in development mode for detection - -## Impact - -- Affected specs: `resource-management` -- Affected code: - - `backend/app/services/project_service.py` - Member loading - - `backend/app/api/v1/endpoints/projects.py` - Query optimization - - `backend/app/models/` - Relationship configurations diff --git a/openspec/changes/archive/2026-01-11-optimize-query-performance/specs/resource-management/spec.md b/openspec/changes/archive/2026-01-11-optimize-query-performance/specs/resource-management/spec.md deleted file mode 100644 index 207b2e4..0000000 --- a/openspec/changes/archive/2026-01-11-optimize-query-performance/specs/resource-management/spec.md +++ /dev/null @@ -1,19 +0,0 @@ -## ADDED Requirements - -### Requirement: Optimized Relationship Loading -The system SHALL use efficient query patterns to avoid N+1 query problems when loading related entities. - -#### Scenario: Project member list loading -- **WHEN** loading a project with its members -- **THEN** the system SHALL load all members in at most 2 database queries -- **AND** NOT one query per member - -#### Scenario: Task assignee loading -- **WHEN** loading a list of tasks with their assignees -- **THEN** the system SHALL batch load assignee details -- **AND** NOT query each assignee individually - -#### Scenario: Query count monitoring -- **WHEN** running in development mode -- **THEN** the system SHALL log query counts per request -- **AND** warn when query count exceeds threshold (e.g., 10 queries) diff --git a/openspec/changes/archive/2026-01-11-optimize-query-performance/tasks.md b/openspec/changes/archive/2026-01-11-optimize-query-performance/tasks.md deleted file mode 100644 index bbe0b09..0000000 --- a/openspec/changes/archive/2026-01-11-optimize-query-performance/tasks.md +++ /dev/null @@ -1,53 +0,0 @@ -## 1. Query Analysis -- [x] 1.1 Enable SQLAlchemy query logging in development -- [x] 1.2 Identify all N+1 query patterns -- [x] 1.3 Document current query counts per endpoint - -## 2. Optimization Implementation -- [x] 2.1 Add joinedload for project member relationships -- [x] 2.2 Add selectinload for task assignee relationships -- [x] 2.3 Implement batch loading for user details -- [x] 2.4 Add appropriate indexes if missing - -## 3. Verification -- [x] 3.1 Benchmark before/after query counts -- [x] 3.2 Write performance regression tests -- [x] 3.3 Document optimization patterns for future reference - ---- - -## Implementation Summary - -### Changes Made - -1. **Query Monitoring Module** (`app/core/query_monitor.py`) - - Added `QueryCounter` context manager for counting queries per request - - Integrated SQLAlchemy event listeners for query logging - - Added threshold-based warnings when query count exceeds limit - - Configurable via `QUERY_LOGGING` and `QUERY_COUNT_THRESHOLD` settings - -2. **Configuration Updates** (`app/core/config.py`) - - Added `DEBUG`, `QUERY_LOGGING`, `QUERY_COUNT_THRESHOLD` settings - -3. **Project Router Optimizations** (`app/api/projects/router.py`) - - `list_projects_in_space`: Added `joinedload` for owner, space, department; `selectinload` for tasks - - `list_project_members`: Added `joinedload` for user (with department) and added_by_user - -4. **Task Router Optimizations** (`app/api/tasks/router.py`) - - `list_tasks`: Added `selectinload` for assignee, status, creator, subtasks, custom_values - - `list_subtasks`: Added `selectinload` for assignee, status, creator, subtasks - -5. **Performance Tests** (`tests/test_query_performance.py`) - - Test cases for project member list optimization - - Test cases for project list optimization - - Test cases for task list optimization - - Test cases for subtask list optimization - -### Query Count Improvements - -| Endpoint | Before (N members/tasks) | After | -|----------|-------------------------|-------| -| `/api/projects/{id}/members` | 1 + 2N queries | 2-3 queries | -| `/api/spaces/{id}/projects` | 1 + 4N queries | 4-5 queries | -| `/api/projects/{id}/tasks` | 1 + 4N queries | 5-6 queries | -| `/api/tasks/{id}/subtasks` | 1 + 4N queries | 4-5 queries | diff --git a/openspec/changes/archive/2026-01-11-update-api-consistency/design.md b/openspec/changes/archive/2026-01-11-update-api-consistency/design.md deleted file mode 100644 index e0a8587..0000000 --- a/openspec/changes/archive/2026-01-11-update-api-consistency/design.md +++ /dev/null @@ -1,30 +0,0 @@ -## Context -The current API behavior is functionally correct but has ambiguities that cause client confusion: WebSocket handshake messaging differs from inline docs, 409 conflict responses are inconsistent for clients, and workload heatmap defaults vary based on calendar edge cases (Sunday). - -## Goals / Non-Goals -- Goals: - - Make WebSocket auth expectations explicit and consistent. - - Provide a stable, machine-readable conflict response for optimistic locking. - - Define predictable workload heatmap defaults and edge-case handling. -- Non-Goals: - - Full API version migration (/api to /api/v1). - - Redesign of workload UI or heatmap visualization. - -## Decisions -- WebSocket auth handshake: keep first-message authentication; do not require a pre-auth banner. Add explicit invalid-token error message prior to close. -- Optimistic locking: return a standardized 409 payload containing a human-readable message plus current/provided version fields. -- Workload defaults: show all accessible users by default (including zero workload); support `hide_empty=true` to exclude them. When `week_start` is omitted and today is Sunday, extend the default week window to include the upcoming week to avoid empty default views. -- Caching: include `hide_empty` in cache key and cache the default path to keep latency stable. - -## Risks / Trade-offs -- Returning more users by default may increase response sizes; cache should offset this. -- Sunday window extension changes semantics of "current week"; clients must understand the rule. -- Conflict payload change may require frontend updates if it expects a string-only detail. - -## Migration Plan -- Update specs first, then implement backend changes. -- Update frontend only if new defaults or conflict payloads require parsing changes. -- Add regression tests for WebSocket auth invalid token, conflict payload shape, and heatmap defaults. - -## Open Questions -- Should we add an explicit `auth_required` message before waiting for the auth payload (client UX improvement), or keep the minimal handshake? diff --git a/openspec/changes/archive/2026-01-11-update-api-consistency/proposal.md b/openspec/changes/archive/2026-01-11-update-api-consistency/proposal.md deleted file mode 100644 index 6049866..0000000 --- a/openspec/changes/archive/2026-01-11-update-api-consistency/proposal.md +++ /dev/null @@ -1,14 +0,0 @@ -# Change: Update API Consistency for WebSocket Auth, Workload Defaults, and Task Locking - -## Why -Several API behaviors are currently ambiguous or inconsistent (WebSocket auth handshake messaging, optimistic locking conflict payloads, and workload heatmap defaults). These gaps can confuse clients and reduce product completeness. - -## What Changes -- Clarify WebSocket authentication error handling and client expectations. -- Standardize optimistic locking conflict responses for task updates. -- Define workload heatmap default inclusion rules and Sunday week-window behavior. -- Align workload heatmap `hide_empty` defaults and caching behavior. - -## Impact -- Affected specs: user-auth, task-management, resource-management -- Affected code: backend WebSocket router, task update endpoint, workload service/router, workload cache; frontend workload views may require adjustments depending on default behavior. diff --git a/openspec/changes/archive/2026-01-11-update-api-consistency/specs/resource-management/spec.md b/openspec/changes/archive/2026-01-11-update-api-consistency/specs/resource-management/spec.md deleted file mode 100644 index ed46780..0000000 --- a/openspec/changes/archive/2026-01-11-update-api-consistency/specs/resource-management/spec.md +++ /dev/null @@ -1,35 +0,0 @@ -## MODIFIED Requirements -### Requirement: Workload Heatmap UI -The system SHALL provide a visual workload heatmap interface for managers. - -#### Scenario: View workload heatmap -- **GIVEN** user is logged in as manager or admin -- **WHEN** user navigates to /workload page without filters -- **THEN** system displays a heatmap showing all accessible users' workload -- **AND** users with zero workload are included by default -- **AND** each user cell is color-coded by load level (green/yellow/red) - -#### Scenario: Hide empty workloads -- **GIVEN** user is viewing the workload page -- **WHEN** user enables the hide_empty filter -- **THEN** the heatmap excludes users with zero workload - -#### Scenario: Navigate between weeks -- **GIVEN** user is viewing the workload page -- **WHEN** user clicks previous/next week buttons -- **THEN** the heatmap updates to show that week's workload data - -#### Scenario: Default week window on Sunday -- **GIVEN** today is Sunday and user opens workload page without selecting week_start -- **THEN** the default heatmap window includes tasks due in the upcoming week - -#### Scenario: View user workload details -- **GIVEN** user is viewing the workload heatmap -- **WHEN** user clicks on a specific user's cell -- **THEN** a modal/drawer opens showing that user's task breakdown -- **AND** tasks show title, project, time estimate, and due date - -#### Scenario: Filter by department -- **GIVEN** user is a system admin -- **WHEN** user selects a department from the filter -- **THEN** the heatmap shows only users from that department diff --git a/openspec/changes/archive/2026-01-11-update-api-consistency/specs/task-management/spec.md b/openspec/changes/archive/2026-01-11-update-api-consistency/specs/task-management/spec.md deleted file mode 100644 index f9439e8..0000000 --- a/openspec/changes/archive/2026-01-11-update-api-consistency/specs/task-management/spec.md +++ /dev/null @@ -1,16 +0,0 @@ -## MODIFIED Requirements -### Requirement: Optimistic Locking for Task Updates -The system SHALL use optimistic locking to prevent concurrent update conflicts on tasks. - -#### Scenario: Concurrent update detected -- **WHEN** user A and user B both load task at version 1 -- **WHEN** user A saves changes, incrementing version to 2 -- **WHEN** user B attempts to save with version 1 -- **THEN** system returns 409 Conflict error -- **AND** response includes a human-readable message instructing refresh and retry -- **AND** response includes the current and provided version numbers - -#### Scenario: Sequential updates succeed -- **WHEN** user loads task at version N -- **WHEN** user saves changes with correct version N -- **THEN** system accepts update and increments version to N+1 diff --git a/openspec/changes/archive/2026-01-11-update-api-consistency/specs/user-auth/spec.md b/openspec/changes/archive/2026-01-11-update-api-consistency/specs/user-auth/spec.md deleted file mode 100644 index d77faa2..0000000 --- a/openspec/changes/archive/2026-01-11-update-api-consistency/specs/user-auth/spec.md +++ /dev/null @@ -1,18 +0,0 @@ -## MODIFIED Requirements -### Requirement: Secure WebSocket Authentication -The system SHALL authenticate WebSocket connections without exposing tokens in URL query parameters. - -#### Scenario: WebSocket connection with token in first message -- **WHEN** client connects to WebSocket endpoint without a query token -- **THEN** server waits for authentication message containing JWT token -- **THEN** server validates token before accepting further messages -- **THEN** server sends an authentication acknowledgment message - -#### Scenario: WebSocket connection with invalid token -- **WHEN** client sends an invalid or expired token -- **THEN** server sends an error message indicating invalid or expired token -- **THEN** server closes the connection with an authentication error code - -#### Scenario: WebSocket connection timeout without authentication -- **WHEN** client connects but does not send authentication within 10 seconds -- **THEN** server closes the connection with appropriate error code diff --git a/openspec/changes/archive/2026-01-11-update-api-consistency/tasks.md b/openspec/changes/archive/2026-01-11-update-api-consistency/tasks.md deleted file mode 100644 index 9a78ab0..0000000 --- a/openspec/changes/archive/2026-01-11-update-api-consistency/tasks.md +++ /dev/null @@ -1,34 +0,0 @@ -## 1. Implementation -- [x] 1.1 Update WebSocket auth spec and align server handshake/error messaging with the agreed behavior -- [x] 1.2 Update optimistic locking conflict response spec and implement standardized payload -- [x] 1.3 Update workload heatmap defaults (hide_empty, week window) and cache behavior -- [x] 1.4 Update frontend workload views and API handling if required by new defaults -- [x] 1.5 Add/adjust tests for WebSocket auth, conflict responses, and workload heatmap defaults - ---- - -## Implementation Summary - -### Changes Made (commit f5f870d) - -1. **WebSocket Auth** (`backend/app/api/websocket/router.py`) - - Standardized error codes: 4001 (invalid token), 4003 (access denied), 4004 (not found) - - Clear error reasons in WebSocket close messages - -2. **Optimistic Locking** (`backend/app/api/tasks/router.py`) - - 409 Conflict response with standardized payload: - - `error: "conflict"` - - `message`, `current_version`, `provided_version`, `your_version` - -3. **Workload Heatmap** (`backend/app/api/workload/router.py`, `workload_service.py`) - - `hide_empty=True` as default - - Caching only when `hide_empty=True` - - Week bounds handle Sunday correctly (returns previous Monday) - -4. **Frontend** (already aligned) - - `workload.ts`: `hideEmpty: boolean = true` default - - TaskDetailModal, GanttChart, CalendarView: Handle 409 conflict with conflict banner UI - -5. **Tests** (`backend/tests/test_workload.py`) - - Week bounds tests including Sunday handling - - Load level calculation tests diff --git a/openspec/project.md b/openspec/project.md deleted file mode 100644 index fd471f9..0000000 --- a/openspec/project.md +++ /dev/null @@ -1,78 +0,0 @@ -# Project Context - -## Purpose -Cross-departmental project management system serving as a Single Source of Truth to: -- Reduce engineering time-reporting burden -- Provide real-time resource load and project progress analysis -- Standardize task tracking across departments - -Target users: -- **Engineers**: Simplified reporting, automated reminders, personal task dashboard -- **Unit Managers**: Team workload visibility, resource allocation heatmaps, multi-project health boards -- **Admin/PMO**: Centralized data, automated weekly reports, document version control - -## Tech Stack -- **Frontend**: React.js (recommended for complex admin dashboards) -- **Backend**: Python with FastAPI (enables future AI/ML integration for risk prediction) -- **Database**: MySQL (relational structure for complex project hierarchies and dependencies) -- **Cache & Real-time**: Redis (push notifications, task state locking) -- **Real-time Sync**: WebSocket for live collaboration -- **Authentication**: Enterprise Windows AD/LDAP via SSO (https://pj-auth-api.vercel.app) -- **Environment Management**: Conda (for Python dependency isolation) - -## Project Conventions - -### Code Style -- [To be defined - React/TypeScript conventions for frontend] -- [To be defined - Python/FastAPI conventions for backend] - -### Database Naming -- **Table Prefix**: All tables must use `pjctrl_` prefix (e.g., `pjctrl_users`, `pjctrl_projects`, `pjctrl_tasks`) -- This ensures isolation in shared database environments and prevents migration conflicts - -### Architecture Patterns -- 3-Tier Architecture (Presentation, Application, Data layers) -- Designed for future extensibility (MES/ERP integration capability) -- Multi-level task hierarchy: Space > Project > Task > Sub-task - -### Testing Strategy -- [To be defined] - -### Git Workflow -- [To be defined] - -## Domain Context -**Semiconductor/Manufacturing Industry** -- Custom fields support domain-specific data: package types, machine numbers, expected yield rates -- Document encryption (AES-256) for sensitive semiconductor drawings -- User watermarking on downloads for IP protection -- Department-level access control (e.g., factory ops cannot view R&D projects) - -## Important Constraints -- **Security**: Enterprise AD/LDAP integration with fine-grained permissions -- **Audit Trail**: All changes logged (deadline modifications, file deletions, etc.) -- **Data Sensitivity**: Encrypted storage for sensitive technical documents -- **Real-time Requirements**: WebSocket-based live updates for concurrent editing -- **Database Isolation**: All tables MUST use a designated prefix (e.g., `pjctrl_`) to prevent conflicts during sync/migration with other tables in shared database environments - -## External Dependencies -- **SSO Auth API**: https://pj-auth-api.vercel.app (Windows AD integration) -- **On-premise Storage**: NAS for file attachments -- Future: MES/ERP system integration - -## Database Connection -- **Host**: mysql.theaken.com -- **Port**: 33306 -- **User**: A060 -- **Database**: db_A060 -- **Table Prefix**: `pjctrl_` - -## System Administrator -- **Email**: ymirliu@panjit.com.tw -- **Role**: super_admin (不可刪除或降級) - -## Core Data Model (ERD) -- **User**: ID, Name, Department, Role, Skills, Capacity -- **Project**: ID, Title, Owner, Budget, Timeline, Security_Level -- **Task**: ID, Project_ID, Assignee, Priority, Status, Original_Estimate, Time_Spent, Blocker_Flag -- **Attachment**: ID, Task_ID, Version, File_Path diff --git a/openspec/specs/audit-trail/spec.md b/openspec/specs/audit-trail/spec.md deleted file mode 100644 index 4839a60..0000000 --- a/openspec/specs/audit-trail/spec.md +++ /dev/null @@ -1,186 +0,0 @@ -# Audit Trail - -## Purpose - -系統級稽核追蹤,記錄所有關鍵變更操作供合規與追溯需求。 - -## Requirements - -### Requirement: Change Logging -系統 SHALL 記錄所有關鍵變更操作,包含誰在何時改了什麼。 - -#### Scenario: 任務欄位變更記錄 -- **GIVEN** 使用者修改任務的任何欄位(如截止日期、狀態、指派者) -- **WHEN** 變更儲存成功 -- **THEN** 系統記錄變更前後的值 -- **AND** 記錄操作者、時間、IP 位址 - -#### Scenario: 專案設定變更記錄 -- **GIVEN** 管理者修改專案設定 -- **WHEN** 設定變更儲存 -- **THEN** 系統記錄所有變更的設定項目 -- **AND** 記錄操作者與時間 - -#### Scenario: 權限變更記錄 -- **GIVEN** 管理者修改使用者權限或角色 -- **WHEN** 權限變更生效 -- **THEN** 系統記錄權限變更詳情 -- **AND** 標記為高敏感度操作 - -### Requirement: Delete Operations Tracking -系統 SHALL 追蹤所有刪除操作,支援軟刪除與追溯。 - -#### Scenario: 任務刪除記錄 -- **GIVEN** 使用者刪除任務 -- **WHEN** 刪除操作執行 -- **THEN** 系統執行軟刪除(標記 is_deleted = true) -- **AND** 記錄刪除操作與原因 - -#### Scenario: 附件刪除記錄 -- **GIVEN** 使用者刪除附件 -- **WHEN** 刪除操作執行 -- **THEN** 系統保留檔案於存檔區 -- **AND** 記錄刪除操作詳情 - -### Requirement: Audit Log Immutability -系統 SHALL 確保稽核日誌不可竄改。 - -#### Scenario: 日誌寫入 -- **GIVEN** 需要記錄稽核事件 -- **WHEN** 日誌寫入 -- **THEN** 日誌記錄不可被修改或刪除 -- **AND** 包含校驗碼確保完整性 - -#### Scenario: 日誌完整性驗證 -- **GIVEN** 稽核人員需要驗證日誌完整性 -- **WHEN** 執行完整性檢查 -- **THEN** 系統驗證所有日誌記錄的校驗碼 -- **AND** 報告任何異常 - -### Requirement: Audit Query Interface -系統 SHALL 提供稽核查詢介面供授權人員使用。 - -#### Scenario: 依時間範圍查詢 -- **GIVEN** 稽核人員需要查詢特定時間範圍的操作 -- **WHEN** 設定時間範圍並執行查詢 -- **THEN** 顯示該時間範圍內的所有稽核記錄 - -#### Scenario: 依操作者查詢 -- **GIVEN** 稽核人員需要查詢特定使用者的操作歷史 -- **WHEN** 選擇使用者並執行查詢 -- **THEN** 顯示該使用者的所有操作記錄 - -#### Scenario: 依資源查詢 -- **GIVEN** 稽核人員需要查詢特定任務或專案的變更歷史 -- **WHEN** 選擇資源並執行查詢 -- **THEN** 顯示該資源的完整變更歷程 - -#### Scenario: 稽核報告匯出 -- **GIVEN** 稽核人員需要匯出稽核報告 -- **WHEN** 選擇匯出格式(CSV/PDF) -- **THEN** 系統生成報告檔案供下載 - -### Requirement: Sensitive Operation Alerts -系統 SHALL 對高敏感度操作發送即時警示。 - -#### Scenario: 權限提升警示 -- **GIVEN** 使用者被授予管理員權限 -- **WHEN** 權限變更生效 -- **THEN** 系統發送警示給安全管理員 - -#### Scenario: 大量刪除警示 -- **GIVEN** 使用者在短時間內刪除大量資料 -- **WHEN** 偵測到異常刪除模式 -- **THEN** 系統發送警示並暫停操作 - -#### Scenario: 異常登入警示 -- **GIVEN** 偵測到異常登入行為(如異地登入、非工作時間登入) -- **WHEN** 異常行為發生 -- **THEN** 系統記錄並發送警示 - -### Requirement: Security Event Logging -The system SHALL record failed access attempts for security monitoring and intrusion detection. - -#### Scenario: Permission denied logged -- **WHEN** server returns 403 Forbidden for a resource access attempt -- **THEN** audit log entry is created with event_type "security.access_denied" -- **AND** entry includes user_id, resource_type, and attempted_action - -#### Scenario: Repeated auth failures logged -- **WHEN** same IP has 5+ failed authentication attempts in 10 minutes -- **THEN** audit log entry is created with event_type "security.suspicious_auth_pattern" -- **AND** entry includes IP address and failure count -- **AND** alert is generated for security administrators - -### Requirement: Detailed Health Endpoint Security -The detailed system health endpoint SHALL require admin authentication to prevent information disclosure. - -#### Scenario: Admin accesses detailed health -- **WHEN** system administrator requests GET /health/detailed -- **THEN** full system status including connection pools is returned - -#### Scenario: Non-admin accesses detailed health -- **WHEN** non-admin user or unauthenticated request to GET /health/detailed -- **THEN** request is rejected with 401 Unauthorized or 403 Forbidden - -## Data Model - -``` -pjctrl_audit_logs -├── id: UUID (PK) -├── event_type: VARCHAR(50) -│ └── 'task.update', 'task.delete', 'project.update', 'user.permission_change', etc. -├── resource_type: VARCHAR(50) -│ └── 'task', 'project', 'user', 'attachment', 'trigger', etc. -├── resource_id: UUID -├── user_id: UUID (FK -> users) -├── action: ENUM('create', 'update', 'delete', 'restore', 'login', 'logout') -├── changes: JSON -│ └── { "field": "due_date", "old_value": "2024-01-15", "new_value": "2024-01-20" } -├── metadata: JSON -│ └── { "ip_address": "192.168.1.100", "user_agent": "...", "session_id": "..." } -├── sensitivity_level: ENUM('low', 'medium', 'high', 'critical') -├── checksum: VARCHAR(64) (SHA-256 of record content) -├── created_at: TIMESTAMP (immutable) -└── INDEX idx_audit_user (user_id, created_at) -└── INDEX idx_audit_resource (resource_type, resource_id, created_at) -└── INDEX idx_audit_time (created_at) - -pjctrl_audit_alerts -├── id: UUID (PK) -├── audit_log_id: UUID (FK -> audit_logs) -├── alert_type: VARCHAR(50) -├── recipients: JSON (array of user IDs) -├── message: TEXT -├── is_acknowledged: BOOLEAN DEFAULT false -├── acknowledged_by: UUID (FK -> users) -├── acknowledged_at: TIMESTAMP -└── created_at: TIMESTAMP -``` - -## Event Types Reference - -| 事件類型 | 說明 | 敏感度 | -|---------|------|--------| -| task.create | 建立任務 | low | -| task.update | 更新任務 | low | -| task.delete | 刪除任務 | medium | -| task.assign | 指派任務 | low | -| task.blocker | 標記阻礙 | medium | -| project.create | 建立專案 | medium | -| project.update | 更新專案 | medium | -| project.delete | 刪除專案 | high | -| user.login | 使用者登入 | low | -| user.logout | 使用者登出 | low | -| user.permission_change | 權限變更 | critical | -| attachment.upload | 上傳附件 | low | -| attachment.download | 下載附件 | low | -| attachment.delete | 刪除附件 | medium | - -## Technical Notes - -- 稽核日誌表設計為 append-only,不允許 UPDATE 或 DELETE -- 使用資料庫觸發器或應用層中間件自動記錄變更 -- 日誌保留期限依法規要求設定(建議至少 7 年) -- 考慮使用時間序列資料庫(如 TimescaleDB)處理大量日誌 -- Checksum 計算包含:event_type + resource_id + user_id + changes + created_at diff --git a/openspec/specs/automation/spec.md b/openspec/specs/automation/spec.md deleted file mode 100644 index a9d6c42..0000000 --- a/openspec/specs/automation/spec.md +++ /dev/null @@ -1,231 +0,0 @@ -# Automation - -## Purpose - -自動化系統,提供觸發器與自動報告生成功能。 -## Requirements -### Requirement: Trigger-Based Automation -系統 SHALL 支援觸發器 (Triggers),當特定條件滿足時自動執行動作。 - -#### Scenario: 狀態變更觸發通知 -- **GIVEN** 專案設定了「當任務狀態變更為待測試時,通知設備工程師」的觸發器 -- **WHEN** 任務狀態變更為「待測試」 -- **THEN** 系統自動發送通知給指定的設備工程師群組 - -#### Scenario: 截止日期觸發提醒 -- **GIVEN** 專案設定了「截止日前 3 天自動提醒」的觸發器 -- **WHEN** 任務距離截止日還有 3 天 -- **THEN** 系統自動發送提醒給任務指派者 - -#### Scenario: 建立觸發器 -- **GIVEN** 專案管理者需要建立自動化規則 -- **WHEN** 管理者設定觸發條件與動作 -- **THEN** 系統儲存觸發器規則 -- **AND** 規則立即生效 - -### Requirement: Trigger Conditions -系統 SHALL 支援多種觸發條件類型,包含欄位變更、時間條件、以及 AND-only 複合條件。欄位變更條件 SHALL 支援 `status_id`、`assignee_id`、`priority`、`due_date`、`start_date` 與 `custom_fields`(含 formula),並支援 `equals`、`not_equals`、`changed_to`、`changed_from`、`before`、`after`、`in` 運算子。日期欄位的 `in` SHALL 使用 `{start, end}` 區間且包含邊界。 - -#### Scenario: 欄位變更條件 -- **GIVEN** 觸發器設定為「當 Status 欄位變更為特定值」 -- **WHEN** 任務的 Status 欄位變更為該值 -- **THEN** 觸發器被觸發 - -#### Scenario: 時間條件 -- **GIVEN** 觸發器設定為「每週五下午 4:00」 -- **WHEN** 系統時間達到設定時間 -- **THEN** 觸發器被觸發 - -#### Scenario: 複合條件 -- **GIVEN** 觸發器設定為「當 Status = 完成 且 Priority = 高」 -- **WHEN** 任務同時滿足兩個條件 -- **THEN** 觸發器被觸發 - -#### Scenario: 日期區間條件 -- **GIVEN** 觸發器設定為「due_date in {start, end}」且區間為含邊界 -- **WHEN** 任務的 due_date 落在該區間內 -- **THEN** 觸發器被觸發 - -#### Scenario: 自訂欄位(公式)條件 -- **GIVEN** 觸發器設定為「custom_fields(公式欄位) equals 目標值」 -- **WHEN** 任務的公式欄位計算值符合目標值 -- **THEN** 觸發器被觸發 - -#### Scenario: Cron 表達式觸發 -- **GIVEN** 觸發器設定為 cron 表達式 (如 `0 9 * * 1` 每週一早上 9 點) -- **WHEN** 系統時間匹配 cron 表達式 -- **THEN** 系統評估並執行該觸發器 -- **AND** 記錄執行結果至 trigger_logs - -#### Scenario: 截止日期提醒 -- **GIVEN** 觸發器設定為「截止日前 N 天提醒」 -- **WHEN** 任務距離截止日剩餘 N 天 -- **THEN** 系統發送提醒通知給任務指派者 -- **AND** 每個任務每個提醒設定只觸發一次 - -### Requirement: Trigger Actions -系統 SHALL 支援多種觸發動作類型。通知動作 SHALL 支援單人與群組目標(`assignee`、`creator`、`project_owner`、`project_members`、`department:`、`role:`、`user:`),並對收件人去重且排除觸發者本人。 - -#### Scenario: 發送通知動作 -- **GIVEN** 觸發器動作設定為發送通知 -- **WHEN** 觸發器被觸發 -- **THEN** 系統發送通知給指定對象 -- **AND** 通知內容可使用變數(如任務名稱、指派者) - -#### Scenario: 群組通知目標 -- **GIVEN** 觸發器通知目標為 `project_members` 或 `department:` 或 `role:` -- **WHEN** 觸發器被觸發 -- **THEN** 系統通知所有對應成員 -- **AND** 去除重複收件人 -- **AND** 排除觸發者本人 - -#### Scenario: 更新欄位動作 -- **GIVEN** 觸發器動作設定為更新欄位 -- **WHEN** 觸發器被觸發 -- **THEN** 系統自動更新指定欄位的值 - -#### Scenario: 指派任務動作 -- **GIVEN** 觸發器動作設定為自動指派 -- **WHEN** 觸發器被觸發 -- **THEN** 系統自動將任務指派給指定人員 - -### Requirement: Automated Weekly Report -系統 SHALL 每週五下午 4:00 自動彙整完整任務清單,發送給已訂閱的專案成員(含跨部門成員)。週報內容 SHALL 以收件人為 owner 或 project member 的專案為範圍。 - -#### Scenario: 週報內容完整清單 -- **GIVEN** 週報生成中 -- **WHEN** 系統彙整資料 -- **THEN** 週報包含各專案的: - - 本週已完成任務清單(含 completed_at, assignee_name) - - 進行中任務清單(含 assignee_name, due_date) - - 逾期任務警示(含 due_date, days_overdue) - - 阻礙中任務清單(含 blocker_reason, blocked_since) - - 下週預計完成任務(含 due_date, assignee_name) -- **AND** 不設任務數量上限 - -#### Scenario: 週報收件人範圍 -- **GIVEN** 使用者為專案成員且已開啟週報訂閱 -- **WHEN** 週報排程執行 -- **THEN** 使用者收到週報 - -#### Scenario: 阻礙任務識別 -- **GIVEN** 任務有未解除的 Blocker 記錄 -- **WHEN** 週報查詢阻礙任務 -- **THEN** 系統查詢 Blocker 表 resolved_at IS NULL 的任務 -- **AND** 顯示阻礙原因與開始時間 - -#### Scenario: 下週預計任務 -- **GIVEN** 任務的 due_date 在下週範圍內 -- **WHEN** 週報查詢下週預計任務 -- **THEN** 系統篩選 due_date >= 下週一 且 < 下週日 -- **AND** 排除已完成狀態的任務 - -### Requirement: Formula Field Cycle Prevention -The system SHALL detect and prevent circular references in custom field formulas to avoid infinite calculation loops. - -#### Scenario: Formula self-reference rejected -- **WHEN** user creates a formula field that references itself -- **THEN** system rejects with 400 Bad Request -- **THEN** error message indicates self-reference is not allowed - -#### Scenario: Formula circular reference chain rejected -- **WHEN** user creates formula where Field A references Field B and Field B references Field A -- **THEN** system rejects with 400 Bad Request -- **THEN** error message includes the reference cycle path - -#### Scenario: Valid formula references accepted -- **WHEN** user creates formula referencing other fields without cycles -- **THEN** system saves the formula and calculates values correctly - -### Requirement: Trigger Execution Retry -The system SHALL retry failed trigger executions with exponential backoff to handle transient failures. - -#### Scenario: Trigger succeeds after retry -- **WHEN** trigger execution fails due to transient error -- **THEN** system retries after 1 second delay -- **WHEN** retry succeeds -- **THEN** trigger is marked as successful in execution log - -#### Scenario: Trigger exhausts retries -- **WHEN** trigger execution fails 3 consecutive times -- **THEN** system marks trigger execution as permanently failed -- **THEN** system sends alert notification to system administrators -- **THEN** execution log contains all retry attempts with error details - -#### Scenario: Non-retryable error -- **WHEN** trigger fails with validation or permission error (4xx) -- **THEN** system does not retry and marks as failed immediately -- **THEN** error is logged with appropriate categorization - -### Requirement: Weekly Report Subscription -系統 SHALL 提供週報訂閱管理功能,讓使用者手動開啟或關閉週報。 - -#### Scenario: 開啟週報訂閱 -- **GIVEN** 使用者尚未訂閱週報 -- **WHEN** 使用者在 MySettings 開啟週報訂閱 -- **THEN** 系統建立或啟用該使用者的 weekly 訂閱 - -#### Scenario: 關閉週報訂閱 -- **GIVEN** 使用者已訂閱週報 -- **WHEN** 使用者在 MySettings 關閉週報訂閱 -- **THEN** 系統停用該使用者的 weekly 訂閱 -- **AND** 後續排程不再發送週報 - -#### Scenario: 未訂閱預設行為 -- **GIVEN** 使用者未開啟週報訂閱 -- **WHEN** 週報排程執行 -- **THEN** 使用者不會收到週報 - -## Data Model - -``` -pjctrl_triggers -├── id: UUID (PK) -├── project_id: UUID (FK -> projects) -├── name: VARCHAR(200) -├── description: TEXT -├── trigger_type: ENUM('field_change', 'schedule', 'creation') -├── conditions: JSON -│ └── { "field": "status", "operator": "equals", "value": "testing" } -├── actions: JSON -│ └── [{ "type": "notify", "target": "group:equipment_engineers" }] -├── is_active: BOOLEAN DEFAULT true -├── created_by: UUID (FK -> users) -├── created_at: TIMESTAMP -└── updated_at: TIMESTAMP - -pjctrl_trigger_logs -├── id: UUID (PK) -├── trigger_id: UUID (FK -> triggers) -├── task_id: UUID (FK -> tasks, nullable) -├── executed_at: TIMESTAMP -├── status: ENUM('success', 'failed') -└── error_message: TEXT - -pjctrl_scheduled_reports -├── id: UUID (PK) -├── report_type: ENUM('weekly', 'monthly', 'custom') -├── recipient_id: UUID (FK -> users) -├── schedule_cron: VARCHAR(50) -├── last_sent_at: TIMESTAMP -├── next_run_at: TIMESTAMP -├── is_active: BOOLEAN DEFAULT true -├── email_enabled: BOOLEAN DEFAULT false (Email 發送開關) -└── created_at: TIMESTAMP - -pjctrl_report_history -├── id: UUID (PK) -├── scheduled_report_id: UUID (FK -> scheduled_reports) -├── generated_at: TIMESTAMP -├── content: JSON -├── sent_to: JSON (array of user IDs) -├── channels_used: JSON (e.g., ["in_app"] or ["in_app", "email"]) -├── email_status: ENUM('sent', 'skipped', 'failed', null) -└── status: ENUM('sent', 'failed') -``` - -## Technical Notes - -- 使用 Celery + Redis 處理排程任務 -- 觸發器執行採用非同步處理,避免阻塞主流程 -- 所有觸發器執行都記錄日誌供追蹤 diff --git a/openspec/specs/collaboration/spec.md b/openspec/specs/collaboration/spec.md deleted file mode 100644 index 299b653..0000000 --- a/openspec/specs/collaboration/spec.md +++ /dev/null @@ -1,178 +0,0 @@ -# Collaboration - -## Purpose - -協作功能系統,提供任務內討論、@提及通知與阻礙處理機制。 -## Requirements -### Requirement: Task Comments -系統 SHALL 支援任務內部的討論線索,減少 Email 往返。 - -#### Scenario: 新增留言 -- **GIVEN** 使用者擁有任務的存取權限 -- **WHEN** 使用者在任務中新增留言 -- **THEN** 系統儲存留言並顯示在討論區 -- **AND** 記錄留言者與時間戳記 - -#### Scenario: 回覆留言 -- **GIVEN** 任務已有留言 -- **WHEN** 使用者回覆特定留言 -- **THEN** 系統建立巢狀回覆結構 -- **AND** 通知原留言者 - -#### Scenario: 編輯留言 -- **GIVEN** 使用者是留言的作者 -- **WHEN** 使用者編輯自己的留言 -- **THEN** 系統更新留言內容 -- **AND** 標示「已編輯」及編輯時間 - -### Requirement: User Mentions -系統 SHALL 支援 @相關人員 功能,提及時發送即時通知。 - -#### Scenario: @提及通知 -- **GIVEN** 使用者在留言中使用 @username 提及某人 -- **WHEN** 留言送出 -- **THEN** 被提及者收到即時通知 -- **AND** 通知包含任務連結與留言摘要 - -#### Scenario: @提及自動完成 -- **GIVEN** 使用者輸入 @ 符號 -- **WHEN** 使用者繼續輸入 -- **THEN** 系統顯示符合的使用者名單供選擇 -- **AND** 可用鍵盤或滑鼠選擇 - -### Requirement: Blocker Management -系統 SHALL 提供阻礙 (Blocker) 機制,強制要求主管介入排解。 - -#### Scenario: 標記阻礙 -- **GIVEN** 工程師的任務遇到阻礙無法進行 -- **WHEN** 工程師將任務標記為 "Blocked" -- **THEN** 系統設定 Blocker_Flag = true -- **AND** 強制發送即時通知給該任務所屬專案的主管 - -#### Scenario: 阻礙原因說明 -- **GIVEN** 任務被標記為 Blocked -- **WHEN** 使用者標記阻礙 -- **THEN** 系統要求填寫阻礙原因 -- **AND** 原因顯示在任務詳情與通知中 - -#### Scenario: 解除阻礙 -- **GIVEN** 主管或被指派者處理完阻礙 -- **WHEN** 使用者解除 Blocked 狀態 -- **THEN** 系統清除 Blocker_Flag -- **AND** 記錄解除時間與處理說明 - -### Requirement: Real-time Notifications -系統 SHALL 透過 Redis 推播即時通知。 - -#### Scenario: 即時通知推播 -- **GIVEN** 發生需要通知的事件(如:被指派任務、被 @提及、阻礙標記) -- **WHEN** 事件發生 -- **THEN** 系統透過 WebSocket 即時推播通知給相關使用者 -- **AND** 未讀通知顯示數量標示 - -#### Scenario: 通知已讀標記 -- **GIVEN** 使用者有未讀通知 -- **WHEN** 使用者查看通知 -- **THEN** 系統標記為已讀 -- **AND** 更新未讀數量 - -#### Scenario: 專案事件頻道分離 -- **GIVEN** 系統運作中 -- **WHEN** 發生任務變更事件 -- **THEN** 通知系統使用 `notifications:{user_id}` 頻道推送個人通知 -- **AND** 即時同步系統使用 `project:{project_id}:tasks` 頻道推送專案事件 -- **AND** 兩者互不干擾 - -### Requirement: Project Real-time Sync -系統 SHALL 提供專案級即時同步機制,讓多位使用者同時協作時能即時看到任務變更。 - -#### Scenario: 訂閱專案即時更新 -- **GIVEN** 使用者擁有專案的存取權限 -- **WHEN** 使用者進入專案看板頁面 -- **THEN** 系統自動建立 WebSocket 連線並訂閱該專案的即時更新 -- **AND** 連線使用 JWT Token 進行身份驗證 - -#### Scenario: 接收任務狀態變更 -- **GIVEN** 使用者已訂閱專案即時更新 -- **WHEN** 其他使用者在同一專案中變更任務狀態 -- **THEN** 系統透過 WebSocket 即時推送 `task_status_changed` 事件 -- **AND** 看板 UI 自動更新任務位置 - -#### Scenario: 接收任務建立事件 -- **GIVEN** 使用者已訂閱專案即時更新 -- **WHEN** 其他使用者在同一專案中建立新任務 -- **THEN** 系統透過 WebSocket 即時推送 `task_created` 事件 -- **AND** 看板 UI 自動顯示新任務 - -#### Scenario: 接收任務刪除事件 -- **GIVEN** 使用者已訂閱專案即時更新 -- **WHEN** 其他使用者在同一專案中刪除任務 -- **THEN** 系統透過 WebSocket 即時推送 `task_deleted` 事件 -- **AND** 看板 UI 自動移除該任務 - -#### Scenario: 取消訂閱專案 -- **GIVEN** 使用者已訂閱專案即時更新 -- **WHEN** 使用者離開專案看板頁面 -- **THEN** 系統自動關閉 WebSocket 連線並取消訂閱 -- **AND** 釋放相關資源 - -#### Scenario: 樂觀更新與衝突處理 -- **GIVEN** 使用者拖曳任務改變狀態 -- **WHEN** 本地先套用變更(樂觀更新)但 API 呼叫失敗 -- **THEN** 系統回滾任務到原本位置 -- **AND** 顯示錯誤訊息通知使用者 - -#### Scenario: 避免重複應用自己的事件 -- **GIVEN** 使用者變更任務狀態並收到自己發起的事件 -- **WHEN** WebSocket 收到該事件 -- **THEN** 系統識別為本地發起的變更 -- **AND** 不重複應用該變更 - -## Data Model - -``` -pjctrl_comments -├── id: UUID (PK) -├── task_id: UUID (FK -> tasks) -├── parent_comment_id: UUID (FK -> comments, nullable) -├── author_id: UUID (FK -> users) -├── content: TEXT -├── is_edited: BOOLEAN DEFAULT false -├── created_at: TIMESTAMP -└── updated_at: TIMESTAMP - -pjctrl_mentions -├── id: UUID (PK) -├── comment_id: UUID (FK -> comments) -├── mentioned_user_id: UUID (FK -> users) -├── created_at: TIMESTAMP -└── is_notified: BOOLEAN DEFAULT false - -pjctrl_notifications -├── id: UUID (PK) -├── user_id: UUID (FK -> users) -├── type: ENUM('mention', 'assignment', 'blocker', 'status_change', 'comment') -├── reference_type: VARCHAR(50) (e.g., 'task', 'comment') -├── reference_id: UUID -├── title: VARCHAR(200) -├── message: TEXT -├── is_read: BOOLEAN DEFAULT false -├── created_at: TIMESTAMP -└── read_at: TIMESTAMP - -pjctrl_blockers -├── id: UUID (PK) -├── task_id: UUID (FK -> tasks) -├── reported_by: UUID (FK -> users) -├── reason: TEXT -├── resolved_by: UUID (FK -> users, nullable) -├── resolution_note: TEXT -├── created_at: TIMESTAMP -└── resolved_at: TIMESTAMP -``` - -## Technical Notes - -- 使用 Redis Pub/Sub 處理即時通知推播 -- WebSocket 連線管理確保訊息送達 -- 離線使用者登入後補送未讀通知 diff --git a/openspec/specs/dashboard/spec.md b/openspec/specs/dashboard/spec.md deleted file mode 100644 index fd87739..0000000 --- a/openspec/specs/dashboard/spec.md +++ /dev/null @@ -1,213 +0,0 @@ -# dashboard Specification - -## Purpose -TBD - created by archiving change add-dashboard-widgets. Update Purpose after archive. -## Requirements -### Requirement: Dashboard Statistics - -The system SHALL display aggregated statistics showing the user's task overview. - -#### Scenario: User views task statistics -- Given: User is authenticated -- When: User navigates to Dashboard -- Then: System displays: - - Total tasks assigned to user - - Tasks due this week - - Overdue tasks count - - Completion rate percentage - -### Requirement: My Workload Widget - -The system SHALL display the current user's workload summary for the current week. - -#### Scenario: User views personal workload -- Given: User is authenticated -- When: User views Dashboard -- Then: System displays: - - Allocated hours vs capacity hours - - Load percentage with visual indicator - - Load level status (normal/warning/overloaded) - -### Requirement: Project Health Summary - -The system SHALL display an aggregated project health summary. - -#### Scenario: User views project health overview -- Given: User is authenticated -- When: User views Dashboard -- Then: System displays: - - Total projects count - - Healthy/At-Risk/Critical breakdown - - Average health score - - Projects with blockers count - -### Requirement: Quick Actions - -The system SHALL provide quick navigation links to common actions. - -#### Scenario: User accesses quick actions -- Given: User is authenticated -- When: User views Dashboard -- Then: System displays navigation links to: - - Spaces page - - Workload page - - Project Health page - - (Admin only) Audit page - -### Requirement: Dashboard API Endpoint - -The backend SHALL provide a single aggregated endpoint for dashboard data. - -#### Scenario: Frontend fetches dashboard data -- Given: User is authenticated -- When: Frontend requests GET /api/dashboard -- Then: Backend returns: - - User task statistics - - Current week workload summary - - Project health summary -- And: Response is optimized with single database query where possible - -### Requirement: Responsive Layout -The system SHALL provide a responsive user interface that adapts to different screen sizes for optimal usability. - -#### Scenario: Mobile sidebar behavior -- **WHEN** user accesses application on mobile device (width < 768px) -- **THEN** sidebar is hidden by default -- **THEN** hamburger menu button is visible in header -- **WHEN** user taps hamburger menu -- **THEN** sidebar slides in from left with backdrop overlay - -#### Scenario: Table responsive behavior -- **WHEN** user views task list on small screen -- **THEN** table displays with horizontal scroll or switches to card layout -- **THEN** all essential information remains accessible - -#### Scenario: Touch-friendly interactions -- **WHEN** user interacts with application on touch device -- **THEN** all interactive elements have minimum 44x44 pixel tap targets -- **THEN** sufficient spacing prevents accidental taps - -### Requirement: Complete Internationalization -The system SHALL support complete internationalization with no hardcoded text strings. - -#### Scenario: Language switching -- **WHEN** user changes language preference -- **THEN** all UI text updates to selected language -- **THEN** no untranslated strings remain visible - -#### Scenario: Date and time localization -- **WHEN** dates and times are displayed -- **THEN** format follows user's locale preference -- **THEN** relative times (e.g., "2 hours ago") are properly translated - -#### Scenario: New component text -- **WHEN** new UI components are added -- **THEN** all text strings use i18n translation keys -- **THEN** translations exist for all supported locales - -### Requirement: Standardized API Response Format -The system SHALL return all API responses in a consistent standardized format. - -#### Scenario: Successful API response -- **WHEN** API request succeeds -- **THEN** response includes success=true -- **THEN** response includes data field with result -- **THEN** response includes timestamp field - -#### Scenario: Error API response -- **WHEN** API request fails -- **THEN** response includes success=false -- **THEN** response includes error_code field -- **THEN** response includes message field with description - -### Requirement: API Versioning -The system SHALL support API versioning to enable backwards compatibility during upgrades. - -#### Scenario: Versioned API endpoint -- **WHEN** client calls /api/v1/tasks -- **THEN** system routes to current version implementation -- **THEN** response works with v1 client expectations - -#### Scenario: Legacy API route -- **WHEN** client calls /api/tasks (unversioned) -- **THEN** system routes to default version -- **THEN** response includes deprecation warning header - -### Requirement: Comprehensive Health Check -The system SHALL provide detailed health check endpoints for monitoring. - -#### Scenario: All systems healthy -- **WHEN** health check is called and all dependencies are available -- **THEN** response includes status=healthy -- **THEN** response includes checks object with database=ok, redis=ok - -#### Scenario: Partial system failure -- **WHEN** health check is called and Redis is unavailable -- **THEN** response includes status=degraded -- **THEN** response includes checks object with database=ok, redis=error - -### Requirement: Project Templates -The system SHALL support project templates to standardize project creation. - -#### Scenario: Create project from template -- **WHEN** user creates project selecting a template -- **THEN** system copies TaskStatus definitions from template -- **THEN** system copies CustomField definitions from template -- **THEN** project is created with predefined structure - -#### Scenario: Save project as template -- **WHEN** user saves existing project as template -- **THEN** system creates template with project's TaskStatus definitions -- **THEN** system creates template with project's CustomField definitions -- **THEN** template is available for future project creation - -### Requirement: Code Splitting -The application SHALL use code splitting with React.lazy() to reduce initial bundle size and improve load times. - -#### Scenario: Initial page load -- **WHEN** user navigates to application -- **THEN** only core framework and current route are loaded -- **AND** other routes are loaded on demand - -#### Scenario: Route-based splitting -- **WHEN** user navigates to a different page -- **THEN** that page's code chunk is loaded dynamically -- **AND** loading fallback is displayed during load - -### Requirement: LocalStorage Data Validation -User data loaded from localStorage SHALL be validated before use to prevent crashes from corrupted data. - -#### Scenario: Corrupted localStorage data -- **WHEN** localStorage contains malformed user JSON -- **THEN** invalid data is cleared -- **AND** user is redirected to login page -- **AND** no application crash occurs - -#### Scenario: Valid localStorage data -- **WHEN** localStorage contains valid user JSON -- **THEN** user is authenticated from stored data -- **AND** application loads normally - -### Requirement: Error Boundary Protection -The frontend application SHALL gracefully handle component render errors without crashing the entire application. - -#### Scenario: Component error contained -- **WHEN** a render error occurs in a dashboard widget -- **THEN** only that widget SHALL display an error state -- **AND** other widgets SHALL continue to function normally - -#### Scenario: User-friendly error display -- **WHEN** a component fails to render -- **THEN** users SHALL see a friendly error message -- **AND** users SHALL have an option to retry or report the issue - -#### Scenario: Error logging -- **WHEN** a render error is caught by an Error Boundary -- **THEN** the error details SHALL be logged for debugging -- **AND** error context (component stack) SHALL be captured - -#### Scenario: Recovery option -- **WHEN** a user sees an error fallback UI -- **AND** the user clicks "Retry" -- **THEN** the failed component SHALL attempt to re-render - diff --git a/openspec/specs/document-management/spec.md b/openspec/specs/document-management/spec.md deleted file mode 100644 index 3a21b2c..0000000 --- a/openspec/specs/document-management/spec.md +++ /dev/null @@ -1,273 +0,0 @@ -# Document Management - -## Purpose - -文件管理系統,提供檔案附件、版本控制、加密存儲與浮水印功能。 -## Requirements -### Requirement: File Attachments -系統 SHALL 支援任務層級的檔案附件,儲存於地端 NAS。 - -#### Scenario: 上傳附件 -- **GIVEN** 使用者擁有任務的編輯權限 -- **WHEN** 使用者上傳檔案至任務 -- **THEN** 系統將檔案儲存至 NAS -- **AND** 建立附件記錄關聯至該任務 - -#### Scenario: 下載附件 -- **GIVEN** 使用者擁有任務的存取權限 -- **WHEN** 使用者下載附件 -- **THEN** 系統驗證權限後提供檔案下載 -- **AND** 記錄下載操作日誌 - -#### Scenario: 刪除附件 -- **GIVEN** 使用者擁有任務的編輯權限 -- **WHEN** 使用者刪除附件 -- **THEN** 系統標記附件為已刪除(軟刪除) -- **AND** 保留檔案供稽核追溯 - -### Requirement: Version Control -系統 SHALL 支援檔案版本控制,追蹤所有版本變更。 - -#### Scenario: 上傳新版本 -- **GIVEN** 任務已有同名附件 -- **WHEN** 使用者上傳同名檔案 -- **THEN** 系統建立新版本而非覆蓋 -- **AND** 版本號自動遞增 - -#### Scenario: 查看版本歷史 -- **GIVEN** 附件有多個版本 -- **WHEN** 使用者查看版本歷史 -- **THEN** 顯示所有版本清單 -- **AND** 包含上傳者、上傳時間、檔案大小 - -#### Scenario: 回復舊版本 -- **GIVEN** 使用者需要使用舊版本 -- **WHEN** 使用者選擇回復特定版本 -- **THEN** 系統將該版本設為當前版本 -- **AND** 記錄回復操作 - -### Requirement: File Encryption -系統 SHALL 對半導體敏感圖檔進行 AES-256 加密存儲。 - -#### Scenario: 自動加密判斷 -- **GIVEN** 使用者上傳檔案至任務 -- **WHEN** 該任務所屬專案的 security_level 為 "confidential" -- **THEN** 系統自動使用 AES-256-GCM 加密檔案 -- **AND** 設定 is_encrypted = true 及 encryption_key_id - -#### Scenario: 加密存儲 -- **GIVEN** 專案設定為機密等級 -- **WHEN** 使用者上傳檔案 -- **THEN** 系統使用 AES-256 加密後存儲 -- **AND** 加密金鑰安全管理 - -#### Scenario: 解密讀取 -- **GIVEN** 使用者請求下載加密檔案 -- **WHEN** 系統驗證權限通過 -- **THEN** 系統解密檔案後提供下載 -- **AND** 解密過程透明,使用者無感 - -#### Scenario: 串流處理大檔案 -- **GIVEN** 使用者上傳或下載大型加密檔案 -- **WHEN** 系統處理加密或解密 -- **THEN** 使用串流方式處理避免記憶體溢出 -- **AND** 效能損耗在可接受範圍內 - -#### Scenario: 金鑰輪換 -- **GIVEN** 安全政策要求金鑰輪換 -- **WHEN** 管理員執行金鑰輪換 -- **THEN** 系統建立新金鑰並標記為 active -- **AND** 舊金鑰保留用於解密既有檔案 -- **AND** 新上傳檔案使用新金鑰加密 - -#### Scenario: Master Key 管理 -- **GIVEN** 系統需要加解密檔案 -- **WHEN** 系統取得加密金鑰 -- **THEN** 使用 Master Key 解密金鑰後使用 -- **AND** Master Key 從環境變數讀取,不存於資料庫 - -#### Scenario: 加密操作稽核 -- **GIVEN** 發生加密相關操作 -- **WHEN** 操作完成 -- **THEN** 系統記錄操作類型、金鑰 ID、檔案 ID、操作者、時間 -- **AND** 日誌不可竄改 - -#### Scenario: 金鑰管理權限 -- **GIVEN** 使用者嘗試管理加密金鑰 -- **WHEN** 使用者不是系統管理員 -- **THEN** 系統拒絕操作並返回 403 錯誤 - -### Requirement: Dynamic Watermarking -系統 SHALL 在下載時自動為檔案加上使用者浮水印。 - -#### Scenario: 圖片浮水印 -- **GIVEN** 使用者下載圖片類型附件 (PNG, JPG, JPEG) -- **WHEN** 系統處理下載請求 -- **THEN** 自動加上包含使用者姓名、工號、下載時間的浮水印 -- **AND** 浮水印位置不影響主要內容 - -#### Scenario: PDF 浮水印 -- **GIVEN** 使用者下載 PDF 類型附件 -- **WHEN** 系統處理下載請求 -- **THEN** 每頁加上浮水印 -- **AND** 浮水印透明度適中 - -#### Scenario: 浮水印內容 -- **GIVEN** 需要加上浮水印 -- **WHEN** 系統生成浮水印 -- **THEN** 浮水印包含: - - 使用者姓名 - - 使用者工號 - - 下載日期時間 - - 機密等級標示(如適用) - -#### Scenario: 不支援的檔案類型 -- **GIVEN** 使用者下載非圖片/PDF 類型附件 -- **WHEN** 系統處理下載請求 -- **THEN** 直接提供原始檔案下載 -- **AND** 不嘗試加上浮水印 - -#### Scenario: 浮水印服務異常處理 -- **GIVEN** 浮水印生成過程發生錯誤 -- **WHEN** 系統無法完成浮水印處理 -- **THEN** 記錄錯誤日誌 -- **AND** 提供原始檔案下載(降級處理) - -### Requirement: Audit Trail -系統 SHALL 記錄所有文件操作供稽核追溯。 - -#### Scenario: 操作日誌記錄 -- **GIVEN** 使用者對附件執行任何操作 -- **WHEN** 操作完成 -- **THEN** 系統記錄操作類型、操作者、時間、IP 位址 -- **AND** 日誌不可竄改 - -#### Scenario: 稽核查詢 -- **GIVEN** 稽核人員需要查詢文件操作歷史 -- **WHEN** 稽核人員執行查詢 -- **THEN** 顯示完整操作歷史 -- **AND** 支援依時間、操作者、檔案篩選 - -### Requirement: Storage Path Validation -The system SHALL validate file storage configuration on startup to ensure reliability. - -#### Scenario: Valid NAS storage path -- **WHEN** application starts with valid UPLOAD_DIR configuration -- **THEN** system verifies path exists and is writable -- **THEN** system logs confirmation of storage configuration - -#### Scenario: Invalid storage path -- **WHEN** application starts with invalid or inaccessible UPLOAD_DIR -- **THEN** system logs error with specific issue (not found, not writable) -- **THEN** system falls back to local storage with warning - -#### Scenario: Storage health check -- **WHEN** health check endpoint is called -- **THEN** response includes storage availability status -- **THEN** response includes available disk space if accessible - -### Requirement: Notification Delivery Reliability -The system SHALL ensure notification delivery even during temporary Redis failures. - -#### Scenario: Redis temporarily unavailable -- **WHEN** Redis publish fails due to connection error -- **THEN** system queues message in local memory -- **WHEN** Redis connection recovers -- **THEN** system retries queued messages - -#### Scenario: Queue overflow prevention -- **WHEN** local message queue exceeds maximum size -- **THEN** oldest messages are dropped -- **THEN** system logs warning about dropped messages - -### Requirement: Task Deletion Safety -The system SHALL warn users when deleting tasks with unresolved blockers. - -#### Scenario: Delete task with active blockers -- **WHEN** user attempts to delete task with unresolved blockers -- **THEN** system returns warning with blocker count -- **THEN** user must confirm or use force_delete flag - -#### Scenario: Force delete with blockers -- **WHEN** user force deletes task with blockers -- **THEN** system auto-resolves all blockers with "task deleted" reason -- **THEN** system proceeds with task deletion - -### Requirement: File MIME Type Validation -The system SHALL validate file content type using magic bytes detection. - -#### Scenario: Valid file with matching extension -- **WHEN** a user uploads a file -- **AND** the detected MIME type matches the file extension -- **THEN** the upload SHALL be accepted - -#### Scenario: Spoofed file extension rejected -- **WHEN** a user uploads a file with extension `.jpg` -- **AND** the actual content is detected as `application/x-executable` -- **THEN** the upload SHALL be rejected with error "File type mismatch" - -#### Scenario: Unsupported MIME type rejected -- **WHEN** a user uploads a file with an unsupported MIME type -- **THEN** the upload SHALL be rejected with error "Unsupported file type" - -#### Scenario: MIME validation bypass for trusted sources -- **WHEN** a file is uploaded from a trusted internal source -- **AND** the system is configured to allow bypass -- **THEN** MIME validation MAY be skipped - -## Data Model - -``` -pjctrl_attachments -├── id: UUID (PK) -├── task_id: UUID (FK -> tasks) -├── filename: VARCHAR(500) -├── original_filename: VARCHAR(500) -├── file_path: VARCHAR(1000) (NAS path) -├── file_size: BIGINT -├── mime_type: VARCHAR(100) -├── version: INT DEFAULT 1 -├── is_encrypted: BOOLEAN DEFAULT false -├── encryption_key_id: UUID (FK -> encryption_keys) -├── checksum: VARCHAR(64) (SHA-256) -├── uploaded_by: UUID (FK -> users) -├── is_deleted: BOOLEAN DEFAULT false -├── created_at: TIMESTAMP -└── updated_at: TIMESTAMP - -pjctrl_attachment_versions -├── id: UUID (PK) -├── attachment_id: UUID (FK -> attachments) -├── version: INT -├── file_path: VARCHAR(1000) -├── file_size: BIGINT -├── checksum: VARCHAR(64) -├── uploaded_by: UUID (FK -> users) -├── is_current: BOOLEAN DEFAULT false -└── created_at: TIMESTAMP - -pjctrl_encryption_keys -├── id: UUID (PK) -├── key_hash: VARCHAR(64) (for verification) -├── algorithm: VARCHAR(20) DEFAULT 'AES-256' -├── is_active: BOOLEAN DEFAULT true -├── created_at: TIMESTAMP -└── rotated_at: TIMESTAMP - -pjctrl_document_audit_logs -├── id: UUID (PK) -├── attachment_id: UUID (FK -> attachments) -├── user_id: UUID (FK -> users) -├── action: ENUM('upload', 'download', 'delete', 'restore', 'version_create', 'version_restore') -├── ip_address: VARCHAR(45) -├── user_agent: VARCHAR(500) -├── details: JSON -└── created_at: TIMESTAMP -``` - -## Technical Notes - -- 加密金鑰存儲於獨立的 Key Management Service (KMS) -- 浮水印使用 Pillow (圖片) 和 PyPDF2 (PDF) 處理 -- 大檔案使用串流處理避免記憶體溢出 -- NAS 存儲路徑結構:`/{project_id}/{task_id}/{attachment_id}/{version}/` diff --git a/openspec/specs/resource-management/spec.md b/openspec/specs/resource-management/spec.md deleted file mode 100644 index a51f7c5..0000000 --- a/openspec/specs/resource-management/spec.md +++ /dev/null @@ -1,239 +0,0 @@ -# Resource Management - -## Purpose - -資源管理系統,提供負載熱圖與人員容量追蹤,協助主管進行資源分配決策。讓主管能即時掌握團隊成員的工作負載狀況,及早發現超載或閒置問題,優化資源分配。 -## Requirements -### Requirement: Workload Heatmap - -系統 SHALL 提供負載熱圖 API,自動統計每人每週分配的任務總時數,並以顏色等級表示負載狀態。 - -#### Scenario: 負載正常顯示 -- **GIVEN** 某人員本週被指派的任務總時數低於其容量的 80% -- **WHEN** 主管查詢負載熱圖 API -- **THEN** 該人員的 `load_level` 為 `normal` -- **AND** 回傳包含 `load_percentage`、`allocated_hours`、`capacity_hours` - -#### Scenario: 負載警告顯示 -- **GIVEN** 某人員本週被指派的任務總時數達到其容量的 80%-99% -- **WHEN** 主管查詢負載熱圖 API -- **THEN** 該人員的 `load_level` 為 `warning` - -#### Scenario: 負載超載顯示 -- **GIVEN** 某人員本週被指派的任務總時數達到或超過其容量的 100% -- **WHEN** 主管查詢負載熱圖 API -- **THEN** 該人員的 `load_level` 為 `overloaded` - -#### Scenario: 查詢特定週的負載 -- **GIVEN** 主管需要查看非當週的負載 -- **WHEN** 主管以 `week_start` 參數查詢負載熱圖 API -- **THEN** 系統回傳該週的負載資料 - -#### Scenario: 快取機制 -- **GIVEN** 負載資料已被計算並快取 -- **WHEN** 相同查詢在 1 小時內再次發生 -- **THEN** 系統從 Redis 快取回傳結果 - -### Requirement: Capacity Planning - -系統 SHALL 支援人員容量規劃,包含預設容量與臨時調整。 - -#### Scenario: 設定人員預設容量 -- **GIVEN** 管理者需要設定人員的週工時上限 -- **WHEN** 管理者更新使用者的 `capacity` 值 -- **THEN** 系統儲存新的容量設定 -- **AND** 後續負載計算使用新容量值 - -#### Scenario: 容量為零處理 -- **GIVEN** 使用者的容量設為 0 -- **WHEN** 系統計算該使用者的負載 -- **THEN** `load_percentage` 顯示為 `null` -- **AND** `load_level` 顯示為 `unavailable` - -#### Scenario: 容量更新 API -- **GIVEN** 管理者需要更新團隊成員的容量 -- **WHEN** 管理者呼叫 `PUT /api/users/{user_id}/capacity` 並提供新容量值 -- **THEN** 系統驗證容量值在有效範圍內 (0-168 小時) -- **AND** 更新使用者的 capacity 欄位 -- **AND** 記錄變更至稽核日誌 - -#### Scenario: 容量更新權限控制 -- **GIVEN** 一般使用者嘗試更新他人容量 -- **WHEN** 使用者呼叫 `PUT /api/users/{other_id}/capacity` -- **THEN** 系統拒絕請求並回傳 403 Forbidden - -### Requirement: Multi-Project Health Dashboard -系統 SHALL 提供多專案健康看板,讓主管一覽所有專案狀態。 - -#### Scenario: 專案健康總覽 -- **GIVEN** 主管負責多個專案 -- **WHEN** 主管開啟健康看板 -- **THEN** 顯示所有專案的進度、風險指標、延遲任務數 -- **AND** 可依風險程度排序 - -#### Scenario: 專案延遲警示 -- **GIVEN** 專案有任務超過截止日期 -- **WHEN** 主管查看健康看板 -- **THEN** 該專案標示為延遲狀態 -- **AND** 顯示延遲任務數量與影響 - -#### Scenario: 專案健康 API -- **GIVEN** 後端系統運行中 -- **WHEN** 客戶端請求 `GET /api/projects/health` -- **THEN** 系統回傳所有可存取專案的健康數據 -- **AND** 包含 `total_tasks`, `completed_tasks`, `overdue_tasks`, `blocked_tasks`, `risk_score` - -#### Scenario: 單一專案健康詳情 -- **GIVEN** 主管需要查看特定專案詳情 -- **WHEN** 客戶端請求 `GET /api/projects/{id}/health` -- **THEN** 系統回傳該專案的詳細健康數據 -- **AND** 包含任務分類統計與風險評估 - -### Requirement: Team Workload Distribution - -系統 SHALL 提供團隊工作分配查詢功能。 - -#### Scenario: 部門負載總覽 -- **GIVEN** 主管需要了解部門整體負載 -- **WHEN** 主管以 `department_id` 參數查詢負載熱圖 API -- **THEN** 僅顯示該部門成員的負載狀況 - -#### Scenario: 使用者負載詳情 -- **GIVEN** 主管需要了解某人的詳細任務分配 -- **WHEN** 主管查詢使用者負載詳情 API -- **THEN** 回傳該週指派給該使用者的所有任務 -- **AND** 包含每個任務的 `original_estimate` 與 `due_date` - -### Requirement: Workload Data Access Control - -系統 SHALL 限制負載資料的存取權限。 - -#### Scenario: 系統管理員查看所有人 -- **GIVEN** 登入者為 `super_admin` -- **WHEN** 查詢負載熱圖 API -- **THEN** 可查看所有使用者的負載資料 - -#### Scenario: 一般使用者查看自己 -- **GIVEN** 登入者為一般使用者 -- **WHEN** 查詢負載熱圖 API 未指定 `user_ids` -- **THEN** 僅回傳自己的負載資料 - -#### Scenario: 跨部門存取拒絕 -- **GIVEN** 登入者非系統管理員 -- **WHEN** 查詢其他部門使用者的負載 -- **THEN** 系統拒絕存取並回傳 403 Forbidden - -### Requirement: Workload Heatmap UI -The system SHALL provide a visual workload heatmap interface for managers. - -#### Scenario: View workload heatmap -- **GIVEN** user is logged in as manager or admin -- **WHEN** user navigates to /workload page without filters -- **THEN** system displays a heatmap showing all accessible users' workload -- **AND** users with zero workload are included by default -- **AND** each user cell is color-coded by load level (green/yellow/red) - -#### Scenario: Hide empty workloads -- **GIVEN** user is viewing the workload page -- **WHEN** user enables the hide_empty filter -- **THEN** the heatmap excludes users with zero workload - -#### Scenario: Navigate between weeks -- **GIVEN** user is viewing the workload page -- **WHEN** user clicks previous/next week buttons -- **THEN** the heatmap updates to show that week's workload data - -#### Scenario: Default week window on Sunday -- **GIVEN** today is Sunday and user opens workload page without selecting week_start -- **THEN** the default heatmap window includes tasks due in the upcoming week - -#### Scenario: View user workload details -- **GIVEN** user is viewing the workload heatmap -- **WHEN** user clicks on a specific user's cell -- **THEN** a modal/drawer opens showing that user's task breakdown -- **AND** tasks show title, project, time estimate, and due date - -#### Scenario: Filter by department -- **GIVEN** user is a system admin -- **WHEN** user selects a department from the filter -- **THEN** the heatmap shows only users from that department - -### Requirement: Manager Workload Visibility -The system SHALL allow department managers to view workload data for all members within their department. - -#### Scenario: Manager views department member workload -- **WHEN** a department manager requests workload data for a user in their department -- **THEN** system returns the workload data for that user - -#### Scenario: Manager denied access to other department workload -- **WHEN** a department manager requests workload data for a user in a different department -- **THEN** system returns 403 Forbidden error - -#### Scenario: Regular user cannot view others' workload -- **WHEN** a non-manager user requests workload data for another user -- **THEN** system returns 403 Forbidden error - -### Requirement: Cross-Department Project Membership -The system SHALL support explicit project membership to enable cross-department collaboration. - -#### Scenario: Add cross-department member to project -- **WHEN** project owner adds a user from another department as project member -- **THEN** user gains access to the project regardless of department - -#### Scenario: Project member accesses cross-department project -- **WHEN** a project member from another department accesses project resources -- **THEN** system grants access based on project membership - -#### Scenario: Non-member denied access despite same department -- **WHEN** a user not in project membership list attempts to access confidential project -- **THEN** system denies access unless user is in the project's department - -### Requirement: Optimized Relationship Loading -The system SHALL use efficient query patterns to avoid N+1 query problems when loading related entities. - -#### Scenario: Project member list loading -- **WHEN** loading a project with its members -- **THEN** the system SHALL load all members in at most 2 database queries -- **AND** NOT one query per member - -#### Scenario: Task assignee loading -- **WHEN** loading a list of tasks with their assignees -- **THEN** the system SHALL batch load assignee details -- **AND** NOT query each assignee individually - -#### Scenario: Query count monitoring -- **WHEN** running in development mode -- **THEN** the system SHALL log query counts per request -- **AND** warn when query count exceeds threshold (e.g., 10 queries) - -## Data Model - -``` -pjctrl_workload_snapshots -├── id: UUID (PK) -├── user_id: UUID (FK -> users) -├── week_start: DATE -├── allocated_hours: DECIMAL -├── capacity_hours: DECIMAL -├── load_percentage: DECIMAL -├── created_at: TIMESTAMP -└── updated_at: TIMESTAMP - -pjctrl_project_health -├── id: UUID (PK) -├── project_id: UUID (FK -> projects) -├── snapshot_date: DATE -├── total_tasks: INT -├── completed_tasks: INT -├── overdue_tasks: INT -├── blocked_tasks: INT -├── risk_score: DECIMAL -├── created_at: TIMESTAMP -└── updated_at: TIMESTAMP -``` - -## Calculation Rules - -- **負載百分比** = (allocated_hours / capacity_hours) × 100 -- **風險評分** = f(overdue_tasks, blocked_tasks, timeline_remaining) -- 快取計算結果於 Redis,每小時更新或任務變更時即時更新 diff --git a/openspec/specs/task-management/spec.md b/openspec/specs/task-management/spec.md deleted file mode 100644 index 3b9f697..0000000 --- a/openspec/specs/task-management/spec.md +++ /dev/null @@ -1,409 +0,0 @@ -# Task Management - -## Purpose - -任務管理核心系統,支援多層級架構、自定義欄位與多維視角。 -## Requirements -### Requirement: Hierarchical Task Structure -系統 SHALL 支援多層級任務架構:空間 (Space) > 專案 (Project) > 任務 (Task) > 子任務 (Sub-task)。 - -#### Scenario: 建立空間 -- **GIVEN** 使用者擁有建立空間的權限 -- **WHEN** 使用者建立新空間 -- **THEN** 系統建立空間並設定擁有者 -- **AND** 空間可包含多個專案 - -#### Scenario: 建立專案 -- **GIVEN** 使用者在某空間內擁有建立專案的權限 -- **WHEN** 使用者建立新專案 -- **THEN** 系統建立專案並關聯至該空間 -- **AND** 設定專案的 Owner、Budget、Timeline、Security_Level - -#### Scenario: 建立任務與子任務 -- **GIVEN** 使用者在專案內擁有建立任務的權限 -- **WHEN** 使用者建立任務或子任務 -- **THEN** 系統建立任務並維護父子關係 -- **AND** 子任務繼承父任務的部分屬性 - -### Requirement: Custom Fields -系統 SHALL 支援自定義欄位,包含下拉選單、公式、人員標籤等類型。 - -#### Scenario: 新增自定義欄位 -- **GIVEN** 專案管理者需要追蹤特定資料(如:封裝類型、機台編號、預計良率) -- **WHEN** 管理者在專案中新增自定義欄位 -- **THEN** 系統建立欄位定義並套用至該專案所有任務 - -#### Scenario: 編輯自定義欄位 -- **GIVEN** 專案已有自定義欄位 -- **WHEN** 管理者修改欄位名稱或選項 -- **THEN** 系統更新欄位定義 -- **AND** 現有任務的欄位值保持不變 - -#### Scenario: 刪除自定義欄位 -- **GIVEN** 專案已有自定義欄位且有任務包含該欄位的值 -- **WHEN** 管理者刪除該欄位 -- **THEN** 系統顯示確認對話框說明將刪除所有相關值 -- **AND** 確認後刪除欄位定義及所有任務的該欄位值 - -#### Scenario: 公式欄位計算 -- **GIVEN** 任務包含公式類型的自定義欄位 -- **WHEN** 相依欄位的值發生變更 -- **THEN** 系統自動重新計算公式欄位的值 - -#### Scenario: 公式欄位循環引用檢查 -- **GIVEN** 管理者建立公式欄位 -- **WHEN** 公式引用自己或形成循環引用 -- **THEN** 系統拒絕建立並顯示錯誤訊息 - -#### Scenario: 人員標籤欄位 -- **GIVEN** 任務包含人員標籤類型的自定義欄位 -- **WHEN** 使用者選擇人員 -- **THEN** 系統驗證人員存在並建立關聯 -- **AND** 被標籤的人員可收到相關通知 - -#### Scenario: 下拉選單欄位 -- **GIVEN** 任務包含下拉選單類型的自定義欄位 -- **WHEN** 使用者選擇選項 -- **THEN** 系統儲存選擇的值 -- **AND** 選項列表由欄位定義提供 - -#### Scenario: 自定義欄位值顯示 -- **GIVEN** 任務有自定義欄位值 -- **WHEN** 使用者在列表或看板視角查看任務 -- **THEN** 自定義欄位值顯示在任務資訊中 -- **AND** 公式欄位顯示計算結果(唯讀) - -#### Scenario: 欄位數量限制 -- **GIVEN** 專案已有 20 個自定義欄位 -- **WHEN** 管理者嘗試新增第 21 個欄位 -- **THEN** 系統拒絕新增並顯示數量已達上限的訊息 - -### Requirement: Input Length Validation -All text input fields SHALL have maximum length constraints to prevent abuse and database issues. - -#### Scenario: Task title exceeds limit -- **WHEN** user creates a task with title exceeding 500 characters -- **THEN** request is rejected with 422 Validation Error -- **AND** error indicates field length exceeded - -#### Scenario: Description within limit -- **WHEN** user creates a task with description 10000 characters or less -- **THEN** task is created successfully - -#### Scenario: Description exceeds limit -- **WHEN** user creates a task with description exceeding 10000 characters -- **THEN** request is rejected with 422 Validation Error - -#### Scenario: Comment content limit -- **WHEN** user submits a comment exceeding 5000 characters -- **THEN** request is rejected with 422 Validation Error - -### Requirement: Multiple Views -系統 SHALL 支援多維視角:看板 (Kanban)、甘特圖 (Gantt)、列表 (List)、行事曆 (Calendar)。 - -#### Scenario: 甘特圖視角 -- **GIVEN** 使用者選擇甘特圖視角 -- **WHEN** 系統載入專案任務 -- **THEN** 任務依時間軸顯示為水平條狀 -- **AND** 顯示任務相依關係與里程碑 - -#### Scenario: 甘特圖時間軸縮放 -- **GIVEN** 使用者正在查看甘特圖 -- **WHEN** 使用者切換縮放層級(日、週、月) -- **THEN** 時間軸相應調整顯示密度 -- **AND** 任務條保持正確的相對位置 - -#### Scenario: 拖拉調整任務日期 -- **GIVEN** 使用者正在查看甘特圖 -- **WHEN** 使用者拖拉任務條改變位置或長度 -- **THEN** 系統更新任務的 start_date 和 due_date -- **AND** 驗證日期合理性(start_date <= due_date) - -#### Scenario: 顯示任務依賴關係 -- **GIVEN** 任務之間存在依賴關係 -- **WHEN** 使用者查看甘特圖 -- **THEN** 系統顯示連接任務的箭頭 -- **AND** 箭頭方向表示依賴方向(前置任務 → 後續任務) - -#### Scenario: 新增任務依賴 -- **GIVEN** 使用者在甘特圖上選擇兩個任務 -- **WHEN** 使用者建立依賴關係 -- **THEN** 系統儲存依賴關係 -- **AND** 顯示連接箭頭 - -#### Scenario: 刪除任務依賴 -- **GIVEN** 任務之間存在依賴關係 -- **WHEN** 使用者刪除該依賴 -- **THEN** 系統移除依賴記錄 -- **AND** 連接箭頭消失 - -#### Scenario: 循環依賴檢測 -- **GIVEN** 使用者嘗試建立依賴關係 -- **WHEN** 該依賴會形成循環(A → B → C → A) -- **THEN** 系統拒絕建立並顯示錯誤訊息 -- **AND** 現有依賴關係保持不變 - -#### Scenario: 依賴類型支援 -- **GIVEN** 使用者建立任務依賴 -- **WHEN** 使用者選擇依賴類型 -- **THEN** 系統支援以下類型: - - Finish-to-Start (FS): 前置完成後開始 - - Start-to-Start (SS): 前置開始後開始 - - Finish-to-Finish (FF): 前置完成後完成 - - Start-to-Finish (SF): 前置開始後完成 - -### Requirement: Task Status Management -系統 SHALL 管理任務狀態,包含標準狀態與自定義狀態。 - -#### Scenario: 狀態變更 -- **GIVEN** 使用者擁有更新任務的權限 -- **WHEN** 使用者變更任務狀態 -- **THEN** 系統更新狀態並記錄變更時間 -- **AND** 觸發相關自動化規則(如有設定) - -#### Scenario: 阻礙標記 -- **GIVEN** 任務遇到阻礙無法進行 -- **WHEN** 工程師將任務標記為 "Blocked" -- **THEN** 系統設定 Blocker_Flag = true -- **AND** 強制發送通知給主管要求介入排解 - -### Requirement: Task Assignment -系統 SHALL 支援任務指派與時間估算。 - -#### Scenario: 指派任務 -- **GIVEN** 使用者擁有指派任務的權限 -- **WHEN** 使用者將任務指派給某人 -- **THEN** 系統更新 Assignee 並發送通知 -- **AND** 任務計入被指派者的工作負載 - -#### Scenario: 時間估算與追蹤 -- **GIVEN** 任務已被指派 -- **WHEN** 使用者設定 Original_Estimate 與回報 Time_Spent -- **THEN** 系統記錄並計算剩餘時間 -- **AND** 更新資源負載統計 - -### Requirement: Kanban View -The system SHALL provide a Kanban board view for tasks with drag-and-drop status management. - -#### Scenario: View Kanban board -- **GIVEN** user is on the Tasks page -- **WHEN** user selects Kanban view -- **THEN** tasks are displayed in columns grouped by status -- **AND** each column header shows the status name and task count - -#### Scenario: Drag task to change status -- **GIVEN** user is viewing the Kanban board -- **WHEN** user drags a task card to a different status column -- **THEN** the task status is updated via API -- **AND** the card moves to the new column -- **AND** other users viewing the board see the update - -#### Scenario: View toggle persistence -- **GIVEN** user switches to Kanban view -- **WHEN** user navigates away and returns -- **THEN** the Kanban view is still selected - -### Requirement: Task Detail Modal -The system SHALL provide a task detail modal with comments and attachments. - -#### Scenario: Open task detail -- **GIVEN** user is viewing tasks in any view -- **WHEN** user clicks on a task -- **THEN** a modal opens showing task details -- **AND** the modal includes comments section -- **AND** the modal includes attachments section - -#### Scenario: Edit task in modal -- **GIVEN** user has task detail modal open -- **WHEN** user modifies task fields and saves -- **THEN** the task is updated via API -- **AND** the task list/board reflects the changes - -### Requirement: Task Assignment UI -The system SHALL allow assigning tasks to users during creation and editing. - -#### Scenario: Assign task during creation -- **GIVEN** user is creating a new task -- **WHEN** user selects an assignee from the dropdown -- **THEN** the task is created with the selected assignee - -#### Scenario: Change task assignee -- **GIVEN** user has task detail modal open -- **WHEN** user changes the assignee -- **THEN** the task assignee is updated -- **AND** the new assignee receives a notification - -#### Scenario: Set due date and time estimate -- **GIVEN** user is creating or editing a task -- **WHEN** user sets due date and time estimate -- **THEN** the values are saved with the task -- **AND** the task appears on the appropriate date in calendar view - -### Requirement: Space Deletion -系統 SHALL 允許空間擁有者刪除空間(軟刪除)。 - -#### Scenario: 刪除空白空間 -- **GIVEN** 使用者是空間的擁有者 -- **AND** 空間內沒有任何專案 -- **WHEN** 使用者點擊刪除按鈕並確認 -- **THEN** 系統將空間標記為已刪除 (is_active = false) -- **AND** 空間不再顯示於列表中 -- **AND** 顯示成功通知 - -#### Scenario: 刪除含專案的空間 -- **GIVEN** 使用者是空間的擁有者 -- **AND** 空間內包含一個或多個專案 -- **WHEN** 使用者點擊刪除按鈕 -- **THEN** 系統顯示警告對話框,說明包含 N 個專案 -- **AND** 要求使用者輸入空間名稱以確認刪除 -- **WHEN** 使用者正確輸入名稱並確認 -- **THEN** 系統將空間標記為已刪除 -- **AND** 空間內的專案同時被軟刪除 -- **AND** 顯示成功通知 - -#### Scenario: 非擁有者無法刪除空間 -- **GIVEN** 使用者不是空間的擁有者 -- **WHEN** 使用者嘗試刪除空間 -- **THEN** 系統拒絕操作 -- **AND** 顯示權限不足的錯誤訊息 - -### Requirement: Project Deletion -系統 SHALL 允許專案擁有者刪除專案(軟刪除),並記錄於稽核日誌。 - -#### Scenario: 刪除專案 -- **GIVEN** 使用者是專案的擁有者 -- **WHEN** 使用者點擊刪除按鈕 -- **THEN** 系統顯示確認對話框,說明專案內的任務數量 -- **WHEN** 使用者確認刪除 -- **THEN** 系統將專案標記為已刪除 (is_active = false) -- **AND** 專案不再顯示於空間的專案列表中 -- **AND** 系統記錄刪除事件至稽核日誌 -- **AND** 顯示成功通知 - -#### Scenario: 非擁有者無法刪除專案 -- **GIVEN** 使用者不是專案的擁有者 -- **WHEN** 使用者嘗試刪除專案 -- **THEN** 系統拒絕操作 -- **AND** 顯示權限不足的錯誤訊息 - -#### Scenario: 刪除專案的稽核記錄 -- **GIVEN** 專案擁有者刪除專案 -- **WHEN** 刪除操作完成 -- **THEN** 稽核日誌記錄以下資訊: - - event_type: "project.delete" - - resource_type: "project" - - action: DELETE - - user_id: 執行刪除的使用者 - - resource_id: 被刪除的專案 ID - - changes: [{ field: "is_active", old_value: true, new_value: false }] - -### Requirement: Task Dependency Cycle Prevention -The system SHALL detect and prevent circular dependencies between tasks to ensure Gantt charts can be properly rendered. - -#### Scenario: Direct circular dependency rejected -- **WHEN** user attempts to create dependency where Task A depends on Task B and Task B depends on Task A -- **THEN** system rejects the operation with 400 Bad Request -- **THEN** error message includes the cycle path (e.g., "Circular dependency detected: A -> B -> A") - -#### Scenario: Indirect circular dependency rejected -- **WHEN** user attempts to create dependency that would form a cycle (A -> B -> C -> A) -- **THEN** system rejects the operation with 400 Bad Request -- **THEN** error message includes the full cycle path - -#### Scenario: Valid dependency chain accepted -- **WHEN** user creates dependencies forming a valid DAG (directed acyclic graph) -- **THEN** system accepts and saves the dependencies -- **THEN** Gantt chart renders correctly with proper task ordering - -### Requirement: Optimistic Locking for Task Updates -The system SHALL use optimistic locking to prevent concurrent update conflicts on tasks. - -#### Scenario: Concurrent update detected -- **WHEN** user A and user B both load task at version 1 -- **WHEN** user A saves changes, incrementing version to 2 -- **WHEN** user B attempts to save with version 1 -- **THEN** system returns 409 Conflict error -- **AND** response includes a human-readable message instructing refresh and retry -- **AND** response includes the current and provided version numbers - -#### Scenario: Sequential updates succeed -- **WHEN** user loads task at version N -- **WHEN** user saves changes with correct version N -- **THEN** system accepts update and increments version to N+1 - -### Requirement: Soft Delete Cascade Restore -The system SHALL restore child tasks when parent task is restored from soft delete. - -#### Scenario: Parent task restored with children -- **WHEN** soft-deleted parent task is restored -- **THEN** system identifies child tasks deleted at same timestamp -- **THEN** system recursively restores all matching child tasks -- **THEN** audit log records restoration of parent and children - -#### Scenario: Selective restore without children -- **WHEN** user explicitly requests restore without cascade -- **THEN** only parent task is restored -- **THEN** child tasks remain in deleted state - -## Data Model - -``` -pjctrl_spaces -├── id: UUID (PK) -├── name: VARCHAR(200) -├── description: TEXT -├── owner_id: UUID (FK -> users) -├── created_at: TIMESTAMP -└── updated_at: TIMESTAMP - -pjctrl_projects -├── id: UUID (PK) -├── space_id: UUID (FK -> spaces) -├── title: VARCHAR(200) -├── description: TEXT -├── owner_id: UUID (FK -> users) -├── budget: DECIMAL -├── start_date: DATE -├── end_date: DATE -├── security_level: ENUM('public', 'department', 'confidential') -├── status: VARCHAR(50) -├── created_at: TIMESTAMP -└── updated_at: TIMESTAMP - -pjctrl_tasks -├── id: UUID (PK) -├── project_id: UUID (FK -> projects) -├── parent_task_id: UUID (FK -> tasks, nullable) -├── title: VARCHAR(500) -├── description: TEXT -├── assignee_id: UUID (FK -> users) -├── priority: ENUM('low', 'medium', 'high', 'urgent') -├── status: VARCHAR(50) -├── original_estimate: DECIMAL (hours) -├── time_spent: DECIMAL (hours) -├── blocker_flag: BOOLEAN DEFAULT false -├── due_date: DATETIME -├── created_at: TIMESTAMP -└── updated_at: TIMESTAMP - -pjctrl_custom_fields -├── id: UUID (PK) -├── project_id: UUID (FK -> projects) -├── name: VARCHAR(100) -├── field_type: ENUM('text', 'number', 'dropdown', 'date', 'person', 'formula') -├── options: JSON (for dropdown) -├── formula: TEXT (for formula type) -├── is_required: BOOLEAN -└── created_at: TIMESTAMP - -pjctrl_task_custom_values -├── id: UUID (PK) -├── task_id: UUID (FK -> tasks) -├── field_id: UUID (FK -> custom_fields) -├── value: TEXT -└── updated_at: TIMESTAMP -``` - -## Real-time Sync - -系統使用 WebSocket 實現即時同步,當多人同時編輯同一個專案看板時,狀態能即時更新而不需刷新頁面。 diff --git a/openspec/specs/user-auth/spec.md b/openspec/specs/user-auth/spec.md deleted file mode 100644 index 1b3e549..0000000 --- a/openspec/specs/user-auth/spec.md +++ /dev/null @@ -1,327 +0,0 @@ -# User Authentication & Authorization - -## Purpose - -使用者認證與授權系統,透過外部認證 API 進行身份驗證,提供細部權限控制。 -## Requirements -### Requirement: API-Based Authentication -系統 SHALL 限定使用外部認證 API (https://pj-auth-api.vercel.app) 進行登入認證,不支援其他認證方式。 - -#### Scenario: API 登入成功 -- **GIVEN** 使用者擁有有效的企業帳號 -- **WHEN** 使用者透過前端提交憑證 -- **THEN** 系統呼叫 https://pj-auth-api.vercel.app 驗證憑證 -- **AND** 驗證成功後建立 session 並回傳 JWT token - -#### Scenario: API 登入失敗 -- **GIVEN** 使用者提供無效的憑證 -- **WHEN** 使用者嘗試登入 -- **THEN** 認證 API 回傳錯誤 -- **AND** 系統拒絕登入並顯示錯誤訊息 -- **AND** 記錄失敗的登入嘗試 - -#### Scenario: 認證 API 無法連線 -- **GIVEN** 認證 API 服務無法連線 -- **WHEN** 使用者嘗試登入 -- **THEN** 系統顯示服務暫時無法使用的訊息 -- **AND** 記錄連線失敗事件 - -### Requirement: System Administrator -系統 SHALL 預設一個系統管理員帳號,擁有所有權限。系統管理員帳號必須存在於外部認證系統,且登入流程仍需透過外部認證 API;不允許本地繞過認證。 - -#### Scenario: 預設管理員帳號 -- **GIVEN** 系統初始化完成 -- **WHEN** 系統啟動 -- **THEN** 存在預設管理員帳號 `ymirliu@panjit.com.tw` -- **AND** 該帳號擁有 `super_admin` 角色 -- **AND** 該帳號不可被刪除或降級 - -#### Scenario: 管理員登入流程 -- **GIVEN** 管理員帳號 `ymirliu@panjit.com.tw` 需要登入 -- **WHEN** 管理員提交憑證 -- **THEN** 系統仍需呼叫 https://pj-auth-api.vercel.app 驗證 -- **AND** 不存在任何本地繞過認證的機制 -- **AND** 驗證成功後才授予 `super_admin` 權限 - -#### Scenario: 管理員全域權限 -- **GIVEN** 管理員帳號 `ymirliu@panjit.com.tw` 已通過 API 認證並登入 -- **WHEN** 管理員存取任何資源 -- **THEN** 系統允許存取,無視部門隔離限制 - -### Requirement: Role-Based Access Control -系統 SHALL 支援基於角色的存取控制 (RBAC)。 - -#### Scenario: 角色權限檢查 -- **GIVEN** 使用者被指派特定角色 (如:工程師、主管、PMO) -- **WHEN** 使用者嘗試存取受保護的資源 -- **THEN** 系統根據角色權限決定是否允許存取 - -#### Scenario: 角色指派 -- **GIVEN** 管理員擁有使用者管理權限 -- **WHEN** 管理員為使用者指派角色 -- **THEN** 系統更新使用者的角色設定 -- **AND** 新權限立即生效 - -### Requirement: Department Isolation -系統 SHALL 實施部門級別的資料隔離,確保跨部門資料安全。 - -#### Scenario: 部門資料隔離 -- **GIVEN** 使用者屬於研發部門 -- **WHEN** 使用者嘗試存取廠務部門的專案 -- **THEN** 系統拒絕存取並顯示無權限訊息 - -#### Scenario: 跨部門專案存取 -- **GIVEN** 專案被設定為跨部門可見 -- **WHEN** 不同部門的使用者嘗試存取該專案 -- **THEN** 系統根據專案的 Security_Level 設定決定是否允許存取 - -### Requirement: Session Management -系統 SHALL 管理使用者 session,包含過期與登出機制。 - -#### Scenario: Session 過期 -- **GIVEN** 使用者已登入系統 -- **WHEN** Session 超過設定的有效期限 -- **THEN** 系統自動使 session 失效 -- **AND** 使用者需重新登入 - -#### Scenario: 主動登出 -- **GIVEN** 使用者已登入系統 -- **WHEN** 使用者執行登出操作 -- **THEN** 系統銷毀 session 並清除 token - -### Requirement: Access Token Expiry -Access tokens SHALL expire within 60 minutes to limit exposure window in case of token compromise. - -#### Scenario: Access token expiry -- **WHEN** an access token issued 61 minutes ago is used for API authentication -- **THEN** request is rejected with 401 Unauthorized -- **AND** error indicates "Token expired" - -### Requirement: Refresh Token Support -The system SHALL support refresh tokens for seamless session continuity without requiring re-authentication. - -#### Scenario: Refresh valid token -- **WHEN** POST to /api/auth/refresh with valid refresh token -- **THEN** new access token is issued -- **AND** new refresh token is issued via rotation -- **AND** old refresh token is invalidated - -#### Scenario: Refresh expired token -- **WHEN** POST to /api/auth/refresh with expired refresh token -- **THEN** request is rejected with 401 Unauthorized -- **AND** user must re-authenticate via login - -#### Scenario: Automatic frontend refresh -- **WHEN** access token expires in less than 5 minutes -- **AND** frontend prepares to make API call -- **THEN** token is automatically refreshed first -- **AND** original request proceeds with new token - -### Requirement: API Rate Limiting -The system SHALL implement rate limiting to protect against brute force attacks and DoS attempts. - -#### Scenario: Login rate limit enforcement -- **GIVEN** a client IP has made 5 login attempts within 1 minute -- **WHEN** the client attempts another login -- **THEN** the system returns HTTP 429 Too Many Requests -- **AND** the response includes a Retry-After header - -#### Scenario: Rate limit window reset -- **GIVEN** a client has exceeded the rate limit -- **WHEN** the rate limit window expires (1 minute) -- **THEN** the client can make new requests - -#### Scenario: Rate limit per IP -- **GIVEN** rate limiting is IP-based -- **WHEN** different IPs make requests -- **THEN** each IP has its own rate limit counter - -### Requirement: Comprehensive API Rate Limiting -The system SHALL enforce rate limits on all sensitive API endpoints to prevent abuse and ensure service availability. - -#### Scenario: Task creation rate limit exceeded -- **WHEN** user exceeds 60 task creation requests per minute -- **THEN** system returns 429 Too Many Requests -- **THEN** response includes Retry-After header - -#### Scenario: Report generation rate limit exceeded -- **WHEN** user exceeds 5 report generation requests per minute -- **THEN** system returns 429 Too Many Requests -- **THEN** response includes rate limit headers - -#### Scenario: Rate limit headers provided -- **WHEN** user makes any rate-limited API request -- **THEN** response includes X-RateLimit-Limit header -- **THEN** response includes X-RateLimit-Remaining header -- **THEN** response includes X-RateLimit-Reset header - -#### Scenario: Rate limit window reset -- **WHEN** rate limit window expires -- **THEN** user can make requests again up to the limit -- **THEN** X-RateLimit-Remaining resets to maximum - -### Requirement: Input Length Validation -The system SHALL enforce maximum length limits on all user-provided string inputs to prevent DoS attacks and database overflow. - -#### Scenario: Task title exceeds maximum length -- **WHEN** user submits a task with title longer than 500 characters -- **THEN** system returns 422 Validation Error with descriptive message - -#### Scenario: Description field within limits -- **WHEN** user submits content with description under 10000 characters -- **THEN** system accepts the input and processes normally - -### Requirement: CORS Security -The system SHALL explicitly define allowed CORS methods and headers instead of using wildcards to reduce attack surface. - -#### Scenario: Request with standard headers -- **WHEN** a cross-origin request includes Content-Type, Authorization, or X-CSRF-Token headers -- **THEN** the request is allowed - -#### Scenario: Request with non-standard header -- **WHEN** a cross-origin request includes a non-whitelisted custom header -- **THEN** CORS preflight fails and request is rejected - -### Requirement: Secure WebSocket Authentication -The system SHALL authenticate WebSocket connections without exposing tokens in URL query parameters. In production environments, query parameter authentication SHALL be disabled. - -#### Scenario: WebSocket connection with token in first message -- **WHEN** client connects to WebSocket endpoint without a query token -- **THEN** server waits for authentication message containing JWT token -- **THEN** server validates token before accepting further messages -- **THEN** server sends an authentication acknowledgment message - -#### Scenario: WebSocket connection with invalid token -- **WHEN** client sends an invalid or expired token -- **THEN** server sends an error message indicating invalid or expired token -- **THEN** server closes the connection with an authentication error code - -#### Scenario: WebSocket connection timeout without authentication -- **WHEN** client connects but does not send authentication within 10 seconds -- **THEN** server closes the connection with appropriate error code - -#### Scenario: Query parameter auth in production -- **WHEN** production environment and WebSocket connection includes token in query parameter -- **THEN** connection is rejected with code 4002 -- **AND** error message indicates "Query parameter auth disabled in production" - -### Requirement: WebSocket Connection Limits -The system SHALL limit each user to a maximum of 5 concurrent WebSocket connections to prevent resource exhaustion. - -#### Scenario: User exceeds connection limit -- **WHEN** user already has 5 active WebSocket connections -- **AND** user attempts to open a 6th connection -- **THEN** connection is rejected with code 4005 -- **AND** error message indicates "Too many connections" - -#### Scenario: User within connection limit -- **WHEN** user has fewer than 5 active connections -- **AND** user opens a new WebSocket connection -- **THEN** connection is accepted - -### Requirement: Path Traversal Protection -The system SHALL prevent file path traversal attacks by validating all file paths resolve within the designated storage directory. - -#### Scenario: Path traversal attempt detected -- **WHEN** request contains file path with "../" or absolute path outside storage -- **THEN** system rejects request and logs security warning -- **THEN** system returns 403 Forbidden error - -#### Scenario: Valid file path within storage -- **WHEN** request contains valid relative file path -- **THEN** system resolves path and verifies it is within storage directory -- **THEN** system processes file operation normally - -### Requirement: JWT Secret Validation -The system SHALL validate JWT secret key strength on startup. - -#### Scenario: Weak secret rejected -- **WHEN** the configured JWT secret is less than 32 characters -- **THEN** the system SHALL log a critical warning -- **AND** optionally refuse to start in production mode - -#### Scenario: Low entropy secret warning -- **WHEN** the JWT secret has low entropy (repeating patterns, common words) -- **THEN** the system SHALL log a security warning - -### Requirement: CSRF Protection -The system SHALL protect all state-changing operations (POST, PUT, PATCH, DELETE) with CSRF tokens. - -#### Scenario: POST request without CSRF token -- **WHEN** an authenticated user makes a POST request without X-CSRF-Token header -- **THEN** the request SHALL be rejected with 403 Forbidden -- **AND** error message indicates "CSRF token is required" - -#### Scenario: PUT/PATCH/DELETE request without CSRF token -- **WHEN** an authenticated user makes a PUT, PATCH, or DELETE request without X-CSRF-Token header -- **THEN** the request SHALL be rejected with 403 Forbidden - -#### Scenario: Valid CSRF token accepted -- **WHEN** a state-changing request includes a valid CSRF token -- **THEN** the request SHALL proceed normally - -#### Scenario: Public endpoints exempt from CSRF -- **WHEN** POST to /api/auth/login or other public endpoints -- **THEN** CSRF token is not required - -## Data Model - -``` -pjctrl_users -├── id: UUID (PK) -├── email: VARCHAR(200) UNIQUE -├── name: VARCHAR(200) -├── department_id: UUID (FK) -├── role_id: UUID (FK) -├── skills: JSON -├── capacity: DECIMAL (週工時上限) -├── is_active: BOOLEAN -├── is_system_admin: BOOLEAN DEFAULT false (不可修改的系統管理員標記) -├── created_at: TIMESTAMP -└── updated_at: TIMESTAMP - -pjctrl_departments -├── id: UUID (PK) -├── name: VARCHAR(100) -├── parent_id: UUID (FK, self-reference) -└── created_at: TIMESTAMP - -pjctrl_roles -├── id: UUID (PK) -├── name: VARCHAR(50) -├── permissions: JSON -├── is_system_role: BOOLEAN DEFAULT false -└── created_at: TIMESTAMP -``` - -## Default Data (Seed) - -```sql --- 預設系統管理員角色 -INSERT INTO pjctrl_roles (id, name, permissions, is_system_role) VALUES -('00000000-0000-0000-0000-000000000001', 'super_admin', '{"all": true}', true); - --- 預設系統管理員帳號 -INSERT INTO pjctrl_users (id, email, name, role_id, is_active, is_system_admin) VALUES -('00000000-0000-0000-0000-000000000001', 'ymirliu@panjit.com.tw', 'System Administrator', - '00000000-0000-0000-0000-000000000001', true, true); -``` - -## External Dependencies - -- **Authentication API**: https://pj-auth-api.vercel.app (唯一認證方式) - -## Authentication Flow - -``` -┌─────────┐ ┌─────────────┐ ┌──────────────────────────┐ -│ User │────▶│ Frontend │────▶│ pj-auth-api.vercel.app │ -└─────────┘ └─────────────┘ └──────────────────────────┘ - │ │ - │◀────── JWT Token ───────│ - │ - ▼ - ┌─────────────┐ - │ Backend │ (驗證 JWT, 建立 Session) - └─────────────┘ -``` diff --git a/prd.txt b/prd.txt deleted file mode 100644 index d3e2f7a..0000000 --- a/prd.txt +++ /dev/null @@ -1 +0,0 @@ -1. 產品目標 (Product Vision)建立一個跨部門的單一事實來源(Single Source of Truth),降低工程師報工負擔,並提供主管即時的資源負載與專案進度分析。2. 目標用戶與核心痛點用戶角色核心痛點系統解決方案基層工程師報工繁瑣、雜事多、不知優先順序自動化提醒、個人任務面板、一鍵回報阻礙單位主管不知道部屬在忙什麼、資源分配不均部署負荷熱圖 (Heatmap)、多專案健康看板行政/PMO資料散落在各處、進度更新不及時標準化範本、自動化週報、文件版控中心3. 功能需求 (Functional Requirements)A. 任務管理與階層 (Task & Hierarchy)多層級架構: 空間 (Space) > 專案 (Project) > 任務 (Task) > 子任務 (Sub-task)。自定義欄位 (Custom Fields): 支持下拉選單、公式、人員標籤。例如:封裝類型、機台編號、預計良率。多維視角: 支援看板 (Kanban)、甘特圖 (Gantt)、列表 (List) 與行事曆視角。B. 資源管理與協作 (Resource & Collaboration)負載熱圖: 自動統計每人每週分配的 Task 總時數,超過 80% 顯示黃色,超過 100% 顯示紅色。阻礙 (Blocker) 機制: 工程師可將任務標記為 "Blocked",強制要求主管介入排解,並發送即時通知。留言回覆: 任務內部的討論線索,支援 @相關人員,減少 Email 往返。C. 自動化與報告 (Automation & Reporting)觸發器 (Triggers): 當任務狀態變更為「待測試」時,自動通知設備工程師。自動週報: 每週五下午 4:00 自動彙整本週「已完成」與「進行中」的任務發送給主管。4. 非功能需求 (Non-functional Requirements)資訊安全: 支援企業 AD/LDAP 整合,設定細部權限(例如:廠務部不能看研發部的專案內容)。操作稽核: 所有的變更(誰改了死線、誰刪了文件)必須有 Log 紀錄。 \ No newline at end of file