Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,11 @@ func registerRoutes(r *mux.Router) {
r.HandleFunc("/api/brief-sessions/{id}/answers", handlers.AnswerBriefSessionHandler).Methods("POST")
r.HandleFunc("/api/brief-sessions/{id}/answers/{answer_id}/edit", handlers.EditBriefAnswerHandler).Methods("POST")
r.HandleFunc("/api/sessions/{id}/answers/{answer_id}/edit", handlers.EditBriefAnswerHandler).Methods("POST")
r.HandleFunc("/api/projects", handlers.ListProjectsHandler).Methods("GET")
r.HandleFunc("/api/projects/{id}", handlers.GetProjectHandler).Methods("GET")
r.HandleFunc("/api/articles/{id}", handlers.GetArticleHandler).Methods("GET")
r.HandleFunc("/api/drafts", handlers.GenerateDraftHandler).Methods("POST")
r.HandleFunc("/api/drafts/{id}", handlers.GetDraftHandler).Methods("GET")
r.HandleFunc("/api/drafts/{id}/regenerate-section", handlers.RegenerateDraftSectionHandler).Methods("POST")

// ルートパスへのアクセスはindex.htmlにリダイレクト
Expand Down
87 changes: 87 additions & 0 deletions cmd/server/main_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package main

import (
"io"
"net/http"
"net/http/httptest"
"os"
"strings"
"testing"

"github.com/gorilla/mux"
Expand All @@ -18,9 +22,15 @@ func TestRegisterRoutesIncludesWorkflowReadAPIs(t *testing.T) {
{method: http.MethodGet, path: "/api/history"},
{method: http.MethodGet, path: "/api/workflow/artifacts"},
{method: http.MethodGet, path: "/api/author-style"},
{method: http.MethodGet, path: "/api/author-style/style-1"},
{method: http.MethodGet, path: "/api/brief-sessions"},
{method: http.MethodGet, path: "/api/brief-sessions/templates"},
{method: http.MethodGet, path: "/api/brief-sessions/session-1"},
{method: http.MethodGet, path: "/api/briefs"},
{method: http.MethodGet, path: "/api/briefs/session-1"},
{method: http.MethodGet, path: "/api/models"},
{method: http.MethodGet, path: "/api/personas"},
{method: http.MethodGet, path: "/api/formats"},
} {
request, err := http.NewRequest(tt.method, tt.path, nil)
if err != nil {
Expand All @@ -32,3 +42,80 @@ func TestRegisterRoutesIncludesWorkflowReadAPIs(t *testing.T) {
}
}
}

func TestRegisterRoutesServesBrowserEntryAndStaticScript(t *testing.T) {
chdir(t, "../..")

router := mux.NewRouter()
registerRoutes(router)
server := httptest.NewServer(router)
defer server.Close()

for _, tt := range []struct {
path string
contentType string
body []string
}{
{
path: "/",
contentType: "text/html",
body: []string{
`<div id="model-status"`,
`<select id="style-model"`,
`<button id="open-history-btn"`,
`<div id="style-guide-card"`,
`<script src="/static/js/script.js"></script>`,
},
},
{
path: "/static/js/script.js",
contentType: "javascript",
body: []string{
"const configStorageKey = 'note-maker-config-v1'",
"const historyEndpoint = '/api/workflow/artifacts'",
"function saveModelConfig()",
"function openSelectedHistory()",
"function renderBriefCard(brief)",
},
},
} {
t.Run(tt.path, func(t *testing.T) {
response, err := http.Get(server.URL + tt.path)
if err != nil {
t.Fatalf("GET %s: %v", tt.path, err)
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
t.Fatalf("GET %s status = %d, want %d", tt.path, response.StatusCode, http.StatusOK)
}
if got := response.Header.Get("Content-Type"); got == "" || !strings.Contains(got, tt.contentType) {
t.Fatalf("GET %s Content-Type = %q, want contains %q", tt.path, got, tt.contentType)
}
body, err := io.ReadAll(response.Body)
if err != nil {
t.Fatalf("read response body: %v", err)
}
for _, want := range tt.body {
if !strings.Contains(string(body), want) {
t.Fatalf("GET %s body missing %q", tt.path, want)
}
}
})
}
}

func chdir(t *testing.T, dir string) {
t.Helper()
previous, err := os.Getwd()
if err != nil {
t.Fatalf("get cwd: %v", err)
}
if err := os.Chdir(dir); err != nil {
t.Fatalf("chdir %s: %v", dir, err)
}
t.Cleanup(func() {
if err := os.Chdir(previous); err != nil {
t.Fatalf("restore cwd: %v", err)
}
})
}
8 changes: 5 additions & 3 deletions docs/adrs/0002-multi-persona-multi-format-extension.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ Current implementation status as of 2026-05-03:
- Phase B5 is implemented: fixed interview questions are composed server-side by `persona_id × output_format_id`, Cloudia technical modes include extra viewpoint/context prompts, the frontend reads `GET /api/brief-sessions/templates`, and `cmd/scenario/media_matrix` produces a six-case cross-media evaluation matrix for note, Cor blog, Zenn, Qiita, and homepage output ([#25](https://github.com/terisuke/note_maker/issues/25)).
- Phase C1 is implemented and merged: `internal/infrastructure/repository/sqlite` adds migrations and storage for author styles, sessions, briefs, projects, articles, source snapshots, draft versions, final verification, and section-regeneration versions. The JSON store remains the compatibility path, while storage mode can now be inspected and switched from the web settings UI unless environment variables lock it ([#26](https://github.com/terisuke/note_maker/issues/26), [#61](https://github.com/terisuke/note_maker/issues/61)).
- Phase C2/C3 has an implemented first product cut for workflow history and readable artifacts ([#27](https://github.com/terisuke/note_maker/issues/27), [#28](https://github.com/terisuke/note_maker/issues/28)): the web app now exposes reusable history through `GET /api/history` and `GET /api/workflow/artifacts`, plus focused read endpoints `GET /api/author-style`, `GET /api/brief-sessions`, `GET /api/briefs`, and `GET /api/briefs/{id}`. The memory and SQLite stores both expose `ListAuthorStyles`, `ListSessions`, and `ListBriefs`; SQLite also gained `ListProjects` and `ListArticlesByProject` for the richer #26 schema. The UI adds `履歴から再開`, saved style-guide/session pickers, human-readable style-guide cards, and human-readable article-brief cards while keeping raw Markdown/JSON details available. Validation is recorded in [Issue 27/28 history and artifact UI/API validation](../validation/issue-27-28-history-artifacts-2026-05-03.md).
- The #13 follow-up has browser-adjacent contract coverage for the static HTML, script selectors, persistence hooks, workflow read routes, and artifact-card structure. It does not close #13: Playwright or equivalent real browser coverage is still required for persona/format switching, history open, readable cards, edit/fork, streaming/cancel, regenerate-section, and localStorage migration. Validation is recorded in [Issue #13 Browser Contract Coverage](../validation/issue-13-browser-contract-coverage-2026-05-03.md).
- A Phase C project/article/draft history follow-up now builds on the #26 SQLite schema: `GET /api/workflow/artifacts` includes project/article/draft summaries when SQLite is active, focused read routes expose project/article/draft details, and the history UI renders project, article, brief, current draft, draft versions, and source snapshot cards. The handler/server/static tests are green, but until the browser flow is covered by #13, the canonical completed Phase C closure claim remains separate from full browser E2E.
- Phase D1 is implemented and merged: handler tests now cover template selection, edit/fork errors, SSE follow-up and draft paths, completed-session draft fallback, regenerate-section context recovery, Analyze/Generate compatibility handlers, and SQLite driver selection. `go test ./internal/handlers -cover` reports 80%+ statement coverage ([#29](https://github.com/terisuke/note_maker/issues/29)).
- Runtime runner support is implemented and merged: `cmd/scenario/live_media_matrix` reads the offline matrix, emits planned aggregate JSON/Markdown by default, and executes live Evo X2 draft runs only when `RUN_LIVE_MEDIA_MATRIX=1` or `make scenario-media-matrix-live` is used ([#57](https://github.com/terisuke/note_maker/issues/57)).
- The 2026-05-03 browser 500 analysis showed an implementation drift: plain web-app startup still defaulted to workstation-local `127.0.0.1:8081`, while this ADR requires Evo X2 Tailnet as primary. Issue [#63](https://github.com/terisuke/note_maker/issues/63) restores the default order to Evo X2 Ollama over Tailnet → Evo X2 llama.cpp → workstation-local llama.cpp and makes the UI show the actual endpoint/model reported by SSE.
Expand All @@ -235,8 +237,8 @@ Current implementation status as of 2026-05-03:
Near-term execution order:

1. Close [#74](https://github.com/terisuke/note_maker/issues/74) and [#40](https://github.com/terisuke/note_maker/issues/40) for the current note/Qiita/Zenn/Cor blog publishing-target scope after linking the final `5/5` aggregate artifacts. Homepage remains a separate short-format check.
2. Land the #27/#28 history/artifact read cut and add Browser E2E coverage ([#13](https://github.com/terisuke/note_maker/issues/13)) for history opening, readable cards, and the existing edit/fork/stream/regenerate flows.
3. Follow with the remaining Phase C product gaps that were intentionally not included in the #27/#28 cut: add-persona authoring UI, broader edit persistence semantics beyond existing fork-on-edit/session saving, and richer project/article/draft history surfaces from the #26 SQLite schema.
2. Keep [#13](https://github.com/terisuke/note_maker/issues/13) open after the browser-contract pass and add real browser E2E coverage for history opening, readable cards, persona/format switching, question config, edit/fork, streaming/cancel, regenerate-section, and localStorage migration.
3. Follow with the remaining Phase C product gaps that were intentionally not included in the #27/#28 first cut: add-persona authoring UI, broader edit persistence semantics beyond existing fork-on-edit/session saving, richer project/article/draft history polish, and browser E2E over the project history cards.
4. Keep fallback-quality and runtime packaging follow-up ([#36](https://github.com/terisuke/note_maker/issues/36), [#45](https://github.com/terisuke/note_maker/issues/45), [#15](https://github.com/terisuke/note_maker/issues/15)) outside the #40 closure gate.

## Tracked issues
Expand All @@ -254,7 +256,7 @@ Filed 2026-05-02 as part of the PR that introduced this ADR.
- B5 — [#25](https://github.com/terisuke/note_maker/issues/25) Format- and persona-aware fixed question sets
- C1 — [#26](https://github.com/terisuke/note_maker/issues/26) Replace JSON store with SQLite-backed schema (extends [#14](https://github.com/terisuke/note_maker/issues/14)) — implemented in the current cut as an opt-in SQLite workflow store.
- C2 — [#27](https://github.com/terisuke/note_maker/issues/27) Persona / past-session picker UI — implemented in the current cut for saved style-guide and brief-session reuse through `履歴から再開`, backed by `GET /api/workflow/artifacts`, `GET /api/author-style`, and `GET /api/brief-sessions`. Add-persona authoring UI and broader edit-persistence expectations remain follow-up work.
- C3 — [#28](https://github.com/terisuke/note_maker/issues/28) Render brief and style guide as human-readable cards — implemented in the current cut for style-guide cards and article-brief cards, with raw Markdown/JSON details preserved behind disclosure controls. Rich project/article/draft artifact browsing remains a later Phase C layer on top of the #26 SQLite schema.
- C3 — [#28](https://github.com/terisuke/note_maker/issues/28) Render brief and style guide as human-readable cards — implemented in the current cut for style-guide cards and article-brief cards, with raw Markdown/JSON details preserved behind disclosure controls. Project/article/draft artifact browsing now has read APIs and history cards, but real browser E2E and remaining edit/add-persona semantics are still outside the #27/#28 first-cut closure claim.
- D1 — [#29](https://github.com/terisuke/note_maker/issues/29) HTTP handler tests for `internal/handlers/workflow.go` — implemented in the current cut with 80.0% handler package coverage.
- Runtime runner — [#57](https://github.com/terisuke/note_maker/issues/57) Add live LLM media-matrix runner and aggregate evaluator, feeding [#40](https://github.com/terisuke/note_maker/issues/40) — implemented in the current cut.
- Runtime stabilization epic — [#40](https://github.com/terisuke/note_maker/issues/40) Stabilize Tailnet Evo X2 draft quality and runtime metrics. #70-#73 provide the prerequisite validation and diagnostics. [#74](https://github.com/terisuke/note_maker/issues/74) has passed the bounded Cloudia/Zenn and Cloudia/Qiita proofs plus the final `5/5` publishing-target matrix.
Expand Down
41 changes: 36 additions & 5 deletions docs/implementation-plans/next-implementation-cut.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,16 @@ Implemented in the current #27/#28 history/artifact cut:
- Focused tests — `internal/handlers/workflow_history_test.go` covers saved artifact responses and brief detail errors; `static/history_ui_test.go` locks the frontend contract.
- Validation — [Issue 27/28 history and artifact UI/API validation](../validation/issue-27-28-history-artifacts-2026-05-03.md).

Implemented in the #13 follow-up contract cut:

- Static browser contract coverage checks the HTML entrypoint, production script, model selectors, question controls, history controls, artifact-card containers, persistence hooks, and route-table wiring.
- This is useful pre-Playwright coverage, but it is not equivalent to browser E2E. It does not exercise real DOM events, fetch stubbing, localStorage reload behavior, SSE/cancel, or the section-regeneration workflow in a browser.
- Validation — [Issue #13 Browser Contract Coverage](../validation/issue-13-browser-contract-coverage-2026-05-03.md).

Open and active:

- Memory/history umbrella: [#14](https://github.com/terisuke/note_maker/issues/14), now backed by the #26 schema work.
- Browser E2E coverage: [#13](https://github.com/terisuke/note_maker/issues/13).
- Browser E2E coverage: [#13](https://github.com/terisuke/note_maker/issues/13), still open after the contract-test cut.
- Runtime evaluation: [#40](https://github.com/terisuke/note_maker/issues/40), now satisfied for the current note/Qiita/Zenn/Cor blog publishing-target acceptance scope by the 2026-05-03 full Tailnet Evo X2 matrix.
- Runtime evaluation sub-issue [#74](https://github.com/terisuke/note_maker/issues/74), satisfied by the staged reruns and the final `5/5` full matrix pass.
- Fallback and packaging follow-up: [#36](https://github.com/terisuke/note_maker/issues/36), [#45](https://github.com/terisuke/note_maker/issues/45), [#15](https://github.com/terisuke/note_maker/issues/15).
Expand All @@ -50,7 +56,32 @@ Remaining Phase C gaps after the current #27/#28 cut:

- Add-persona authoring UI is not implemented; the current UI consumes seeded personas and saved artifacts.
- Broader edit persistence called out in the issue text is not implemented beyond the existing fork-on-edit/session/brief save paths.
- Project/article/draft artifact browsing from SQLite's normalized #26 schema is not exposed in the web UI yet; this cut intentionally uses style guides, sessions, and completed briefs as the reusable history surface.
- Project/article/draft artifact browsing from SQLite's normalized #26 schema now has server routes, response-shape contract coverage, frontend selectors, and readable history cards. The remaining gap is real browser E2E over those cards and broader edit/add-persona semantics, so #13/#14/#27/#28 should stay open unless the owner explicitly accepts a narrower first-cut closure.

## Current Review Findings

Review date: 2026-05-03 on `codex/phase-c-history-e2e`.

Validation that passed:

```sh
go test ./...
go test ./internal/handlers ./static
go test ./cmd/server ./internal/handlers ./static
node --check static/js/script.js
git diff --check
```

No blocking code-risk finding remains in the targeted suite after the parallel fixes. The remaining closure risk is coverage scope: #13 still lacks real browser E2E, and #27/#28 still contain product scope that is broader than the first cut.

## Issue Close/Open Proposal

| Issue | Proposal | Rationale |
|---|---|---|
| [#13](https://github.com/terisuke/note_maker/issues/13) | Keep open | Static and route contract coverage was added, but the issue asks for browser E2E. Real browser coverage still needs model config, question customisation, persona/format switching, history open, readable cards, streaming/cancel, edit/fork, regenerate-section, and localStorage migration. |
| [#14](https://github.com/terisuke/note_maker/issues/14) | Keep open | #26 gives the SQLite schema and restart-capable storage foundation, but the product still lacks full queryable project/article/draft browsing and versioned edit/history surfaces. |
| [#27](https://github.com/terisuke/note_maker/issues/27) | Keep open, or close only if the issue owner accepts the first-cut scope | Saved style-guide/session reuse is implemented, but the original issue still includes add-persona authoring UI, project/article navigation, restart semantics, and broader edit persistence. |
| [#28](https://github.com/terisuke/note_maker/issues/28) | Keep open, or close only if the issue owner accepts the first-cut scope | Readable style-guide and brief cards landed, but editable card persistence/versioning and richer project/article/draft artifacts are still outstanding. |

## Final evaluation target

Expand Down Expand Up @@ -124,14 +155,14 @@ Use subagents with disjoint write scopes when implementation resumes:
|---|---|---|---|---|
| A | [#74](https://github.com/terisuke/note_maker/issues/74) | Full matrix worker | live aggregate and validation docs | Complete for current scope: note, Qiita, Zenn, and Cor blog rows all pass and record artifacts |
| D | [#27](https://github.com/terisuke/note_maker/issues/27) / [#28](https://github.com/terisuke/note_maker/issues/28) | History/artifact UI worker | done for this cut | style-guide/session history picker and readable brief/style cards use persisted workflow state |
| E | [#13](https://github.com/terisuke/note_maker/issues/13) | Browser E2E worker | browser tests and fixtures | persona/format switching, history open, readable cards, edit/fork, streaming, regenerate-section, and legacy localStorage migration are covered |
| E | [#13](https://github.com/terisuke/note_maker/issues/13) | Browser E2E worker | browser tests and fixtures | contract tests are done; close only after persona/format switching, history open, readable cards, edit/fork, streaming/cancel, regenerate-section, and legacy localStorage migration are covered in a browser |
| F | Phase C follow-up | Product worker | future history UI/API files | add-persona UI, broader edit persistence, and project/article/draft browsing are split from the #27/#28 first cut |

Lane A is the next expensive Evo X2 spend. Lane D/E can continue in parallel when they do not need the same frontend files.

## Recommended order

1. Land the current #27/#28 history/artifact cut with its validation doc, then wire #13 Browser E2E around the new history picker and cards while preserving the existing edit/fork, streaming, and regenerate-section coverage goals.
2. Split the remaining Phase C work into explicit follow-up issues before implementation: add-persona authoring UI, broader edit persistence semantics, and project/article/draft artifact browsing from the #26 SQLite schema.
1. Wire #13 Browser E2E around the new history picker and cards while preserving the existing edit/fork, streaming/cancel, and regenerate-section coverage goals.
2. Split the remaining Phase C product work into explicit follow-up issues before broadening implementation: add-persona authoring UI, broader edit persistence semantics, project/article/draft artifact browsing polish from the #26 SQLite schema, and real browser E2E over these history paths.
3. Close #74 and #40 for the current publishing-target acceptance scope after the PR lands and the issue comments link the final aggregate artifacts.
4. Keep #36/#45 as fallback/runtime P2 work and #15 as packaging after persistence/history are usable. Homepage remains a separate short-format check, not part of the #40 closure gate.
Loading