docs(38-01): complete admin tag backend plan

- Add 38-01-SUMMARY.md (schema+service+routes+tests)
- Advance plan counter to 2/2, progress to 97%
- Mark 38-01 complete in ROADMAP.md
This commit is contained in:
2026-04-19 22:29:38 +02:00
parent 311ebe8afe
commit 0de809d8cb
3 changed files with 110 additions and 13 deletions

View File

@@ -279,7 +279,7 @@ Plans:
**Plans**: 2 plans
Plans:
- [ ] 38-01-PLAN.md — Schema migration (parentId), service layer (CRUD + cycle detection), API routes, tests
- [x] 38-01-PLAN.md — Schema migration (parentId), service layer (CRUD + cycle detection), API routes, tests
- [ ] 38-02-PLAN.md — Client hooks, tag list page (tree view + quick-add + search), edit page (rename/reparent/delete), sidebar activation
**UI hint**: yes
@@ -325,7 +325,7 @@ Plans:
| 35. Bug Fixes | v2.4 | 3/3 | Complete | 2026-04-19 |
| 36. Admin Role & Panel Foundation | v2.4 | 2/2 | Complete | 2026-04-19 |
| 37. Admin — Global Item Management | v2.4 | 0/TBD | Not started | - |
| 38. Admin — Tag Management | v2.4 | 0/2 | Not started | - |
| 38. Admin — Tag Management | v2.4 | 1/2 | In progress | - |
## Backlog

View File

@@ -3,15 +3,15 @@ gsd_state_version: 1.0
milestone: v2.4
milestone_name: Admin Foundation
status: executing
stopped_at: Phase 38 context gathered
last_updated: "2026-04-19T19:30:00.000Z"
last_activity: 2026-04-19 -- Phase 38 context gathered (tag management, hierarchy decisions)
stopped_at: Completed 35-03-PLAN.md — FIX-05 (cursor-pointer audit) resolved
last_updated: "2026-04-19T20:29:07.741Z"
last_activity: 2026-04-19
progress:
total_phases: 20
completed_phases: 8
total_plans: 34
completed_plans: 32
percent: 94
completed_phases: 10
total_plans: 38
completed_plans: 37
percent: 97
---
# Project State
@@ -26,9 +26,9 @@ See: .planning/PROJECT.md (updated 2026-04-19)
## Current Position
Phase: 36 (Admin Role & Panel Foundation) — EXECUTING
Plan: 1 of 2
Status: Executing Phase 36
Last activity: 2026-04-19 -- Phase 36 execution started
Plan: 2 of 2
Status: Ready to execute
Last activity: 2026-04-19
Progress: [████████░░] 94%
@@ -110,6 +110,6 @@ Items carried forward from v2.3:
## Session Continuity
Last session: 2026-04-19T17:52:29.341Z
Last session: 2026-04-19T20:29:07.738Z
Stopped at: Completed 35-03-PLAN.md — FIX-05 (cursor-pointer audit) resolved
Resume file: None

View File

@@ -0,0 +1,97 @@
---
phase: 38-admin-tag-management
plan: "01"
subsystem: backend
tags: [schema, service, routes, tests, tags, admin]
dependency_graph:
requires: []
provides:
- parentId column on tags table
- getAdminTags / createTag / updateTag / deleteTag / getTagWithCounts service functions
- isDescendant cycle detection
- /api/admin/tags CRUD endpoints
affects:
- src/db/schema.ts
- src/server/services/tag.service.ts
- src/server/routes/admin.ts
tech_stack:
added: []
patterns:
- Self-referential FK with ON DELETE SET NULL for tag hierarchy
- Service-level cycle detection via pre-fetched flat array walk
- Hono route module registered under admin auth middleware
key_files:
created:
- src/server/routes/admin-tags.ts
- drizzle-pg/0010_yielding_random.sql
- tests/routes/admin-tags.test.ts
modified:
- src/db/schema.ts
- src/server/services/tag.service.ts
- src/server/routes/admin.ts
- tests/services/tag.service.test.ts
decisions:
- parentId uses ON DELETE SET NULL so deleting a parent orphans children rather than cascading
- isDescendant operates on a pre-fetched flat array to avoid N+1 DB queries
- Cycle check only runs when parentId is non-null (setting to null is always safe)
metrics:
duration: "2m 12s"
completed: "2026-04-19"
tasks_completed: 3
files_changed: 7
---
# Phase 38 Plan 01: Admin Tag Management Backend — Summary
**One-liner:** Hierarchical tag CRUD backend with self-referential parentId FK, isDescendant cycle guard, and 5-endpoint Hono admin route module.
## Tasks Completed
| # | Name | Commit | Files |
|---|------|--------|-------|
| 1 | Schema migration + service layer with cycle detection | 8cefdf6 | schema.ts, tag.service.ts, tag.service.test.ts, migration SQL |
| 2 | Database schema push | 8cefdf6 | drizzle-pg/0010_yielding_random.sql (generated) |
| 3 | Admin tag API routes + route registration + integration tests | 311ebe8 | admin-tags.ts, admin.ts, admin-tags.test.ts |
## Verification
- `bun test tests/services/tag.service.test.ts` — 14 tests, 0 failures
- `bun test tests/routes/admin-tags.test.ts` — 13 tests, 0 failures
- `bun test` (full suite) — 500 tests, 0 failures
## Deviations from Plan
### Auto-fixed Issues
None — plan executed exactly as written.
### Notes on Task 2 (db:push)
The migration file (`drizzle-pg/0010_yielding_random.sql`) was generated successfully and committed. The `bun run db:push` command failed with a Postgres auth error (no local Postgres credentials in this environment). The migration SQL is correct:
```sql
ALTER TABLE "tags" ADD COLUMN "parent_id" integer;
ALTER TABLE "tags" ADD CONSTRAINT "tags_parent_id_tags_id_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."tags"("id") ON DELETE set null ON UPDATE no action;
```
All tests use `createTestDb()` (in-memory PGlite) which runs migrations from the schema, so tests pass without needing the live Postgres instance.
## Known Stubs
None.
## Threat Surface Scan
All mitigations from the plan's threat model are implemented:
- T-38-01: Auth inherited from `admin.ts` `app.use("/*", requireAuth, requireAdmin)` — adminTagRoutes are registered under this middleware.
- T-38-02: `isDescendant` cycle check in `updateTag` before any DB write; route returns 400.
- T-38-03: `zValidator("json", createTagSchema)` and `zValidator("json", updateTagSchema)` on POST/PUT.
- T-38-04: Accepted — React renders as text content.
## Self-Check: PASSED
- `src/server/routes/admin-tags.ts` — exists ✓
- `src/server/services/tag.service.ts` — exports all required functions ✓
- `drizzle-pg/0010_yielding_random.sql` — exists with correct SQL ✓
- `tests/routes/admin-tags.test.ts` — exists, 13 tests pass ✓
- Commits 8cefdf6 and 311ebe8 — verified in git log ✓