Skip to content

Commit f1635f3

Browse files
committed
standardized struct and interface impl
1 parent b879327 commit f1635f3

11 files changed

Lines changed: 195 additions & 89 deletions

File tree

.serena/memories/2026-01-05-bug-fixes-report.md

Lines changed: 0 additions & 32 deletions
This file was deleted.

.serena/memories/2026-01-05-test-fixes-report.md

Lines changed: 0 additions & 16 deletions
This file was deleted.
Lines changed: 8 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,8 @@
1-
# Core Foundation State (Jan 5, 2026)
2-
3-
## Security & Infrastructure
4-
- **Container Security:** All services (`backend`, `frontend`, `ingestion-worker`) run as non-root users (`appuser`/`nginx`) to prevent container escape.
5-
- **Network Security:** Frontend Nginx binds to unprivileged port 8080.
6-
- **Permissions:** Strict file ownership and read/write permissions for application artifacts and shared volumes.
7-
8-
## Ingestion Pipeline Stability
9-
- **Architecture:** Robust multi-process worker using `pebble`.
10-
- **Reliability:**
11-
- Hard timeouts (30 mins) with auto-kill for stuck processes.
12-
- Resource limits (8 CPUs, 8GB RAM) enforced via Docker.
13-
- Thread safety enforced via environment variables (`OMP_NUM_THREADS=2`).
14-
- **Performance:**
15-
- Worker: 8 concurrent workers processing files.
16-
- Backend: Parallel chunk embedding (5 concurrent routines).
17-
- **Capabilities:**
18-
- Handles large books (e.g., 50MB, 500+ pages).
19-
- Extracts rich metadata (Author, Created Date, Page Count).
20-
- Standardized on Docling v2 API.
21-
22-
## Code Quality & Testing
23-
- **Backend Coverage:** High (>90%) for core adapters (`gemini`, `weaviate`) and repositories (`settings`, `source`).
24-
- **Observability:** Context-aware JSON logging standardized across Backend and Adapters.
25-
- **Frontend Quality:** Full store and component testing coverage for `jobs`, `stats`, `sources`, and `settings`.
26-
- **Standards:** STRICT adherence to `technical-constitution` (Dependency Injection, Interface-based design).
27-
28-
## Known Limitations
29-
- **Ingestion Worker Environment:** Local testing requires manual `pip install pebble`.
30-
- **Progress Reporting:** "Pending" state is a black box. No page-level progress. (Proposal created: `docs/2026-01-04-proposal-worker-realtime-progression.md`)
31-
- **Search API:** Metadata (Author, CreatedAt, PageCount, Type) is now returned in `SearchResult` struct and populated by Weaviate. Frontend integration pending.
1+
The codebase state for `qurio` is significantly ahead of the documentation/bug reports (specifically `2026-01-05-bug-inconsistencies-3.md`).
2+
Verified that:
3+
1. API Envelopes are implemented.
4+
2. Background Janitor is implemented.
5+
3. MCP Context Propagation is correct.
6+
4. TSConfig redundancy is non-existent.
7+
5. Source Entity timestamp inconsistency (`updated_at` vs `lastSyncedAt`) is resolved across DB, Backend, and Frontend.
8+
Future tasks should verify code state before assuming bug reports are accurate.
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
11
- **Logging:** Implemented `apps/backend/internal/logger` package with `ContextHandler` to automatically propagate `correlation_id` from context to JSON logs.
22
- **Worker Logic:** Link discovery logic extracted to pure function `DiscoverLinks` in `apps/backend/internal/worker/link_discovery.go` to separate I/O from business logic.
3-
- **MCP Errors:** `qurio_search` now returns standard JSON-RPC errors (code -32603) for internal failures instead of embedded text errors.
3+
- **MCP Errors:** `qurio_search` now returns standard JSON-RPC errors (code -32603) for internal failures instead of embedded text errors.
4+
- **Ingestion Worker:** Refactored `handle_file_task` to return `list[dict]` matching `handle_web_task`, removing brittle manual list wrapping.
5+
- **Data Consistency:**
6+
- Standardized `Source` entity across stack.
7+
- Backend `Source` struct now includes `UpdatedAt` field.
8+
- Repository fetches `updated_at` column.
9+
- Frontend `Source` interface uses `updated_at` (removed `lastSyncedAt`).
10+
- **Metadata Exposure:** Promoted metadata fields (`Author`, `CreatedAt`, `PageCount`, `Language`, `Type`, `SourceID`, `URL`) to top-level fields in `SearchResult` struct and refactored Weaviate adapter/MCP handler to use strong typing.

.serena/memories/mcp_tools_spec.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@ Qurio exposes its knowledge base via the Model Context Protocol (MCP).
2020
- `filters` (object): Metadata filters (e.g. `type='code'`, `language='go'`).
2121

2222
**Output:**
23-
- List of results with Title, Type, Language, SourceID, and Content.
23+
- List of results with strong-typed top-level metadata:
24+
- `Content`: The text snippet.
25+
- `Title`, `Author`, `CreatedAt`, `PageCount`: Document metadata.
26+
- `Type`, `Language`: Content classification.
27+
- `SourceID`, `URL`: Origin tracking.
2428
- Includes explicit instruction: "Use qurio_read_page(url=\"...\") to read the full content of any result."
2529

2630
### `qurio_list_sources`

