Skip to content

Commit 3c7881c

Browse files
author
Christian Förster
committed
Add pc-server implementation tasks
- Add tasks/ folder with 14 structured implementation tasks - Add workflow documentation for task tracking - Update .gitignore for local Claude config
1 parent 1948f06 commit 3c7881c

16 files changed

+3229
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,5 +45,6 @@ go.work.sum
4545
.DS_Store
4646

4747
.claude
48+
claude.local.md
4849

4950
*.html

tasks/01-project-structure.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Task 01: Project Structure
2+
3+
**Status:** `[ ]` Not started
4+
5+
**Dependencies:** None
6+
7+
## Objective
8+
9+
Set up the directory structure for the PC server without any implementation code.
10+
11+
## Deliverables
12+
13+
### 1. Create directory structure
14+
15+
```
16+
internal/
17+
└── server/
18+
├── api/
19+
├── cache/
20+
├── store/
21+
├── models/
22+
├── analyzer/
23+
└── worker/
24+
25+
cmd/
26+
└── pc-server/
27+
```
28+
29+
### 2. Create placeholder files
30+
31+
Each directory should have a `.go` file with just the package declaration:
32+
33+
- `internal/server/server.go` - `package server`
34+
- `internal/server/config.go` - `package server`
35+
- `internal/server/api/handlers.go` - `package api`
36+
- `internal/server/api/middleware.go` - `package api`
37+
- `internal/server/api/responses.go` - `package api`
38+
- `internal/server/cache/cache.go` - `package cache`
39+
- `internal/server/cache/sqlite.go` - `package cache`
40+
- `internal/server/store/jobs.go` - `package store`
41+
- `internal/server/store/tokens.go` - `package store`
42+
- `internal/server/models/types.go` - `package models`
43+
- `internal/server/analyzer/analyzer.go` - `package analyzer`
44+
- `internal/server/worker/worker.go` - `package worker`
45+
- `cmd/pc-server/main.go` - `package main`
46+
47+
### 3. Verify build
48+
49+
```bash
50+
go build ./...
51+
```
52+
53+
Should complete without errors.
54+
55+
## Tests Required
56+
57+
None for this task (structure only).
58+
59+
## Acceptance Criteria
60+
61+
- [ ] All directories created
62+
- [ ] All placeholder files created with correct package names
63+
- [ ] `go build ./...` succeeds
64+
- [ ] No changes to existing `pkg/` code or `main.go`

