@gi4nks/ph 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -1
- package/README.md +34 -18
- package/dist/cli.js +3383 -3700
- package/dist/cli.js.map +1 -1
- package/docs/evals/context-retrieval-golden.json +17 -1
- package/docs/ph-architecture-review.html +179 -0
- package/docs/ph-manual.md +88 -73
- package/docs/ph-operational-memory-analysis.html +104 -0
- package/docs/plans/2026-10-10-operational-memory.md +192 -0
- package/docs/specs/SPEC-001-architecture-cli.md +100 -72
- package/docs/specs/SPEC-002-database-layer.md +22 -3
- package/docs/specs/SPEC-003-capture-modes.md +22 -9
- package/docs/specs/SPEC-005-search.md +46 -11
- package/docs/specs/SPEC-006-analysis-pipeline.md +8 -6
- package/docs/specs/SPEC-007-memory-summaries.md +19 -19
- package/docs/specs/SPEC-008-embeddings.md +8 -2
- package/docs/specs/SPEC-009-retention-archive.md +2 -2
- package/docs/specs/SPEC-010-remote-sync.md +77 -103
- package/docs/specs/SPEC-011-mcp-server.md +7 -4
- package/docs/specs/SPEC-012-tui-browser.md +30 -96
- package/docs/specs/SPEC-015-config-env.md +4 -0
- package/docs/specs/SPEC-016-operational-memory.md +48 -0
- package/docs/specs/SPEC-017-architecture-consolidation.md +63 -0
- package/docs/specs/SPEC-018-interaction-inspection-and-recall-integrity.md +164 -0
- package/docs/specs/SPEC-019-network-capture-and-ranking-integrity.md +36 -0
- package/docs/specs/SPEC-INDEX.md +12 -7
- package/docs/specs/SPEC-ISSUES.md +104 -18
- package/hooks/codex/README.md +1 -1
- package/hooks/codex/ph-hook.sh +2 -1
- package/package.json +1 -7
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
- **Source**: reverse-engineered from code (files listed in §8)
|
|
8
8
|
|
|
9
9
|
## 1. Purpose
|
|
10
|
-
Find prompts by full-text (FTS5 over prompt+response), by metadata filters
|
|
10
|
+
Find prompts by full-text (FTS5 over prompt+response+derived analysis metadata), by metadata filters
|
|
11
11
|
(tool/project/language/role/tag/starred/quality/relevance/date range), or by
|
|
12
12
|
semantic similarity (sqlite-vec, SPEC-008). Backs `ph search`, the TUI text
|
|
13
13
|
filter, the MCP `search_prompts*` tools and the HTTP `/api/prompts/search`.
|
|
@@ -30,8 +30,9 @@ filter, the MCP `search_prompts*` tools and the HTTP `/api/prompts/search`.
|
|
|
30
30
|
1. Build identical filters for two paths: FTS (JOIN with alias `p.`) and scan
|
|
31
31
|
(direct `prompts`). Filters use `json_extract` on metadata for
|
|
32
32
|
project/language/role/tag/starred/quality/relevance (db/index.ts:252-297).
|
|
33
|
-
2. With a text query → FTS path: `prompts_fts
|
|
34
|
-
prefixed `p.`
|
|
33
|
+
2. With a text query → FTS path: `prompts_fts` plus the derived-analysis FTS index are matched and deduplicated by interaction, filters
|
|
34
|
+
prefixed `p.` and ordered by FTS5 BM25 relevance, then recency for ties
|
|
35
|
+
(db/index.ts:300-320).
|
|
35
36
|
3. Without text → scan path: `SELECT * FROM prompts WHERE … ORDER BY timestamp
|
|
36
37
|
DESC LIMIT ?` (db/index.ts:322-330).
|
|
37
38
|
4. Date range: `since`/`until` become timestamp comparisons.
|
|
@@ -43,21 +44,37 @@ that project before the final result limit. Requires embeddings (SPEC-008).
|
|
|
43
44
|
Because sqlite-vec applies `k` before ordinary metadata predicates, scoped
|
|
44
45
|
queries currently examine all stored vectors before filtering and limiting.
|
|
45
46
|
|
|
47
|
+
The CLI cosine-similarity path uses the legacy embedding table. When scoped, it
|
|
48
|
+
loads only vectors joined to prompts whose valid metadata names the selected
|
|
49
|
+
project. It does not load all vectors and issue one `getById` query per vector.
|
|
50
|
+
|
|
46
51
|
### 4.3 CLI (src/commands/search.ts)
|
|
47
|
-
`ph search`
|
|
48
|
-
|
|
52
|
+
`ph search` defaults to five results and scopes to the detected current project.
|
|
53
|
+
`--all` searches every project; `--project` selects another project. If no
|
|
54
|
+
project is detected, it labels and searches all saved work. `-i` is a text-only
|
|
55
|
+
compatibility flag (SPEC-001); `--archive` queries `prompts_archive` (SPEC-009).
|
|
49
56
|
|
|
50
57
|
## 5. Invariants & Business rules
|
|
51
58
|
- FTS path is used ONLY when a text query exists; otherwise full scan + filters.
|
|
52
59
|
- `tag` filter is a JSON-array LIKE `%"tag"%` — substring match on the serialized
|
|
53
60
|
array, not a JSON array membership check (db/index.ts:276-282).
|
|
54
|
-
- `limit` defaults to
|
|
61
|
+
- CLI `limit` defaults to five and must be between 1 and 1000.
|
|
62
|
+
- Raw prompts/responses and derived analysis remain in separate FTS5 indexes.
|
|
63
|
+
Text search ranks each source list by BM25, then combines their ordinal ranks
|
|
64
|
+
with Reciprocal Rank Fusion (`1 / (60 + rank)`). Results order by fused score,
|
|
65
|
+
then timestamp DESC and ID DESC for deterministic ties. Raw-only query order
|
|
66
|
+
retains its BM25 ordering; an interaction found in both sources receives
|
|
67
|
+
evidence from both ranked lists.
|
|
68
|
+
- The user-facing search defaults to the current detected project; `--all`
|
|
69
|
+
explicitly broadens the search. The archive path remains global.
|
|
55
70
|
- Project-scoped semantic search returns the nearest matching prompts within
|
|
56
71
|
that project, even when closer prompts from other projects exist.
|
|
72
|
+
- Project-scoped CLI semantic search loads its vectors with one project join;
|
|
73
|
+
malformed metadata is ignored without aborting the search.
|
|
57
74
|
|
|
58
75
|
## 6. UI / UX surface
|
|
59
76
|
`ph search` output: `#id tool date [tags] [exit:N]` + truncated prompt
|
|
60
|
-
(printResults); `--full` shows
|
|
77
|
+
(printResults); `--full` shows complete prompt and response and prints a `ph show <id>` hint. Search text also matches derived summary, insights, and technical decisions; a separate FTS index preserves raw prompt/response BM25 ranking.
|
|
61
78
|
|
|
62
79
|
## 7. Acceptance criteria (Given/When/Then)
|
|
63
80
|
- **G1**: Given prompts with text "goroutines" and "react", When `search({query:
|
|
@@ -77,6 +94,16 @@ TUI (SPEC-001); `--archive` queries `prompts_archive` (SPEC-009).
|
|
|
77
94
|
- **G6**: Given an unrelated project has the nearest vector, When
|
|
78
95
|
`searchSemantic(query, 1, "go-play")` runs, Then it returns the nearest
|
|
79
96
|
vector belonging to `go-play` (src/db/index.ts, project scope before limit).
|
|
97
|
+
- **G7**: Given the CLI runs inside a detected project, When `ph search <query>`
|
|
98
|
+
runs, Then it returns at most five matches from that project by default.
|
|
99
|
+
- **G8**: Given `ph search --all <query>`, When the command runs, Then results
|
|
100
|
+
may include every project and the output labels the global scope.
|
|
101
|
+
- **G9**: Given matching prompts with different FTS5 scores, When the text
|
|
102
|
+
search runs, Then matches from raw text and analysis are fused by RRF and
|
|
103
|
+
tied results resolve by timestamp and ID (SPEC-019).
|
|
104
|
+
- **G10**: Given embeddings across projects, When CLI semantic search targets
|
|
105
|
+
one project, Then the database returns only that project's vectors in one
|
|
106
|
+
joined query and the command does not call `getById` for each vector.
|
|
80
107
|
|
|
81
108
|
## 8. Key implementation map
|
|
82
109
|
| Concern | File(s) |
|
|
@@ -84,6 +111,7 @@ TUI (SPEC-001); `--archive` queries `prompts_archive` (SPEC-009).
|
|
|
84
111
|
| Dual-path search | src/db/index.ts:243-330 |
|
|
85
112
|
| Semantic KNN | src/db/index.ts (`searchSemantic`) |
|
|
86
113
|
| CLI output | src/commands/search.ts, src/display/print.ts:33-60 |
|
|
114
|
+
| Search scope and defaults | src/commands/search.ts |
|
|
87
115
|
| SearchOptions | src/types.ts:54-68 |
|
|
88
116
|
|
|
89
117
|
## 9. Open questions / discrepancies
|
|
@@ -91,10 +119,17 @@ TUI (SPEC-001); `--archive` queries `prompts_archive` (SPEC-009).
|
|
|
91
119
|
`auth` (substring). JSON array membership would be stricter.
|
|
92
120
|
- FTS path columns need the `p.` prefix; a new filter added to only one path
|
|
93
121
|
silently breaks the other (dual maintenance).
|
|
94
|
-
- Project-scoped semantic search
|
|
95
|
-
before metadata filtering because sqlite-vec's KNN limit
|
|
96
|
-
predicate.
|
|
97
|
-
reduce
|
|
122
|
+
- Project-scoped semantic search on the sqlite-vec KNN path still evaluates the
|
|
123
|
+
full vector set before metadata filtering because sqlite-vec's KNN limit
|
|
124
|
+
precedes the join predicate. The CLI cosine path is project-filtered at load
|
|
125
|
+
time. A project-aware vector index may reduce KNN cost if vector counts make
|
|
126
|
+
it material.
|
|
127
|
+
- Semantic search has two implementations: CLI `--semantic` loads the legacy
|
|
128
|
+
BLOB table and computes cosine similarity in TypeScript; task context, HTTP,
|
|
129
|
+
and MCP use `sqlite-vec`. Embeddings do not record their producing model, and
|
|
130
|
+
`ph embed-all` fills missing rows without rebuilding vectors after a model
|
|
131
|
+
change. Consolidate the search contract and add explicit vector profile and
|
|
132
|
+
rebuild behavior (ISSUE-026).
|
|
98
133
|
|
|
99
134
|
## 10. Related
|
|
100
135
|
- SPEC-002 (primitives), SPEC-008 (embeddings), SPEC-009 (archive),
|
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
|
|
9
9
|
## 1. Purpose
|
|
10
10
|
Turns raw prompts into structured knowledge: role/tags/relevance/quality plus
|
|
11
|
-
summary/key_insights/technical_decisions via an LLM. Results
|
|
12
|
-
(SPEC-002)
|
|
11
|
+
summary/key_insights/technical_decisions via an LLM. Results enrich interaction
|
|
12
|
+
metadata (SPEC-002) and derived full-text search. They do not create active
|
|
13
|
+
memories or project summaries; explicit approval promotes durable knowledge.
|
|
13
14
|
|
|
14
15
|
## 2. Scope
|
|
15
16
|
- **In scope**: ANALYSIS_PROMPT, parseAnalysisResponse, analyzePrompt, mergeMetadata,
|
|
@@ -41,8 +42,8 @@ contract; input truncated to 2000 chars (:49). No markdown fences allowed.
|
|
|
41
42
|
metadata unless `force=true` (:136-183).
|
|
42
43
|
|
|
43
44
|
### 4.4 Batch (src/analyzer/index.ts:205)
|
|
44
|
-
`analyzeAll(db, provider, opts)` iterates unanalyzed prompts
|
|
45
|
-
|
|
45
|
+
`analyzeAll(db, provider, opts)` iterates unanalyzed prompts and updates only
|
|
46
|
+
per-interaction metadata, including lifecycle outcome fields. CLI `ph analyze` with `--limit/--force/--prune/
|
|
46
47
|
--dry-run`.
|
|
47
48
|
|
|
48
49
|
### 4.5 Background (src/background/analyzer.ts:7)
|
|
@@ -60,8 +61,9 @@ deletes low-score ones (SPEC-009 cross-ref).
|
|
|
60
61
|
- Input truncated to 2000 chars before the LLM.
|
|
61
62
|
|
|
62
63
|
## 6. UI / UX surface
|
|
63
|
-
`ph analyze` progress output;
|
|
64
|
-
|
|
64
|
+
`ph analyze` progress output; `ph show` displays full analysis and state;
|
|
65
|
+
`ph status` lists bounded recent background/remote lifecycle outcomes without
|
|
66
|
+
printing prompt or response content.
|
|
65
67
|
|
|
66
68
|
## 7. Acceptance criteria (Given/When/Then)
|
|
67
69
|
- **G1**: Given an LLM response with fenced JSON, When `parseAnalysisResponse`
|
|
@@ -7,11 +7,10 @@
|
|
|
7
7
|
- **Source**: reverse-engineered from code (files listed in §8)
|
|
8
8
|
|
|
9
9
|
## 1. Purpose
|
|
10
|
-
Project-level knowledge
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
2026-06-12-ph-evolution-design.md).
|
|
10
|
+
Project-level knowledge explicitly promoted from reviewed proposals or MCP
|
|
11
|
+
decisions: an append-only `memories` table plus one merged, deduplicated
|
|
12
|
+
`project_summaries` row per project. Current automatic analysis enriches prompt
|
|
13
|
+
metadata only and does not write to these active stores.
|
|
15
14
|
|
|
16
15
|
## 2. Scope
|
|
17
16
|
- **In scope**: memories CRUD, upsertProjectMemory (append-only),
|
|
@@ -19,7 +18,7 @@ knowledge base" core of the evolution plan (docs/superpowers/specs/
|
|
|
19
18
|
tracking.
|
|
20
19
|
- **Out of scope**: LLM analysis itself (SPEC-006), MCP exposure (SPEC-011),
|
|
21
20
|
context output (SPEC-014).
|
|
22
|
-
- **Entry points**:
|
|
21
|
+
- **Entry points**: `ph close`, MCP `save_decision`, `ph memory-migrate`, `ph context`,
|
|
23
22
|
`ph timeline`, MCP tools, `save_decision`.
|
|
24
23
|
|
|
25
24
|
## 3. Data Model
|
|
@@ -33,13 +32,13 @@ knowledge base" core of the evolution plan (docs/superpowers/specs/
|
|
|
33
32
|
|
|
34
33
|
## 4. Flows
|
|
35
34
|
|
|
36
|
-
### 4.1 Append-only memory (src/db/
|
|
35
|
+
### 4.1 Append-only memory (src/db/memory-repository.ts)
|
|
37
36
|
`upsertProjectMemory({project, prompt_id, summary, key_insights,
|
|
38
|
-
technical_decisions, git_context_snapshot})`
|
|
39
|
-
|
|
37
|
+
technical_decisions, git_context_snapshot})` inserts a new row for an explicitly
|
|
38
|
+
approved or saved decision; automatic per-prompt analysis does not call it. `searchMemories(project, limit)` returns latest N;
|
|
40
39
|
`getAllMemoriesByProject` (774) ascending for timelines.
|
|
41
40
|
|
|
42
|
-
### 4.2 Curated summary (src/db/
|
|
41
|
+
### 4.2 Curated summary (src/db/memory-repository.ts)
|
|
43
42
|
`upsertProjectSummary(params)` upserts ONE row per project: merges new insights/
|
|
44
43
|
decisions into existing arrays, deduping by content; updates prompt_count,
|
|
45
44
|
first/last_analyzed. `getProjectSummary(project)` (690).
|
|
@@ -52,7 +51,8 @@ first/last_analyzed. `getProjectSummary(project)` (690).
|
|
|
52
51
|
`recordMemoryAccess(id)` bumps access_count + last_accessed (used by MCP reads).
|
|
53
52
|
|
|
54
53
|
## 5. Invariants & Business rules
|
|
55
|
-
- memories = append-only timeline; project_summaries = 1 row per project.
|
|
54
|
+
- memories = append-only timeline of promoted knowledge; project_summaries = 1 row per project.
|
|
55
|
+
- Automatic analysis writes source interaction metadata only; explicit promotion owns active knowledge.
|
|
56
56
|
- Summary merge dedups by insight/decision CONTENT (not id).
|
|
57
57
|
- `save_decision` writes to summaries too (MCP, SPEC-011).
|
|
58
58
|
- Access stats are write-on-read (recordMemoryAccess on MCP/timeline reads).
|
|
@@ -63,15 +63,15 @@ PreviewPane memory tab (SPEC-012); `ph context` markdown output; `ph timeline`.
|
|
|
63
63
|
## 7. Acceptance criteria (Given/When/Then)
|
|
64
64
|
- **G1**: Given two analyses for the same project, When `upsertProjectMemory`
|
|
65
65
|
runs twice, Then `memories` contains 2 rows (append-only)
|
|
66
|
-
(src/db/
|
|
66
|
+
(src/db/memory-repository.ts).
|
|
67
67
|
- **G2**: Given two summaries with the same insight, When `upsertProjectSummary`
|
|
68
68
|
runs twice, Then the insight appears once in key_insights and there is 1 row
|
|
69
|
-
(src/db/
|
|
69
|
+
(src/db/memory-repository.ts).
|
|
70
70
|
- **G3**: Given `upsertProjectSummary` with prompt_count 3, When the row is
|
|
71
71
|
read, Then prompt_count is 3 and first/last_analyzed are set
|
|
72
|
-
(src/db/
|
|
72
|
+
(src/db/memory-repository.ts).
|
|
73
73
|
- **G4**: Given `recordMemoryAccess(id)`, When run, Then access_count
|
|
74
|
-
increments and last_accessed updates (src/db/
|
|
74
|
+
increments and last_accessed updates (src/db/memory-repository.ts).
|
|
75
75
|
- **G5**: Given `ph memory-migrate` on a DB with memories but no summaries,
|
|
76
76
|
When run, Then project_summaries rows are backfilled
|
|
77
77
|
(src/commands/memory-migrate.ts).
|
|
@@ -79,9 +79,9 @@ PreviewPane memory tab (SPEC-012); `ph context` markdown output; `ph timeline`.
|
|
|
79
79
|
## 8. Key implementation map
|
|
80
80
|
| Concern | File(s) |
|
|
81
81
|
|---|---|
|
|
82
|
-
| Memories CRUD + upsert | src/db/
|
|
83
|
-
| Summary upsert/merge + read | src/db/
|
|
84
|
-
| Access tracking | src/db/
|
|
82
|
+
| Memories CRUD + upsert | src/db/memory-repository.ts |
|
|
83
|
+
| Summary upsert/merge + read | src/db/memory-repository.ts |
|
|
84
|
+
| Access tracking | src/db/memory-repository.ts |
|
|
85
85
|
| Migrate command | src/commands/memory-migrate.ts |
|
|
86
86
|
| Evolution design | docs/superpowers/specs/2026-06-12-ph-evolution-design.md |
|
|
87
87
|
|
|
@@ -92,5 +92,5 @@ PreviewPane memory tab (SPEC-012); `ph context` markdown output; `ph timeline`.
|
|
|
92
92
|
but their use beyond storage is undocumented.
|
|
93
93
|
|
|
94
94
|
## 10. Related
|
|
95
|
-
- SPEC-006 (analysis
|
|
95
|
+
- SPEC-006 (interaction analysis metadata), SPEC-011 (MCP tools), SPEC-014 (context/
|
|
96
96
|
timeline). No tests (SPEC-ISSUES-007) — G1/G2/G3 are prime vitest targets.
|
|
@@ -82,8 +82,14 @@ unreachable.
|
|
|
82
82
|
## 9. Open questions / discrepancies
|
|
83
83
|
- 768 is hard-coded in the vec0 DDL; a model with a different dim fails at
|
|
84
84
|
insert time with a vec0 error.
|
|
85
|
-
- `searchSemantic`
|
|
86
|
-
|
|
85
|
+
- Project-scoped `searchSemantic` requests all vectors as the KNN candidate set
|
|
86
|
+
and applies the project predicate in SQL afterward; large databases may pay
|
|
87
|
+
full-index KNN cost first (`src/db/semantic-index.ts:17-35`).
|
|
88
|
+
- CLI semantic search uses the legacy BLOB vectors and a TypeScript cosine
|
|
89
|
+
ranking path, while context, HTTP, and MCP use `sqlite-vec`. Vector rows do
|
|
90
|
+
not retain their producing model, and `ph embed-all` fills missing vectors
|
|
91
|
+
rather than rebuilding after a model change. The same query can therefore
|
|
92
|
+
return different results or compare incompatible vector spaces (ISSUE-026).
|
|
87
93
|
|
|
88
94
|
## 10. Related
|
|
89
95
|
- SPEC-005 (semantic search), SPEC-014 (cluster/cohesion use embeddings),
|
|
@@ -75,8 +75,8 @@ TUI header shows `N prompts (M archived)` (Header.tsx archiveCount prop);
|
|
|
75
75
|
| Concern | File(s) |
|
|
76
76
|
|---|---|
|
|
77
77
|
| Retention policy + selection | src/commands/cleanup.ts:88-133 |
|
|
78
|
-
| Archive move (transaction) | src/db/
|
|
79
|
-
| Archive search + purge + stats | src/db/
|
|
78
|
+
| Archive move (transaction) | src/db/archive-repository.ts |
|
|
79
|
+
| Archive search + purge + stats | src/db/archive-repository.ts |
|
|
80
80
|
| Config keys | src/config/index.ts:18-21 |
|
|
81
81
|
| TUI archive count | src/ui/Header.tsx, BrowseApp.tsx:793 |
|
|
82
82
|
|
|
@@ -1,106 +1,80 @@
|
|
|
1
|
-
# SPEC-010
|
|
1
|
+
# SPEC-010 — Remote sync and HTTP server
|
|
2
2
|
|
|
3
3
|
- **ID**: SPEC-010
|
|
4
4
|
- **Cluster**: Sync
|
|
5
|
-
- **Status**: Implemented
|
|
6
|
-
- **Date**: 2026-
|
|
7
|
-
- **Source**:
|
|
8
|
-
|
|
9
|
-
##
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
##
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
- `
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
-
|
|
29
|
-
|
|
30
|
-
##
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
`
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
- **
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
- **
|
|
74
|
-
|
|
75
|
-
- **
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
## 8. Key implementation map
|
|
83
|
-
| Concern | File(s) |
|
|
84
|
-
|---|---|
|
|
85
|
-
| HTTP server + endpoints | src/server/index.ts:9-164 |
|
|
86
|
-
| Push/pull/status | src/commands/remote.ts:37-160 |
|
|
87
|
-
| Background push | src/commands/log.ts:65-85 |
|
|
88
|
-
| Config/env | src/config/index.ts:15-18, remote.ts:6-8 |
|
|
89
|
-
| Dedup primitive | src/db/index.ts:403-407 |
|
|
90
|
-
|
|
91
|
-
## 9. Open questions / discrepancies
|
|
92
|
-
- ~~sync_hash excludes args/workdir~~ Fixed 2026-08-08: shared `syncHash`
|
|
93
|
-
helper (src/utils/syncHash.ts) — args included only when present, backward
|
|
94
|
-
compatible with the legacy `tool|prompt|response` hash.
|
|
95
|
-
- ~~The server NEVER verifies `remoteApiKey`~~ Fixed 2026-08-08: auth gate in
|
|
96
|
-
`createRequestHandler` (Bearer + timingSafeEqual) on every endpoint except
|
|
97
|
-
/health when `remoteApiKey` is configured.
|
|
98
|
-
- `remoteLastPush` uses the CLIENT clock; clock skew between laptops breaks
|
|
99
|
-
incremental sync.
|
|
100
|
-
- No pagination beyond the 10000/5000 caps; a laptop with >5000 unsynced
|
|
101
|
-
prompts needs repeated pushes.
|
|
102
|
-
|
|
103
|
-
## 10. Related
|
|
104
|
-
- SPEC-002 (dedup/insert primitives), SPEC-005/008 (search endpoints),
|
|
105
|
-
SPEC-015 (config). GWT tests target the deterministic parts (hash, dedup,
|
|
106
|
-
since-window) — no network needed.
|
|
5
|
+
- **Status**: Implemented; lossless protocol defined by SPEC-019
|
|
6
|
+
- **Date**: 2026-10-24
|
|
7
|
+
- **Source**: `src/server/`, `src/commands/remote.ts`, `src/sync/`
|
|
8
|
+
|
|
9
|
+
## Purpose and scope
|
|
10
|
+
|
|
11
|
+
Share local PH history across trusted machines while each local SQLite database
|
|
12
|
+
remains usable offline. This spec covers server access, authentication, stable
|
|
13
|
+
pagination, deduplication, cursor persistence, and the status command.
|
|
14
|
+
|
|
15
|
+
## Server contract
|
|
16
|
+
|
|
17
|
+
- The default listener is `127.0.0.1`; a non-loopback host must be explicit.
|
|
18
|
+
- The server refuses to start without a non-empty `remote-api-key`.
|
|
19
|
+
- `/health` is public and returns `{ "status": "ok" }`. All data routes require
|
|
20
|
+
`Authorization: Bearer <remote-api-key>` and compare credentials safely.
|
|
21
|
+
- Request bodies are limited to 32 MiB. Oversized and malformed requests return
|
|
22
|
+
413 and 400 respectively. Internal errors are returned without exception
|
|
23
|
+
details.
|
|
24
|
+
- `/api/sync/push` accepts up to 500 entries. It reports imported, duplicate,
|
|
25
|
+
and rejected records separately; the client advances only after complete
|
|
26
|
+
acknowledgement of the page.
|
|
27
|
+
- `/api/sync/pull` accepts `afterId` and `limit` (1–500), and returns entries in
|
|
28
|
+
ascending database row-ID order with `hasMore` and `nextCursor`.
|
|
29
|
+
|
|
30
|
+
## Client contract
|
|
31
|
+
|
|
32
|
+
- Push selects local rows after a cursor scoped to the normalized target and
|
|
33
|
+
persistent source database UUID. New imports receive higher IDs even when
|
|
34
|
+
their timestamps are old, so the next page sees them. Active wrapper captures
|
|
35
|
+
are not sent until completion; exited-owner markers are reclaimed. Byte-limited
|
|
36
|
+
HTTP subpages may split one 500-row outer page, and the outer cursor advances
|
|
37
|
+
only after every subpage is acknowledged.
|
|
38
|
+
- Pull reads remote row-ID pages, inserts or deduplicates every entry, then
|
|
39
|
+
persists the cursor. Replaying a page is safe through `sync_hash`.
|
|
40
|
+
- Switching the normalized server target or local database starts from row
|
|
41
|
+
zero. Legacy timestamp or database-unscoped cursors are ignored and trigger an
|
|
42
|
+
idempotent full scan. A server without cursor response fields produces an
|
|
43
|
+
actionable upgrade error.
|
|
44
|
+
- `PH_REMOTE_URL` overrides `remoteUrl`. Credentials are never included in the
|
|
45
|
+
stored target identity.
|
|
46
|
+
- `ph remote status` reports counts after the current target's push cursor and
|
|
47
|
+
the last successful push/pull times.
|
|
48
|
+
|
|
49
|
+
## Open reassessment finding
|
|
50
|
+
|
|
51
|
+
- **ISSUE-025**: cursor identity currently includes the normalized URL and local
|
|
52
|
+
database UUID, but not the remote database generation. Replacing the remote
|
|
53
|
+
SQLite file behind the same URL can make a high stored cursor skip the new
|
|
54
|
+
database's lower row IDs. Config holds only one cursor per direction, so
|
|
55
|
+
switching local databases and switching back triggers an idempotent full
|
|
56
|
+
replay. Last-success timestamps are global config fields rather than
|
|
57
|
+
target-scoped values. Add a remote identity handshake and persist state per
|
|
58
|
+
complete source/target identity before treating cursors as durable across
|
|
59
|
+
server restores.
|
|
60
|
+
|
|
61
|
+
## Acceptance criteria
|
|
62
|
+
|
|
63
|
+
- **G1**: Server startup requires an API key and defaults to loopback; health
|
|
64
|
+
discloses no database path; data endpoints reject missing/incorrect keys.
|
|
65
|
+
- **G2**: Equal timestamps, old-timestamp late imports, and multiple pages do
|
|
66
|
+
not skip rows.
|
|
67
|
+
- **G3**: A failed record prevents that page's cursor from advancing; retrying
|
|
68
|
+
the page does not duplicate acknowledged records.
|
|
69
|
+
- **G4**: Pull pages are ordered and cursor-based; legacy timestamp configuration
|
|
70
|
+
replays from the beginning; an old server fails clearly.
|
|
71
|
+
- **G5**: Malformed and oversized bodies return bounded client errors and do not
|
|
72
|
+
expose internal exceptions.
|
|
73
|
+
- **G6**: `PH_REMOTE_URL` takes precedence and target changes restart sync from
|
|
74
|
+
row zero.
|
|
75
|
+
- **G7**: Changing local database files restarts push and pull at row zero;
|
|
76
|
+
byte-limited subpages all remain within the server request cap; live records
|
|
77
|
+
are not bulk-pushed before response completion.
|
|
78
|
+
|
|
79
|
+
Focused server and protocol tests live under `src/server/__tests__/` and
|
|
80
|
+
`src/sync/`. SPEC-019 defines cross-cutting capture and ranking requirements.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
- **ID**: SPEC-011
|
|
4
4
|
- **Cluster**: MCP
|
|
5
|
-
- **Status**: Implemented
|
|
5
|
+
- **Status**: Implemented; adapter decomposition in SPEC-017
|
|
6
6
|
- **Date**: 2026-08-08
|
|
7
7
|
- **Source**: reverse-engineered from code (files listed in §8)
|
|
8
8
|
|
|
@@ -74,9 +74,12 @@ MCP config.
|
|
|
74
74
|
## 8. Key implementation map
|
|
75
75
|
| Concern | File(s) |
|
|
76
76
|
|---|---|
|
|
77
|
-
| Server + transport | src/mcp/server.ts
|
|
78
|
-
| Tool
|
|
79
|
-
|
|
|
77
|
+
| Server composition + transport | src/mcp/server.ts |
|
|
78
|
+
| Tool definitions | src/mcp/tools/definitions.ts |
|
|
79
|
+
| Prompt and project handlers | src/mcp/tools/prompts.ts, src/mcp/tools/projects.ts |
|
|
80
|
+
| Timeline handler | src/mcp/tools/timeline.ts |
|
|
81
|
+
| Response formatting | src/mcp/tools/format.ts |
|
|
82
|
+
| Memory/summary backends | src/db/memory-repository.ts |
|
|
80
83
|
|
|
81
84
|
## 9. Open questions / discrepancies
|
|
82
85
|
- Protocol smoke coverage uses the official SDK client with paired in-memory
|
|
@@ -1,98 +1,32 @@
|
|
|
1
|
-
# SPEC-012:
|
|
1
|
+
# SPEC-012: Interactive History Browser (Retired)
|
|
2
2
|
|
|
3
3
|
- **ID**: SPEC-012
|
|
4
|
-
- **Cluster**:
|
|
5
|
-
- **Status**:
|
|
6
|
-
- **
|
|
7
|
-
- **
|
|
8
|
-
|
|
9
|
-
##
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
3. `Tab` switches pane in wide mode; `1/2/3` switch prompt/response/memory tabs.
|
|
34
|
-
4. `y` copy, `s` star, `e` edit metadata, `x` delete (DB + refresh),
|
|
35
|
-
`r` rerun (onRerun → cli.ts spawns the tool), `C` chat mode (launch tool with
|
|
36
|
-
project context), `o` settings.
|
|
37
|
-
5. ListEntry renders role-colored bar, analysis indicator ●/○, star, Q/R badges,
|
|
38
|
-
summary line (ListEntry.tsx); Header shows counts + archive count
|
|
39
|
-
(Header.tsx:archiveCount); session separators `╌╌ date ╌╌`.
|
|
40
|
-
|
|
41
|
-
### 4.2 Filter panel
|
|
42
|
-
Flat scrollable list of all filter options with `[N]` counts; Enter toggles a
|
|
43
|
-
filter; letter jump to category. Pure filtering and option/count generation
|
|
44
|
-
live in `src/ui/filtering.ts`; `FilterPanel.tsx` owns keyboard interaction and
|
|
45
|
-
rendering.
|
|
46
|
-
|
|
47
|
-
## 5. Invariants & Business rules
|
|
48
|
-
- All mutations go through PhDB and refresh via `refreshKey`.
|
|
49
|
-
- The TUI is launched inside alt-screen (`\x1b[?1049h` … `l`) by cli.ts
|
|
50
|
-
(SPEC-001) — cleanup in `finally`.
|
|
51
|
-
- Rerun is synchronous spawnSync after the TUI exits (cli.ts:176-181).
|
|
52
|
-
|
|
53
|
-
## 6. UI / UX surface
|
|
54
|
-
Keybindings (AGENTS.md table): ↑↓/PgUp/PgDn, Tab, 1/2/3, y/s/e/r/x, /, o, f, c,
|
|
55
|
-
q/ESC. Themes in src/ui/themes.ts.
|
|
56
|
-
|
|
57
|
-
## 7. Acceptance criteria (Given/When/Then)
|
|
58
|
-
- **G1**: Given a DB with entries, When BrowseApp mounts, Then the list shows the
|
|
59
|
-
entries and Header shows the count (BrowseApp.tsx:791, Header.tsx).
|
|
60
|
-
- **G2**: Given `/` typed and a query, When the search applies, Then the list is
|
|
61
|
-
filtered via db.search (BrowseApp.tsx SearchBar wiring).
|
|
62
|
-
- **G3**: Given `x` on a selected entry, When pressed, Then the entry is deleted
|
|
63
|
-
via db.delete and the list refreshes (BrowseApp.tsx delete handler).
|
|
64
|
-
- **G4**: Given `s` on an entry, When pressed, Then `starred` toggles in
|
|
65
|
-
metadata (BrowseApp.tsx star handler).
|
|
66
|
-
- **G5**: Given an entry with a project, When `C` is pressed, Then chat mode
|
|
67
|
-
launches with project context injected (BrowseApp chat handler, SPEC-014).
|
|
68
|
-
- **G6**: Given entries and active filters, When `applyFilters` runs, Then only
|
|
69
|
-
matching metadata and text entries remain (`src/ui/filtering.ts`).
|
|
70
|
-
- **G7**: Given entries with filter metadata, When filter options are built,
|
|
71
|
-
Then category counts and active state reflect the full entry set
|
|
72
|
-
(`src/ui/filtering.ts`).
|
|
73
|
-
|
|
74
|
-
## 8. Key implementation map
|
|
75
|
-
| Concern | File(s) |
|
|
76
|
-
|---|---|
|
|
77
|
-
| Main app + settings | src/ui/BrowseApp.tsx |
|
|
78
|
-
| Filter interaction | src/ui/FilterPanel.tsx |
|
|
79
|
-
| Filter policy and option counts | src/ui/filtering.ts |
|
|
80
|
-
| Entry list / detail | src/ui/ListEntry.tsx, PreviewPane.tsx |
|
|
81
|
-
| Header / footer / search | src/ui/Header.tsx, Footer.tsx, SearchBar.tsx |
|
|
82
|
-
| Themes | src/ui/themes.ts |
|
|
83
|
-
| Alt-screen bootstrap (×3) | src/cli.ts:159-186, 214-248, 373-396 |
|
|
84
|
-
|
|
85
|
-
## 9. Open questions / discrepancies
|
|
86
|
-
- BrowseApp remains a large module; filter policy now has a tested seam, while
|
|
87
|
-
detail/edit/rerun/settings interactions remain coupled and need direct UI
|
|
88
|
-
coverage.
|
|
89
|
-
- `refreshKey` increments trigger full re-search — no pagination past the 1000
|
|
90
|
-
seed; huge histories truncate silently.
|
|
91
|
-
- React lint issues in BrowseApp/PreviewPane were reported pre-upgrade
|
|
92
|
-
(AGENTS.md "Known Issues") — status after ink 7 upgrade unverified (lint runs
|
|
93
|
-
clean now, but the rule set may have changed).
|
|
94
|
-
|
|
95
|
-
## 10. Related
|
|
96
|
-
- SPEC-001 (bootstrap), SPEC-005 (search backend), SPEC-002 (mutations),
|
|
97
|
-
SPEC-014 (chat/context). Filter policy/count tests are in
|
|
98
|
-
`src/ui/__tests__/filtering.test.ts`; full Ink interaction tests remain open.
|
|
4
|
+
- **Cluster**: Retired UI
|
|
5
|
+
- **Status**: Retired on 2026-10-22
|
|
6
|
+
- **Implementation**: Removed from `src/ui/`; Ink/React runtime dependencies removed
|
|
7
|
+
- **Replacement**: `ph search`, `ph last`, and `ph context` print terminal text
|
|
8
|
+
|
|
9
|
+
## Decision
|
|
10
|
+
|
|
11
|
+
PH no longer ships the interactive history browser. The former browser combined
|
|
12
|
+
history filtering, details, metadata editing, starring, deletion, reruns,
|
|
13
|
+
provider selection, settings, and context launch in a keyboard-driven Ink/React
|
|
14
|
+
application. This breadth made PH feel like a second terminal application and
|
|
15
|
+
kept the user from the smaller jobs the product needs to do well.
|
|
16
|
+
|
|
17
|
+
The default command now prints a short guide. `ph search` is the primary way to
|
|
18
|
+
find past interactions; `ph last` and the legacy `ph browse` command print recent
|
|
19
|
+
interactions. `ph search -i` is accepted for compatibility and prints normal
|
|
20
|
+
search results. No search or memory data is changed by this UI retirement.
|
|
21
|
+
|
|
22
|
+
## Historical Context
|
|
23
|
+
|
|
24
|
+
The retired implementation supported list/detail panes, filters, settings,
|
|
25
|
+
star/edit/delete, rerun, context-aware chat, keyboard navigation, and an
|
|
26
|
+
alternate-screen terminal mode. Those interactions were removed with the UI
|
|
27
|
+
components and their PTY/filter tests. Equivalent history and knowledge remain
|
|
28
|
+
available through the CLI, MCP, and existing storage APIs where applicable.
|
|
29
|
+
|
|
30
|
+
The old keybindings, source locations, and interaction criteria are deliberately
|
|
31
|
+
not maintained as current requirements. Use `SPEC-001` for the current CLI
|
|
32
|
+
contract.
|