apps/backend/features/source/repo.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func (r *PostgresRepo) UpdateStatus(ctx context.Context, id, status string) erro
3838
}
3939

4040
func (r *PostgresRepo) List(ctx context.Context) ([]Source, error) {
41-
query := `SELECT id, type, url, status, max_depth, exclusions, name FROM sources WHERE deleted_at IS NULL ORDER BY created_at DESC`
41+
query := `SELECT id, type, url, status, max_depth, exclusions, name, updated_at FROM sources WHERE deleted_at IS NULL ORDER BY created_at DESC`
4242
rows, err := r.db.QueryContext(ctx, query)
4343
if err != nil {
4444
return nil, err
@@ -48,7 +48,7 @@ func (r *PostgresRepo) List(ctx context.Context) ([]Source, error) {
4848
var sources []Source
4949
for rows.Next() {
5050
var s Source
51-
if err := rows.Scan(&s.ID, &s.Type, &s.URL, &s.Status, &s.MaxDepth, pq.Array(&s.Exclusions), &s.Name); err != nil {
51+
if err := rows.Scan(&s.ID, &s.Type, &s.URL, &s.Status, &s.MaxDepth, pq.Array(&s.Exclusions), &s.Name, &s.UpdatedAt); err != nil {
5252
return nil, err
5353
}
5454
sources = append(sources, s)
@@ -58,8 +58,8 @@ func (r *PostgresRepo) List(ctx context.Context) ([]Source, error) {
5858

5959
func (r *PostgresRepo) Get(ctx context.Context, id string) (*Source, error) {
6060
s := &Source{}
61-
query := `SELECT id, type, url, status, max_depth, exclusions, name FROM sources WHERE id = $1 AND deleted_at IS NULL`
62-
err := r.db.QueryRowContext(ctx, query, id).Scan(&s.ID, &s.Type, &s.URL, &s.Status, &s.MaxDepth, pq.Array(&s.Exclusions), &s.Name)
61+
query := `SELECT id, type, url, status, max_depth, exclusions, name, updated_at FROM sources WHERE id = $1 AND deleted_at IS NULL`
62+
err := r.db.QueryRowContext(ctx, query, id).Scan(&s.ID, &s.Type, &s.URL, &s.Status, &s.MaxDepth, pq.Array(&s.Exclusions), &s.Name, &s.UpdatedAt)
6363
if err != nil {
6464
return nil, err
6565
}

apps/backend/features/source/repo_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,16 @@ func TestPostgresRepo_Get(t *testing.T) {
4444

4545
repo := source.NewPostgresRepo(db)
4646

47-
rows := sqlmock.NewRows([]string{"id", "type", "url", "status", "max_depth", "exclusions", "name"}).
48-
AddRow("id1", "web", "http://example.com", "active", 2, "{}", "Test")
47+
rows := sqlmock.NewRows([]string{"id", "type", "url", "status", "max_depth", "exclusions", "name", "updated_at"}).
48+
AddRow("id1", "web", "http://example.com", "active", 2, "{}", "Test", "2024-01-01T00:00:00Z")
4949

50-
mock.ExpectQuery(regexp.QuoteMeta(`SELECT id, type, url, status, max_depth, exclusions, name FROM sources WHERE id = $1 AND deleted_at IS NULL`)).
50+
mock.ExpectQuery(regexp.QuoteMeta(`SELECT id, type, url, status, max_depth, exclusions, name, updated_at FROM sources WHERE id = $1 AND deleted_at IS NULL`)).
5151
WithArgs("id1").
5252
WillReturnRows(rows)
5353

5454
s, err := repo.Get(context.Background(), "id1")
5555
assert.NoError(t, err)
5656
assert.Equal(t, "id1", s.ID)
5757
assert.Equal(t, "Test", s.Name)
58+
assert.Equal(t, "2024-01-01T00:00:00Z", s.UpdatedAt)
5859
}

apps/backend/features/source/source.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ type Source struct {
2323
MaxDepth int `json:"max_depth"`
2424
Exclusions []string `json:"exclusions"`
2525
Name string `json:"name"`
26+
UpdatedAt string `json:"updated_at"`
2627
}
2728

2829
type SourcePage struct {

apps/frontend/src/features/sources/source.store.spec.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,19 @@ describe('Source Store', () => {
9393
expect(store.isLoading).toBe(false)
9494
expect(store.error).toContain('Failed to add source')
9595
})
96+
97+
it('should map updated_at correctly', async () => {
98+
const store = useSourceStore()
99+
// @ts-ignore - simulating API response with field that doesn't exist on type yet
100+
const mockSources = [{ id: '1', name: 'Test', updated_at: '2024-01-01' }]
101+
102+
global.fetch = vi.fn().mockResolvedValue({
103+
ok: true,
104+
json: () => Promise.resolve({ data: mockSources })
105+
})
106+
107+
await store.fetchSources()
108+
// This assertion relies on the field existing on the interface
109+
expect(store.sources[0].updated_at).toBe('2024-01-01')
110+
})
96111
})

apps/frontend/src/features/sources/source.store.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export interface Source {
1919
type?: string
2020
url?: string
2121
status?: string
22-
lastSyncedAt?: string
22+
updated_at?: string
2323
max_depth?: number
2424
exclusions?: string[]
2525
chunks?: Chunk[]

0 commit comments

Comments
 (0)