tasks/02-models.md

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
# Task 02: Data Models
2+
3+
**Status:** `[ ]` Not started
4+
5+
**Dependencies:** Task 01
6+
7+
## Objective
8+
9+
Define all data types used by the server for requests, responses, and internal state.
10+
11+
## Deliverables
12+
13+
### 1. `internal/server/models/types.go`
14+
15+
```go
16+
package models
17+
18+
import (
19+
"encoding/json"
20+
"time"
21+
)
22+
23+
// ============================================================================
24+
// Request Types
25+
// ============================================================================
26+
27+
// AnalysisRequest represents a request to analyze a file collection
28+
type AnalysisRequest struct {
29+
CollectionID string `json:"collection_id"` // e.g., CKAN package ID
30+
Files []FileInfo `json:"files"`
31+
}
32+
33+
// FileInfo represents a single file in the collection
34+
type FileInfo struct {
35+
FileID string `json:"file_id"` // e.g., CKAN resource ID
36+
Path string `json:"path"` // filesystem path
37+
LastModified time.Time `json:"last_modified"` // for cache invalidation
38+
}
39+
40+
// ============================================================================
41+
// Response Types
42+
// ============================================================================
43+
44+
// JobResponse is returned when querying job status
45+
type JobResponse struct {
46+
JobID string `json:"job_id"`
47+
CollectionID string `json:"collection_id"`
48+
Status JobStatus `json:"status"`
49+
Progress string `json:"progress,omitempty"`
50+
CreatedAt time.Time `json:"created_at"`
51+
CompletedAt *time.Time `json:"completed_at,omitempty"`
52+
Result json.RawMessage `json:"result,omitempty"`
53+
Error string `json:"error,omitempty"`
54+
Cached bool `json:"cached"`
55+
}
56+
57+
// ResultResponse is returned when querying cached results directly
58+
type ResultResponse struct {
59+
CollectionID string `json:"collection_id"`
60+
CachedAt time.Time `json:"cached_at"`
61+
ExpiresAt time.Time `json:"expires_at"`
62+
Result json.RawMessage `json:"result"`
63+
}
64+
65+
// ErrorResponse is returned on errors
66+
type ErrorResponse struct {
67+
Error string `json:"error"`
68+
Code string `json:"code,omitempty"`
69+
Details string `json:"details,omitempty"`
70+
}
71+
72+
// HealthResponse is returned by the health endpoint
73+
type HealthResponse struct {
74+
Status string `json:"status"` // "ok" or "degraded"
75+
Version string `json:"version"`
76+
Timestamp string `json:"timestamp"`
77+
}
78+
79+
// ============================================================================
80+
// Job Status
81+
// ============================================================================
82+
83+
type JobStatus string
84+
85+
const (
86+
JobStatusPending JobStatus = "pending"
87+
JobStatusRunning JobStatus = "running"
88+
JobStatusCompleted JobStatus = "completed"
89+
JobStatusFailed JobStatus = "failed"
90+
)
91+
92+
// ============================================================================
93+
// Internal/Database Types
94+
// ============================================================================
95+
96+
// Job represents a job record in the database
97+
type Job struct {
98+
ID string `db:"id"`
99+
CollectionID string `db:"collection_id"`
100+
FilesHash string `db:"files_hash"`
101+
Status JobStatus `db:"status"`
102+
Progress string `db:"progress"`
103+
Result *string `db:"result"`
104+
Error *string `db:"error"`
105+
CreatedAt time.Time `db:"created_at"`
106+
CompletedAt *time.Time `db:"completed_at"`
107+
}
108+
109+
// ToResponse converts a Job to a JobResponse
110+
func (j *Job) ToResponse() *JobResponse {
111+
resp := &JobResponse{
112+
JobID: j.ID,
113+
CollectionID: j.CollectionID,
114+
Status: j.Status,
115+
Progress: j.Progress,
116+
CreatedAt: j.CreatedAt,
117+
CompletedAt: j.CompletedAt,
118+
Cached: false,
119+
}
120+
if j.Result != nil {
121+
resp.Result = json.RawMessage(*j.Result)
122+
}
123+
if j.Error != nil {
124+
resp.Error = *j.Error
125+
}
126+
return resp
127+
}
128+
129+
// CacheEntry represents a cached analysis result
130+
type CacheEntry struct {
131+
CollectionID string `db:"collection_id"`
132+
FilesHash string `db:"files_hash"`
133+
Result string `db:"result"`
134+
CreatedAt time.Time `db:"created_at"`
135+
ExpiresAt time.Time `db:"expires_at"`
136+
}
137+
138+
// APIToken represents an API token record
139+
type APIToken struct {
140+
TokenHash string `db:"token_hash"`
141+
Name string `db:"name"`
142+
CreatedAt time.Time `db:"created_at"`
143+
ExpiresAt *time.Time `db:"expires_at"`
144+
Active bool `db:"active"`
145+
}
146+
```
147+
148+
### 2. `internal/server/models/types_test.go`
149+
150+
```go
151+
package models
152+
153+
import (
154+
"encoding/json"
155+
"testing"
156+
"time"
157+
)
158+
159+
func TestJobToResponse(t *testing.T) {
160+
// Test basic conversion
161+
// Test with nil Result
162+
// Test with nil Error
163+
// Test with CompletedAt set
164+
}
165+
166+
func TestAnalysisRequestJSON(t *testing.T) {
167+
// Test JSON marshaling/unmarshaling
168+
// Test with various time formats
169+
}
170+
171+
func TestJobStatus(t *testing.T) {
172+
// Test status constants are correct strings
173+
}
174+
```
175+
176+
## Tests Required
177+
178+
- `TestJobToResponse` - Verify Job to JobResponse conversion
179+
- `TestAnalysisRequestJSON` - Verify JSON serialization/deserialization
180+
- `TestJobStatus` - Verify status constants
181+
182+
## Acceptance Criteria
183+
184+
- [ ] All types defined as specified
185+
- [ ] `Job.ToResponse()` method implemented
186+
- [ ] All tests pass
187+
- [ ] `go build ./...` succeeds

0 commit comments

Comments
 (0)