Просмотр исходного кода

feat(suggest-path): revise Phase 2 spec with AI candidate evaluation

Update requirements, design, and tasks for Phase 2 revision:
- Add flow/stock information type classification (Req 10)
- Add AI-based candidate evaluation and ranking (Req 11)
- Add three-pattern path proposals (Req 12)
- Add client LLM independence principle (Req 13)
- Regenerate tasks based on revised design

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
VANELLOPE\tomoyuki-t 1 месяц назад
Родитель
Сommit
50b7a68d73

+ 346 - 188
.kiro/specs/suggest-path/design.md

@@ -11,16 +11,29 @@
 ### Goals
 
 - Provide a single POST endpoint that returns path suggestions with metadata (type, path, label, description, grant)
-- Phase 1 (MVP): Return personal memo path with fixed metadata — zero external dependencies
-- Phase 2: Add search-based and category-based suggestions using GROWI AI keyword extraction and search service
+- Phase 1 (MVP): Return personal memo path with fixed metadata — zero external dependencies. **Implemented.**
+- Phase 2: Add AI-powered search-based suggestions with flow/stock information classification, multi-candidate evaluation, and intelligent path proposals including new paths
 - Enable independent access control via separate namespace from `/page`
 
+### Design Principles
+
+- **Client LLM independence**: Heavy reasoning (content analysis, candidate evaluation, path proposal, description generation) is centralized in GROWI AI on the server side. The API response includes structured data fields (`informationType`, `type`, `grant`) alongside natural language (`description`) so that even less capable LLM clients can make correct decisions through simple field access, without requiring advanced reasoning to interpret the response.
+
 ### Non-Goals
 
 - Page creation or saving (existing `POST /_api/v3/page` handles this)
 - Page title suggestion (Claude handles this via user dialogue)
 - Client-side "enter manually" option (Agent Skill responsibility)
-- GROWI AI keyword extraction implementation details (separate design)
+
+### Phase 2 Revision Summary
+
+Phase 2 design was revised based on reviewer feedback. Key architectural changes from the prior revision:
+
+1. **AI calls: 1 → 2**: Content analysis (keyword extraction + flow/stock classification) followed by candidate evaluation. Elasticsearch sits between the two calls, making consolidation structurally impossible.
+2. **Candidate selection: mechanical → AI-evaluated**: Instead of top-1 by ES score, multiple candidates are passed to GROWI AI for content-destination fit evaluation.
+3. **Path proposals: 3 patterns**: Parent directory, subdirectory, sibling page (including new paths that don't yet exist).
+4. **Descriptions: mechanical → AI-generated**: Phase 2 descriptions are generated by the candidate evaluator as part of the evaluation.
+5. **Category type: under review**: The existing `category` implementation is retained, but may be merged into the AI evaluation approach after implementation and reviewer discussion.
 
 ## Architecture
 
@@ -33,6 +46,7 @@ The suggest-path endpoint integrates with GROWI's existing API infrastructure:
 - **Response format**: `res.apiv3(data)` for success, `res.apiv3Err(error, status)` for errors
 - **AI features**: Existing `features/openai/` module with `certifyAiService` middleware for AI-enabled gating
 - **Search**: `searchService.searchKeyword()` for full-text search with permission scoping
+- **Flow/stock classification**: Existing `instructionsForInformationTypes` in `features/openai/server/services/assistant/instructions/commons.ts`
 
 No existing architecture needs modification. The endpoint adds a new route namespace alongside existing ones.
 
@@ -44,15 +58,17 @@ graph TB
         MCP[MCP Server]
     end
 
-    subgraph GROWI API
+    subgraph GROWI_API[GROWI API]
         Router[ai-tools Router]
         Handler[suggest-path Handler]
-        MemoGen[Memo Suggestion Logic]
-        SearchGen[Search Suggestion Logic - Phase 2]
-        CategoryGen[Category Suggestion Logic - Phase 2]
+        MemoGen[Memo Suggestion]
+        Analyzer[Content Analyzer - 1st AI Call]
+        Retriever[Search Candidate Retriever]
+        Evaluator[Candidate Evaluator - 2nd AI Call]
+        CategoryGen[Category Suggestion - Under Review]
     end
 
-    subgraph Existing Services
+    subgraph Existing[Existing Services]
         SearchSvc[Search Service]
         GrantSvc[Page Grant Service]
         AIFeature[GROWI AI - OpenAI Feature]
@@ -66,23 +82,26 @@ graph TB
     MCP -->|POST suggest-path| Router
     Router --> Handler
     Handler --> MemoGen
-    Handler --> SearchGen
+    Handler --> Analyzer
+    Analyzer --> AIFeature
+    Handler --> Retriever
+    Retriever --> SearchSvc
+    Handler --> Evaluator
+    Evaluator --> AIFeature
     Handler --> CategoryGen
-    SearchGen --> AIFeature
-    SearchGen --> SearchSvc
     CategoryGen --> SearchSvc
-    SearchGen --> GrantSvc
-    CategoryGen --> GrantSvc
     SearchSvc --> ES
+    Evaluator --> GrantSvc
+    CategoryGen --> GrantSvc
     GrantSvc --> Mongo
 ```
 
 **Architecture Integration**:
 
-- **Selected pattern**: Layered handler following existing GROWI route conventions. Phase 1 uses inline logic in handler; Phase 2 adds generator functions called by the handler (see [Implementation Paradigm](#implementation-paradigm) for function vs class rationale)
+- **Selected pattern**: Layered handler following existing GROWI route conventions. Phase 1 uses inline logic in handler; Phase 2 adds function components called by the handler (see [Implementation Paradigm](#implementation-paradigm))
 - **Domain boundaries**: Route layer (`ai-tools/`) owns the endpoint. Suggestion logic delegates to existing services (search, grant, AI) without modifying them
 - **Existing patterns preserved**: Handler factory pattern, middleware chain, `res.apiv3()` response format
-- **New components**: `ai-tools/` route directory (new namespace), `suggest-path.ts` handler
+- **New components**: ContentAnalyzer and CandidateEvaluator wrap GROWI AI calls with suggest-path-specific prompting
 - **Steering compliance**: Feature-based separation, named exports, TypeScript strict typing
 
 ### Implementation Paradigm
@@ -91,22 +110,22 @@ graph TB
 
 **Class adoption criteria** — a class is permitted only when at least one of the following applies AND a function-based alternative would be clearly inferior:
 
-1. **Shared dependency management**: Multiple exported functions within a module depend on the same external services (e.g., SearchService), making argument passing across all functions verbose. A class with dependency fields reduces repetition.
-2. **Singleton state/cache management**: The module must maintain mutable state or cached data in a singleton instance, where immutability is not feasible.
+1. **Shared dependency management**: Multiple exported functions within a module depend on the same external services, making argument passing verbose.
+2. **Singleton state/cache management**: The module must maintain mutable state or cached data.
 
 **Component assessment**:
 
-| Component                             | Paradigm | Rationale                                                                                                                                                                                                        |
-| ------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| MemoSuggestionGenerator               | Function | No external service dependencies beyond `user`. Single function.                                                                                                                                                 |
-| DescriptionGenerator                  | Function | Stateless, no dependencies. Pure transformation functions.                                                                                                                                                       |
-| GrantResolver                         | Function | Single function. Page Model accessed via argument.                                                                                                                                                               |
-| KeywordExtractor (Phase 2)            | Function | Single function delegating to OpenAI Feature.                                                                                                                                                                    |
-| SearchSuggestionGenerator (Phase 2)   | Function | Single function. SearchService and GrantResolver passed as arguments.                                                                                                                                            |
-| CategorySuggestionGenerator (Phase 2) | Function | Single function. Same dependency pattern as SearchSuggestionGenerator.                                                                                                                                           |
-| SuggestPathService (Phase 2)          | Function | Single public function. No state or cache. Dependencies as arguments. May adopt class if public functions grow and shared dependency passing becomes verbose.                                                    |
+| Component | Paradigm | Rationale |
+| --- | --- | --- |
+| MemoSuggestionGenerator | Function | No external service dependencies beyond `user`. Single function. |
+| ContentAnalyzer (Phase 2) | Function | Single function delegating to OpenAI Feature. |
+| SearchCandidateRetriever (Phase 2) | Function | Single function. SearchService passed as argument. |
+| CandidateEvaluator (Phase 2) | Function | Single function delegating to OpenAI Feature. GrantResolver passed as argument. |
+| CategorySuggestionGenerator (Phase 2) | Function | Single function. SearchService and GrantResolver passed as arguments. Under review. |
+| GrantResolver | Function | Single function. Page Model accessed via argument. |
+| SuggestPathOrchestrator (Phase 2) | Function | Single public function. Dependencies as arguments. |
 
-No component currently meets the class adoption criteria. All are implemented as exported functions.
+No component currently meets the class adoption criteria.
 
 ### Technology Stack
 
@@ -114,8 +133,8 @@ No component currently meets the class adoption criteria. All are implemented as
 |-------|------------------|-----------------|-------|
 | Backend | Express.js (existing) | Route handling, middleware | No new dependencies |
 | Validation | express-validator (existing) | Request body validation | Existing pattern |
-| Search | Elasticsearch via searchService (existing) | Phase 2 keyword search | Used for `search` and `category` suggestions |
-| AI | OpenAI feature module (existing) | Phase 2 keyword extraction | Existing `features/openai/` infrastructure |
+| Search | Elasticsearch via searchService (existing) | Phase 2 candidate retrieval | Score threshold filtering added |
+| AI | OpenAI feature module (existing) | Phase 2: 1st call (content analysis), 2nd call (candidate evaluation) | Existing `features/openai/` infrastructure |
 | Data | MongoDB via Mongoose (existing) | Page grant lookup | For parent page grant resolution |
 
 No new dependencies introduced. All technology is already in the GROWI stack.
@@ -139,92 +158,122 @@ sequenceDiagram
     Handler-->>Client: 200 suggestions array - memo only
 ```
 
-### Phase 2 Flow
+### Phase 2 Flow (Revised)
 
 ```mermaid
 sequenceDiagram
     participant Client as MCP Client
-    participant Handler as suggest-path Handler
-    participant AI as GROWI AI
+    participant Handler as Orchestrator
+    participant AI1 as Content Analyzer
     participant Search as Search Service
-    participant Grant as Page Grant Service
+    participant AI2 as Candidate Evaluator
+    participant Grant as Grant Resolver
+    participant CatGen as Category Generator
 
     Client->>Handler: POST with body content
     Handler->>Handler: Generate memo suggestion
-    Handler->>AI: Extract keywords from body
-    AI-->>Handler: Keywords array
-
-    par Search-based suggestion
-        Handler->>Search: searchKeyword with keywords
-        Search-->>Handler: Related pages
-        Handler->>Grant: Resolve parent grant
-        Grant-->>Handler: Grant value
-    and Category-based suggestion
-        Handler->>Search: searchKeyword with prefix scope
-        Search-->>Handler: Top-level pages
-        Handler->>Grant: Resolve parent grant
-        Grant-->>Handler: Grant value
+
+    Handler->>AI1: Analyze content body
+    Note over AI1: 1st AI Call
+    AI1-->>Handler: keywords + informationType
+
+    par Search and evaluate
+        Handler->>Search: Search by keywords
+        Search-->>Handler: Raw results with scores
+        Handler->>Handler: Filter by score threshold
+        Handler->>AI2: body + analysis + candidates
+        Note over AI2: 2nd AI Call
+        AI2-->>Handler: Evaluated suggestions with paths and descriptions
+        loop For each evaluated suggestion
+            Handler->>Grant: Resolve grant for proposed path
+            Grant-->>Handler: Grant value
+        end
+    and Category suggestion
+        Handler->>CatGen: Generate from keywords
+        CatGen->>Search: Scoped keyword search
+        Search-->>CatGen: Top-level pages
+        CatGen->>Grant: Resolve parent grant
+        Grant-->>CatGen: Grant value
+        CatGen-->>Handler: Category suggestion or null
     end
 
-    Handler-->>Client: 200 suggestions array - memo + search + category
+    Handler-->>Client: 200 suggestions array
 ```
 
 **Key decisions**:
 
-- Search-based and category-based suggestions are generated in parallel where possible
-- If keyword extraction fails, handler falls back to memo-only response (Phase 1 behavior)
-- If search returns no results for a suggestion type, that type is omitted from the response
+- Content analysis (1st AI call) and candidate evaluation (2nd AI call) are structurally sequential — Elasticsearch sits between them
+- Search-evaluate flow and category generation run in parallel where possible
+- If content analysis fails, handler falls back to memo-only response (Phase 1 behavior)
+- If candidate evaluation fails, handler falls back to memo-only response
+- If search returns no results above the score threshold, search-based suggestions are omitted
+- Category generator runs independently as existing implementation (under review)
 
 ## Requirements Traceability
 
 | Requirement | Summary | Components | Interfaces | Flows |
 |-------------|---------|------------|------------|-------|
-| 1.1 | POST endpoint returns suggestions array | SuggestPathRouter, SuggestPathHandler | API Contract | Phase 1, Phase 2 |
-| 1.2 | Suggestion fields: type, path, label, description, grant | SuggestPathHandler | PathSuggestion type | — |
-| 1.3 | Path values as directory paths with trailing slash | SuggestPathHandler | PathSuggestion type | — |
+| 1.1 | POST endpoint returns suggestions array | SuggestPathRouter, Orchestrator | API Contract | Phase 1, Phase 2 |
+| 1.2 | Suggestion fields: type, path, label, description, grant | Orchestrator | PathSuggestion type | — |
+| 1.3 | Path values as directory paths with trailing slash | Orchestrator | PathSuggestion type | — |
 | 1.4 | Separate namespace from /page | SuggestPathRouter | Route registration | — |
 | 2.1 | Include memo type suggestion | MemoSuggestionGenerator | PathSuggestion type | Phase 1 |
-| 2.2 | Memo path under user home directory (user pages enabled) | MemoSuggestionGenerator | — | Phase 1 |
+| 2.2 | Memo path under user home directory | MemoSuggestionGenerator | — | Phase 1 |
 | 2.3 | Memo path under alternative namespace (user pages disabled) | MemoSuggestionGenerator | — | Phase 1 |
-| 2.4 | Memo grant = 4 when user pages enabled; resolve from parent when disabled | MemoSuggestionGenerator, GrantResolver | — | — |
-| 2.5 | Fixed description for memo | MemoSuggestionGenerator, DescriptionGenerator | — | — |
-| 3.1 | Search related pages by keywords | SearchSuggestionGenerator | SearchService | Phase 2 |
-| 3.2 | Return parent directory of most relevant page | SearchSuggestionGenerator | — | Phase 2 |
-| 3.3 | Include related page titles in description | SearchSuggestionGenerator, DescriptionGenerator | — | — |
-| 3.4 | Include parent page grant for search type | SearchSuggestionGenerator, GrantResolver | — | — |
-| 3.5 | Omit search type if no results | SearchSuggestionGenerator | — | — |
+| 2.4 | Memo grant = 4 when user pages enabled | MemoSuggestionGenerator, GrantResolver | — | — |
+| 2.5 | Fixed description for memo | MemoSuggestionGenerator | — | — |
+| 3.1 | Search related pages by keywords | SearchCandidateRetriever | SearchService | Phase 2 |
+| 3.2 | Filter candidates by ES score threshold | SearchCandidateRetriever | — | Phase 2 |
+| 3.3 | Pass candidates to AI evaluation | Orchestrator, CandidateEvaluator | — | Phase 2 |
+| 3.4 | Include parent page grant for search-based suggestions | GrantResolver | — | — |
+| 3.5 | Omit search-based suggestions if no results | SearchCandidateRetriever, Orchestrator | — | — |
 | 4.1 | Search top-level directories by keywords | CategorySuggestionGenerator | SearchService | Phase 2 |
 | 4.2 | Extract top-level path segment | CategorySuggestionGenerator | — | Phase 2 |
 | 4.3 | Include parent page grant for category type | CategorySuggestionGenerator, GrantResolver | — | — |
 | 4.4 | Omit category type if no results | CategorySuggestionGenerator | — | — |
-| 5.1 | Delegate keyword extraction to GROWI AI | KeywordExtractor | GROWI AI interface | Phase 2 |
-| 5.2 | Use extracted keywords for search, not raw body | SearchSuggestionGenerator, CategorySuggestionGenerator | — | Phase 2 |
-| 5.3 | Fallback to memo if extraction fails | SuggestPathHandler | — | Phase 2 |
-| 6.1 | Description provides selection rationale | DescriptionGenerator | — | — |
-| 6.2 | Fixed text for memo in Phase 1 | DescriptionGenerator | — | — |
-| 6.3 | List page titles for search type in Phase 2 | DescriptionGenerator | — | — |
-| 6.4 | Path segment name for category type in Phase 2 | DescriptionGenerator | — | — |
-| 6.5 | Phase 2 descriptions mechanical, no AI | DescriptionGenerator | — | — |
+| 5.1 | Delegate content analysis to GROWI AI (single call: keywords + flow/stock) | ContentAnalyzer | GROWI AI interface | Phase 2 |
+| 5.2 | Extract 3-5 keywords prioritizing proper nouns | ContentAnalyzer | — | Phase 2 |
+| 5.3 | Use extracted keywords for search, not raw body | SearchCandidateRetriever, CategorySuggestionGenerator | — | Phase 2 |
+| 5.4 | Classify content as flow or stock | ContentAnalyzer | ContentAnalysis type | Phase 2 |
+| 5.5 | Fallback to memo if analysis fails | Orchestrator | — | Phase 2 |
+| 6.1 | Description provides selection rationale | MemoSuggestionGenerator, CandidateEvaluator | — | — |
+| 6.2 | Fixed text for memo in Phase 1 | MemoSuggestionGenerator | — | — |
+| 6.3 | AI-generated description for search-based suggestions | CandidateEvaluator | — | Phase 2 |
 | 7.1 | Grant field = parent page grant value | GrantResolver | PageGrant type | — |
-| 7.2 | Grant = upper bound constraint, not recommendation | GrantResolver | — | — |
+| 7.2 | Grant = upper bound constraint | GrantResolver | — | — |
 | 8.1 | Require valid API token or login session | SuggestPathRouter | Middleware chain | — |
 | 8.2 | Return auth error if unauthenticated | SuggestPathRouter | — | — |
-| 8.3 | Use authenticated user for user-specific suggestions | SuggestPathHandler | — | — |
+| 8.3 | Use authenticated user for user-specific suggestions | Orchestrator | — | — |
 | 9.1 | Validation error if body missing/empty | SuggestPathRouter | Validator | — |
-| 9.2 | No internal details in error responses | SuggestPathHandler | ErrorV3 | — |
+| 9.2 | No internal details in error responses | Orchestrator | ErrorV3 | — |
+| 10.1 | Consider flow/stock alignment in candidate evaluation | CandidateEvaluator | ContentAnalysis type | Phase 2 |
+| 10.2 | Identify flow characteristics in candidate locations | CandidateEvaluator | — | Phase 2 |
+| 10.3 | Identify stock characteristics in candidate locations | CandidateEvaluator | — | Phase 2 |
+| 10.4 | Flow/stock as ranking factor, not hard filter | CandidateEvaluator | — | Phase 2 |
+| 11.1 | Evaluate candidates by passing body + path + snippet to AI | CandidateEvaluator | GROWI AI interface | Phase 2 |
+| 11.2 | Rank by content-destination fit and flow/stock alignment | CandidateEvaluator | — | Phase 2 |
+| 11.3 | Generate description per suggestion as part of evaluation | CandidateEvaluator | EvaluatedSuggestion type | Phase 2 |
+| 11.4 | Fallback to memo-only if evaluation fails | Orchestrator | — | Phase 2 |
+| 12.1 | Consider 3 structural patterns: parent, subdirectory, sibling | CandidateEvaluator | — | Phase 2 |
+| 12.2 | Generate new directory names for sibling pattern | CandidateEvaluator | — | Phase 2 |
+| 12.3 | Determine appropriate pattern based on content-destination fit | CandidateEvaluator | — | Phase 2 |
+| 12.4 | Sibling pattern paths at same hierarchy level as candidate | CandidateEvaluator | — | Phase 2 |
+| 13.1 | Include informationType field in search-based suggestions | CandidateEvaluator, Orchestrator | PathSuggestion type | Phase 2 |
+| 13.2 | Provide both structured metadata and natural language context | Orchestrator | PathSuggestion type | Phase 2 |
+| 13.3 | All reasoning-intensive operations server-side | ContentAnalyzer, CandidateEvaluator | — | Phase 2 |
 
 ## Components and Interfaces
 
 | Component | Domain/Layer | Intent | Req Coverage | Key Dependencies | Contracts |
 |-----------|-------------|--------|--------------|------------------|-----------|
-| SuggestPathRouter | Route | Route registration and middleware composition | 1.4, 8.1, 8.2, 9.1 | Express Router (P0) | API |
-| SuggestPathHandler | Route | Orchestrate suggestion generation and response | 1.1, 1.2, 1.3, 5.3, 8.3, 9.2 | SuggestionGenerators (P0) | API, Service |
-| MemoSuggestionGenerator | Service | Generate memo path suggestion from user identity | 2.1, 2.2, 2.3, 2.4 | req.user (P0) | Service |
-| SearchSuggestionGenerator | Service | Generate search-based suggestion from keywords (Phase 2) | 3.1-3.5, 5.2 | SearchService (P0), GrantResolver (P1) | Service |
-| CategorySuggestionGenerator | Service | Generate category-based suggestion from keywords (Phase 2) | 4.1-4.4, 5.2 | SearchService (P0), GrantResolver (P1) | Service |
-| KeywordExtractor | Service | Extract keywords from content via GROWI AI (Phase 2) | 5.1, 5.2 | OpenAI Feature (P0) | Service |
-| DescriptionGenerator | Service | Generate description text per suggestion type | 6.1-6.5 | None | Service |
-| GrantResolver | Service | Resolve parent page grant for a given path | 7.1, 7.2, 3.4, 4.3 | Page Model (P0) | Service |
+| SuggestPathRouter | Route | Route registration and middleware | 1.4, 8.1, 8.2, 9.1 | Express Router (P0) | API |
+| SuggestPathOrchestrator | Service | Orchestrate all suggestion generators | 1.1-1.3, 3.3, 3.5, 5.5, 8.3, 9.2, 11.4, 13.2 | All generators (P0) | Service |
+| MemoSuggestionGenerator | Service | Generate memo path suggestion | 2.1-2.5, 6.2 | req.user (P0) | Service |
+| ContentAnalyzer | Service | Extract keywords + classify flow/stock (1st AI call) | 5.1-5.4 | OpenAI Feature (P0) | Service |
+| SearchCandidateRetriever | Service | Search ES and filter by score threshold | 3.1, 3.2, 3.5 | SearchService (P0) | Service |
+| CandidateEvaluator | Service | AI-evaluate candidates, propose paths, generate descriptions (2nd AI call) | 3.3, 6.3, 10.1-10.4, 11.1-11.3, 12.1-12.4, 13.1 | OpenAI Feature (P0), GrantResolver (P1) | Service |
+| CategorySuggestionGenerator | Service | Generate category suggestion (under review) | 4.1-4.4 | SearchService (P0), GrantResolver (P1) | Service |
+| GrantResolver | Service | Resolve parent page grant for a path | 7.1, 7.2, 3.4, 4.3 | Page Model (P0) | Service |
 
 ### Route Layer
 
@@ -245,7 +294,7 @@ sequenceDiagram
 **Dependencies**
 
 - Inbound: MCP Client — HTTP POST requests (P0)
-- Outbound: SuggestPathHandler — request processing (P0)
+- Outbound: SuggestPathOrchestrator — request processing (P0)
 - External: Express Router, express-validator — routing and validation (P0)
 
 **Contracts**: API [x]
@@ -259,44 +308,60 @@ sequenceDiagram
 **Implementation Notes**
 
 - Route registered in `apps/app/src/server/routes/apiv3/index.js` as `router.use('/ai-tools', ...)`
-- Middleware chain follows existing pattern: `accessTokenParser` → `loginRequiredStrictly` → `certifyAiService` → validators → `apiV3FormValidator` → handler
+- Middleware chain: `accessTokenParser` → `loginRequiredStrictly` → `certifyAiService` → validators → `apiV3FormValidator` → handler
 - Namespace `ai-tools` is tentative pending yuki confirmation; change requires single line edit in `index.js`
 
-#### SuggestPathHandler
+### Service Layer
+
+#### SuggestPathOrchestrator
 
 | Field | Detail |
 |-------|--------|
-| Intent | Orchestrate suggestion generation, collect results, return unified response |
-| Requirements | 1.1, 1.2, 1.3, 5.3, 8.3, 9.2 |
+| Intent | Orchestrate all suggestion generators, handle failures with graceful degradation |
+| Requirements | 1.1, 1.2, 1.3, 3.3, 3.5, 5.5, 8.3, 9.2, 11.4 |
 
 **Responsibilities & Constraints**
 
-- Invoke suggestion generators (memo always; search and category in Phase 2)
+- Always generate memo suggestion first (guaranteed fallback)
+- Invoke ContentAnalyzer, then pass results to SearchCandidateRetriever and CandidateEvaluator
+- Run category generation in parallel with the search-evaluate pipeline
 - Collect non-null results into suggestions array
-- Handle errors gracefully: if Phase 2 logic fails, fall back to memo-only
-- Format response using `res.apiv3()`
+- On any Phase 2 failure, fall back to memo-only response
 
 **Dependencies**
 
 - Inbound: SuggestPathRouter — validated request (P0)
-- Outbound: MemoSuggestionGenerator, SearchSuggestionGenerator, CategorySuggestionGenerator, KeywordExtractor — suggestion generation (P0)
+- Outbound: All service layer components — suggestion generation (P0)
 
 **Contracts**: Service [x]
 
 ##### Service Interface
 
 ```typescript
-// Phase 1: Handler contains inline logic
-// Phase 2: Handler calls generateSuggestions with explicit dependencies
+interface SuggestPathDependencies {
+  analyzeContent: (body: string) => Promise<ContentAnalysis>;
+  retrieveSearchCandidates: (
+    keywords: string[],
+    user: IUserHasId,
+    userGroups: PopulatedGrantedGroup[],
+  ) => Promise<SearchCandidate[]>;
+  evaluateCandidates: (
+    body: string,
+    analysis: ContentAnalysis,
+    candidates: SearchCandidate[],
+  ) => Promise<EvaluatedSuggestion[]>;
+  generateCategorySuggestion: (
+    keywords: string[],
+    user: IUserHasId,
+    userGroups: PopulatedGrantedGroup[],
+  ) => Promise<PathSuggestion | null>;
+  resolveParentGrant: (path: string) => Promise<number>;
+}
 
 function generateSuggestions(
   user: IUserHasId,
   body: string,
-  deps: {
-    searchService: SearchService;
-    extractKeywords: (body: string) => Promise<string[]>;
-    resolveParentGrant: (path: string) => Promise<number>;
-  },
+  deps: SuggestPathDependencies,
 ): Promise<PathSuggestion[]>;
 ```
 
@@ -306,168 +371,210 @@ function generateSuggestions(
 
 **Implementation Notes**
 
-- Phase 1: Logic is inline in handler (memo generation is ~10 lines). The `body` field is required but unused in Phase 1 — this maintains API contract stability so the transition to Phase 2 introduces no breaking changes. The MCP client always has content body available in the save workflow
-- Phase 2: Extract orchestration logic to a `generateSuggestions` function. Dependencies (SearchService, KeywordExtractor, GrantResolver) are passed as arguments. See [Implementation Paradigm](#implementation-paradigm) for class adoption criteria
-- Error handling: Catch Phase 2 failures, log, return memo-only response
-
-### Service Layer
+- Phase 1: Logic is inline in handler (memo generation is ~10 lines). The `body` field is required but unused in Phase 1 — this maintains API contract stability
+- Phase 2: Orchestration function calls content analyzer, then fans out to search-evaluate pipeline and category generator in parallel. Dependencies injected for testability
+- Error handling: Catch Phase 2 failures at orchestration level, log, return memo-only
+- informationType mapping: When building `PathSuggestion` from `EvaluatedSuggestion`, the orchestrator attaches `ContentAnalysis.informationType` to each search-type suggestion (Req 13.1)
 
 #### MemoSuggestionGenerator
 
 | Field | Detail |
 |-------|--------|
 | Intent | Generate personal memo area path suggestion |
-| Requirements | 2.1, 2.2, 2.3, 2.4, 2.5 |
+| Requirements | 2.1, 2.2, 2.3, 2.4, 2.5, 6.2 |
 
 **Responsibilities & Constraints**
 
 - Check `disableUserPages` configuration via `crowi.configManager`
 - When user pages are enabled (default): Generate path `/user/{username}/memo/` using `userHomepagePath(user)` utility, set grant to `PageGrant.GRANT_OWNER` (4)
-- When user pages are disabled: Generate path under alternative namespace (e.g., `/memo/{username}/`), resolve grant from parent page. The exact alternative path is subject to confirmation
+- When user pages are disabled: Generate path under alternative namespace (e.g., `/memo/{username}/`), resolve grant from parent page
 - Set fixed description and label text
-- Always succeeds (path can be determined from either configuration)
+- Always succeeds
 
 **Contracts**: Service [x]
 
 ##### Service Interface
 
 ```typescript
-function generateMemoSuggestion(user: IUserHasId): PathSuggestion {
-  // Returns memo suggestion with type 'memo'
-}
+function generateMemoSuggestion(user: IUserHasId): PathSuggestion;
 ```
 
 - Preconditions: `user` has valid `username` field
-- Postconditions: Returns a `PathSuggestion` with `type: 'memo'`. When user pages are enabled, `grant: 4`; when disabled, grant is resolved from the parent page
+- Postconditions: Returns `PathSuggestion` with `type: 'memo'`, `grant: 4` when user pages enabled
 
-#### SearchSuggestionGenerator (Phase 2)
+#### ContentAnalyzer (Phase 2)
 
 | Field | Detail |
 |-------|--------|
-| Intent | Find related pages via keyword search and suggest their parent directory |
-| Requirements | 3.1, 3.2, 3.3, 3.4, 3.5, 5.2 |
+| Intent | Extract keywords and classify content information type via GROWI AI (1st AI call) |
+| Requirements | 5.1, 5.2, 5.3, 5.4 |
 
 **Responsibilities & Constraints**
 
-- Call `searchService.searchKeyword()` with extracted keywords
-- Select the top-1 result by Elasticsearch score; extract parent directory from its path
-- Generate description listing up to 3 related page titles (top results by score)
-- Resolve parent page grant via GrantResolver
-- Return `null` if no search results found
-- Note: Selection heuristic (top-1 by score) is the initial approach; may be refined with real-world data during Phase 2 implementation
+- Accept content body string
+- Delegate to GROWI AI (existing OpenAI feature) for a single AI call that performs:
+  - Keyword extraction: 3-5 keywords, prioritizing proper nouns and technical terms
+  - Flow/stock classification: Determine if content is flow information (time-bound: meeting notes, diaries, reports) or stock information (reference: documentation, knowledge base)
+- Return structured `ContentAnalysis` result
+- Existing `instructionsForInformationTypes` in commons.ts serves as reference for AI prompting but is not the sole classification criterion (per reviewer feedback)
 
 **Dependencies**
 
-- Outbound: SearchService — keyword search (P0)
-- Outbound: GrantResolver — parent page grant lookup (P1)
+- External: OpenAI Feature module — AI inference (P0)
 
 **Contracts**: Service [x]
 
 ##### Service Interface
 
 ```typescript
-function generateSearchSuggestion(
-  keywords: string[],
-  user: IUserHasId,
-  userGroups: PopulatedGrantedGroup[],
-): Promise<PathSuggestion | null>;
+interface ContentAnalysis {
+  keywords: string[];
+  informationType: 'flow' | 'stock';
+}
+
+function analyzeContent(body: string): Promise<ContentAnalysis>;
 ```
 
-- Preconditions: `keywords` is non-empty array
-- Postconditions: Returns `PathSuggestion` with `type: 'search'` or `null` if no results
+- Preconditions: `body` is non-empty string
+- Postconditions: Returns `ContentAnalysis` with 1-5 keywords and informationType
+- Error behavior: Throws on failure; caller handles fallback
 
-#### CategorySuggestionGenerator (Phase 2)
+#### SearchCandidateRetriever (Phase 2)
 
 | Field | Detail |
 |-------|--------|
-| Intent | Find matching top-level category directory for content |
-| Requirements | 4.1, 4.2, 4.3, 4.4, 5.2 |
+| Intent | Search for related pages using keywords and filter by score threshold |
+| Requirements | 3.1, 3.2, 3.5 |
 
 **Responsibilities & Constraints**
 
-- Call `searchService.searchKeyword()` with keywords scoped to top-level (`prefix:/`)
-- Select the top-1 result by Elasticsearch score; extract top-level path segment (e.g., `/tech-notes/React/hooks` → `/tech-notes/`)
-- Generate description from top-level segment name
-- Resolve parent page grant via GrantResolver
-- Return `null` if no matching top-level pages found
-- Note: Selection heuristic (top-1 by score) is the initial approach; may be refined with real-world data during Phase 2 implementation
+- Call `searchService.searchKeyword()` with extracted keywords
+- Filter results by Elasticsearch score threshold to retain only sufficiently relevant candidates
+- Return array of `SearchCandidate` objects with page path, snippet, and score
+- Return empty array if no results pass the threshold
+- Score threshold value is tunable (determined during implementation with real data)
 
 **Dependencies**
 
-- Outbound: SearchService — scoped keyword search (P0)
-- Outbound: GrantResolver — parent page grant lookup (P1)
+- Outbound: SearchService — keyword search (P0)
 
 **Contracts**: Service [x]
 
 ##### Service Interface
 
 ```typescript
-function generateCategorySuggestion(
+interface SearchCandidate {
+  pagePath: string;
+  snippet: string;
+  score: number;
+}
+
+function retrieveSearchCandidates(
   keywords: string[],
   user: IUserHasId,
   userGroups: PopulatedGrantedGroup[],
-): Promise<PathSuggestion | null>;
+): Promise<SearchCandidate[]>;
 ```
 
 - Preconditions: `keywords` is non-empty array
-- Postconditions: Returns `PathSuggestion` with `type: 'category'` or `null` if no results
+- Postconditions: Returns array of `SearchCandidate` (may be empty). All candidates have scores above the configured threshold
+- Note: Replaces the prior `SearchSuggestionGenerator` which performed top-1 selection and description generation. Those responsibilities moved to `CandidateEvaluator`
 
-#### KeywordExtractor (Phase 2)
+#### CandidateEvaluator (Phase 2)
 
 | Field | Detail |
 |-------|--------|
-| Intent | Extract search-relevant keywords from content body via GROWI AI |
-| Requirements | 5.1, 5.2 |
+| Intent | Evaluate search candidates via GROWI AI for content-destination fit, propose paths, generate descriptions (2nd AI call) |
+| Requirements | 3.3, 6.3, 10.1, 10.2, 10.3, 10.4, 11.1, 11.2, 11.3, 12.1, 12.2, 12.3, 12.4, 13.1 |
 
 **Responsibilities & Constraints**
 
-- Accept content body string
-- Delegate to GROWI AI (existing OpenAI feature) for keyword extraction
-- Return 3-5 keywords prioritizing proper nouns and technical terms
-- Avoid generic/common words
-- Implementation details are out of scope for this spec (handled in separate GROWI AI design)
+- Accept content body, content analysis (from 1st AI call), and search candidates
+- Delegate to GROWI AI for a single AI call that performs:
+  - **Candidate evaluation**: Assess each candidate's suitability considering content relevance and flow/stock alignment
+  - **Path proposal**: For each suitable candidate, propose a save location using one of 3 structural patterns:
+    - (a) Parent directory of the matching page
+    - (b) Subdirectory under the matching page
+    - (c) Sibling directory alongside the matching page (may generate new path)
+  - **Description generation**: Generate a description for each suggestion explaining why the location is suitable
+  - **Ranking**: Order suggestions by content-destination fit
+- Flow/stock alignment is a ranking factor, not a hard filter (10.4)
+- Sibling pattern (c) may generate paths that don't yet exist in GROWI (12.2). Generated paths must be at the same hierarchy level as the matching search candidate page (12.4)
+- AI context budget: Pass candidate paths + ES snippets, NOT full page bodies (see architecture doc)
+- Resolve grant for each proposed path via GrantResolver after AI evaluation returns
 
 **Dependencies**
 
 - External: OpenAI Feature module — AI inference (P0)
+- Outbound: GrantResolver — grant resolution for proposed paths (P1)
 
 **Contracts**: Service [x]
 
 ##### Service Interface
 
 ```typescript
-function extractKeywords(body: string): Promise<string[]>;
+interface EvaluatedSuggestion {
+  path: string;        // Proposed directory path with trailing /
+  label: string;       // Display label
+  description: string; // AI-generated rationale
+}
+
+function evaluateCandidates(
+  body: string,
+  analysis: ContentAnalysis,
+  candidates: SearchCandidate[],
+): Promise<EvaluatedSuggestion[]>;
 ```
 
-- Preconditions: `body` is non-empty string
-- Postconditions: Returns array of 0-5 keyword strings
+- Preconditions: `candidates` is non-empty array, `analysis` contains valid informationType
+- Postconditions: Returns array of `EvaluatedSuggestion` ordered by fit score (best first). May return empty array if no candidates are suitable. All paths end with `/`
 - Error behavior: Throws on failure; caller handles fallback
+- Note: Grant resolution is performed by the orchestrator after this function returns, not inside this function
+
+**Implementation Notes**
+
+- The 2nd AI call receives: POST body, informationType (from 1st call), and for each candidate: pagePath + ES snippet
+- The AI is instructed to consider the 3 path proposal patterns and select the most appropriate for each candidate
+- Existing `instructionsForInformationTypes` from commons.ts is referenced in the AI prompt as guidance for flow/stock assessment of candidate locations
+- AI prompt design details are deferred to implementation phase
 
-#### DescriptionGenerator
+#### CategorySuggestionGenerator (Phase 2 — Under Review)
 
 | Field | Detail |
 |-------|--------|
-| Intent | Generate human-readable description for each suggestion type |
-| Requirements | 6.1, 6.2, 6.3, 6.4, 6.5 |
+| Intent | Find matching top-level category directory for content |
+| Requirements | 4.1, 4.2, 4.3, 4.4 |
+
+> **Note**: This component has an existing implementation from the prior Phase 2 design. With the introduction of AI-based candidate evaluation, this component may overlap with CandidateEvaluator's path proposal capabilities. Whether to retain, merge, or remove is deferred to post-implementation reviewer discussion. The existing implementation is maintained as-is.
 
 **Responsibilities & Constraints**
 
-- `memo` type: Return fixed descriptive text (e.g., "Save to your personal memo area")
-- `search` type (Phase 2): List up to 3 related page titles from top search results by score. No AI usage — purely mechanical
-- `category` type (Phase 2): Generate from top-level path segment name. No AI usage — purely mechanical
+- Call `searchService.searchKeyword()` with keywords scoped to top-level (`prefix:/`)
+- Select the top-1 result by Elasticsearch score; extract top-level path segment
+- Generate description from top-level segment name (mechanical, no AI)
+- Resolve parent page grant via GrantResolver
+- Return `null` if no matching top-level pages found
+
+**Dependencies**
+
+- Outbound: SearchService — scoped keyword search (P0)
+- Outbound: GrantResolver — parent page grant lookup (P1)
 
 **Contracts**: Service [x]
 
 ##### Service Interface
 
 ```typescript
-function generateMemoDescription(): string;
-
-// Phase 2
-function generateSearchDescription(relatedPageTitles: string[]): string; // accepts up to 3 titles
-function generateCategoryDescription(topLevelSegment: string): string;
+function generateCategorySuggestion(
+  keywords: string[],
+  user: IUserHasId,
+  userGroups: PopulatedGrantedGroup[],
+): Promise<PathSuggestion | null>;
 ```
 
+- Preconditions: `keywords` is non-empty array
+- Postconditions: Returns `PathSuggestion` with `type: 'category'` or `null` if no results
+
 #### GrantResolver
 
 | Field | Detail |
@@ -479,8 +586,9 @@ function generateCategoryDescription(topLevelSegment: string): string;
 
 - Given a directory path, find the corresponding page in MongoDB
 - Return its `grant` value as the upper bound for child pages
-- For memo path: always returns `PageGrant.GRANT_OWNER` (4) — can be hardcoded in Phase 1
-- For search/category paths (Phase 2): query Page model for the parent page's grant
+- For memo path: always returns `PageGrant.GRANT_OWNER` (4)
+- For search/category/evaluated paths (Phase 2): query Page model for the parent page's grant
+- For new paths (sibling pattern): traverse upward to find the nearest existing ancestor page's grant
 
 **Dependencies**
 
@@ -515,26 +623,28 @@ No new database entities. The endpoint reads from existing models only.
 
 ```typescript
 interface SuggestPathRequest {
-  body: string; // Page content for keyword extraction
+  body: string; // Page content for analysis
 }
 ```
 
 **Validation rules**:
 
 - `body`: Required, non-empty string
-- No endpoint-specific maximum length. Body size is governed by GROWI's global Express body-parser configuration. The KeywordExtractor (Phase 2) handles truncation internally if content exceeds its processing capacity
+- No endpoint-specific maximum length. Body size is governed by GROWI's global Express body-parser configuration
 
 #### Response Schema
 
 ```typescript
 type SuggestionType = 'memo' | 'search' | 'category';
+type InformationType = 'flow' | 'stock';
 
 interface PathSuggestion {
   type: SuggestionType;
-  path: string;        // Directory path with trailing '/'
-  label: string;       // Display label for the suggestion
-  description: string; // Selection rationale
-  grant: number;       // Parent page grant (PageGrant value)
+  path: string;              // Directory path with trailing '/'
+  label: string;             // Display label for the suggestion
+  description: string;       // Selection rationale (fixed for memo, AI-generated for search)
+  grant: number;             // Parent page grant (PageGrant value)
+  informationType?: InformationType; // Content's information type as classified by GROWI AI (Phase 2, search-based only)
 }
 
 interface SuggestPathResponse {
@@ -549,6 +659,29 @@ interface SuggestPathResponse {
 - `grant` is a valid PageGrant value (1, 2, 4, or 5)
 - `type` is one of the defined SuggestionType values
 
+> **Resolved**: The `informationType` field has been added to `PathSuggestion` as an optional field for search-based suggestions. This supports the Client LLM Independence design principle (Requirement 13) by providing structured metadata that any client can use regardless of reasoning capability.
+
+#### Internal Types (Phase 2)
+
+```typescript
+interface ContentAnalysis {
+  keywords: string[];
+  informationType: 'flow' | 'stock';
+}
+
+interface SearchCandidate {
+  pagePath: string;
+  snippet: string;
+  score: number;
+}
+
+interface EvaluatedSuggestion {
+  path: string;        // Proposed directory path with trailing /
+  label: string;
+  description: string; // AI-generated rationale
+}
+```
+
 #### Phase 1 Response Example
 
 ```json
@@ -579,10 +712,19 @@ interface SuggestPathResponse {
     },
     {
       "type": "search",
-      "path": "/tech-notes/React/",
+      "path": "/tech-notes/React/state-management/",
       "label": "Save near related pages",
-      "description": "Related pages under this directory: React Hooks Guide, Jotai State Management",
-      "grant": 1
+      "description": "This area contains pages about React state management including Jotai and Redux. Your stock content fits well alongside this existing reference material.",
+      "grant": 1,
+      "informationType": "stock"
+    },
+    {
+      "type": "search",
+      "path": "/tech-notes/React/backend/",
+      "label": "New section for backend topics",
+      "description": "Related frontend pages exist nearby. This new section organizes your backend content as a sibling to the existing frontend knowledge.",
+      "grant": 1,
+      "informationType": "stock"
     },
     {
       "type": "category",
@@ -595,6 +737,8 @@ interface SuggestPathResponse {
 }
 ```
 
+Note: Phase 2 may return multiple `search`-type suggestions (one per evaluated candidate). The `category` suggestion appears if the CategorySuggestionGenerator finds a match (component under review).
+
 ## Error Handling
 
 ### Error Categories and Responses
@@ -607,23 +751,33 @@ interface SuggestPathResponse {
 | No authentication token/session | 401 | Authentication required | 8.2 |
 | AI service not enabled | 403 | GROWI AI is not enabled | 1.4 |
 
+**System Errors — Graceful Degradation (returns 200)**:
+
+| Error | Behavior | Fallback | Requirement |
+|-------|----------|----------|-------------|
+| Content analysis failure (1st AI call) | Log error, skip search pipeline | Memo suggestion only | 5.5 |
+| Search service failure | Log error, skip search-based suggestions | Memo + category (if available) | 3.5 |
+| Candidate evaluation failure (2nd AI call) | Log error, skip search-based suggestions | Memo + category (if available) | 11.4 |
+| Category generation failure | Log error, skip category suggestion | Memo + search-based (if available) | 4.4 |
+
 **System Errors (5xx)**:
 
-| Error | Status | Response | Behavior |
-|-------|--------|----------|----------|
-| Search service failure (Phase 2) | 200 | Memo suggestion only | Graceful degradation, log error |
-| GROWI AI failure (Phase 2) | 200 | Memo suggestion only | Graceful degradation, log error |
-| Unexpected error | 500 | Generic error, no internal details | Requirement 9.2 |
+| Error | Status | Response | Requirement |
+|-------|--------|----------|-------------|
+| Unexpected error | 500 | Generic error, no internal details | 9.2 |
 
-**Key decision**: Phase 2 failures degrade to Phase 1 behavior (memo-only) rather than returning errors. The memo suggestion is always generated first and acts as guaranteed fallback.
+**Key decision**: Phase 2 failures degrade gracefully rather than returning errors. The memo suggestion is generated first and acts as guaranteed fallback. Each Phase 2 component fails independently — content analysis failure skips the entire search pipeline, but category generation can still proceed if it runs independently.
 
 ## Testing Strategy
 
 ### Unit Tests
 
-- `MemoSuggestionGenerator`: Generates correct path from username, correct grant value, correct description
-- `DescriptionGenerator`: Fixed text for memo, page title listing for search, segment name for category
-- `GrantResolver`: Returns correct grant from page, default grant when page not found
+- `MemoSuggestionGenerator`: Correct path, grant, description for both user-pages-enabled and disabled cases
+- `ContentAnalyzer`: Correct keyword extraction, flow/stock classification, error propagation
+- `SearchCandidateRetriever`: Score threshold filtering, empty result handling, candidate structure
+- `CandidateEvaluator`: Path proposal patterns (parent/subdirectory/sibling), description generation, ranking, flow/stock consideration, error propagation
+- `CategorySuggestionGenerator`: Top-level extraction, description, grant, empty result handling
+- `GrantResolver`: Returns correct grant from page, default grant when page not found, ancestor traversal for new paths
 - `PathSuggestion` type validation: Trailing slash enforcement, required fields present
 - Request validation: Missing body, empty body, valid body
 
@@ -633,19 +787,23 @@ interface SuggestPathResponse {
 - `POST /suggest-path` without auth: Returns 401
 - `POST /suggest-path` with empty body: Returns 400
 - `POST /suggest-path` with AI disabled: Returns 403
-- Phase 2: Search returns results → includes search/category suggestions
-- Phase 2: Search returns nothing → memo-only response
-- Phase 2: AI extraction fails → memo-only fallback
+- Phase 2: Full pipeline — content analysis → search → candidate evaluation → response with multiple suggestion types
+- Phase 2: Content analysis fails → memo-only fallback
+- Phase 2: Search returns nothing → search-based suggestions omitted, category may still appear
+- Phase 2: Candidate evaluation fails → memo + category fallback
+- Phase 2: All Phase 2 components fail → memo-only response
 
 ### Performance (Phase 2)
 
-- Keyword extraction latency under typical content sizes
-- Search query performance with extracted keywords
-- Parallel generation of search + category suggestions
+- Content analysis (1st AI call) latency under typical content sizes
+- Candidate evaluation (2nd AI call) latency with varying numbers of candidates
+- Total end-to-end latency for the 2-AI-call flow
+- Parallel execution: search-evaluate pipeline vs category generation
 
 ## Security Considerations
 
 - **Authentication**: All requests require valid API token or login session (standard middleware)
 - **Authorization**: User can only see suggestions based on their own identity and permissions. Search results are permission-scoped via `searchKeyword()` user/group parameters
 - **Input safety**: Content body is passed to GROWI AI, not directly to Elasticsearch. No NoSQL injection risk from body content
+- **AI prompt injection**: Content body is user-provided and passed to AI. AI prompts should be structured to minimize prompt injection risk (system prompt + user content separation)
 - **Information leakage**: Error responses use generic messages per requirement 9.2. No stack traces or internal paths exposed

+ 83 - 20
.kiro/specs/suggest-path/requirements.md

@@ -6,10 +6,21 @@ The suggest-path feature provides an AI-powered API endpoint for GROWI that sugg
 
 The feature is delivered incrementally in two phases:
 
-- **Phase 1 (MVP)**: Personal memo path suggestion — establishes the endpoint, authentication, and response structure. Implemented first to provide immediate value.
-- **Phase 2 (Full)**: Search-based and category-based path suggestions powered by GROWI AI keyword extraction. Builds on the Phase 1 foundation.
+- **Phase 1 (MVP)**: Personal memo path suggestion — establishes the endpoint, authentication, and response structure. Implemented first to provide immediate value. **Implemented.**
+- **Phase 2 (Full)**: AI-powered search-based path suggestions with flow/stock information classification, multi-candidate evaluation, and intelligent path proposal. GROWI AI extracts keywords and classifies content type, searches for related pages, then evaluates candidates and proposes optimal save locations including newly generated paths.
 
-Both phases are covered by this specification. Implementation proceeds Phase 1 first, then Phase 2.
+Both phases are covered by this specification. Phase 1 is implemented. Phase 2 builds on the Phase 1 foundation.
+
+### Phase 2 Revision History
+
+Phase 2 requirements were revised based on reviewer feedback to incorporate:
+
+1. **Flow/stock information classification**: Content is classified as flow (time-bound) or stock (reference) information, and this classification informs save location evaluation.
+2. **Multi-candidate AI evaluation**: Instead of mechanically selecting the top-1 search result, multiple candidates are retrieved and evaluated by GROWI AI for content-destination fit.
+3. **Three-pattern path proposals**: The AI proposes paths using three structural patterns (parent directory, subdirectory, sibling page including new paths), enabling more precise save location suggestions.
+4. **AI-generated descriptions**: Phase 2 suggestion descriptions are generated by GROWI AI as part of candidate evaluation, providing richer context for user decision-making.
+
+Requirements 3–6 have been updated and new requirements 10–12 added to reflect these changes. Requirement 4 (Category-Based Path Suggestion) is under review — the existing implementation is retained, but its relationship to the new AI-based evaluation approach will be determined after implementation and reviewer discussion.
 
 ## Out of Scope
 
@@ -43,40 +54,44 @@ The following are explicitly **not** part of this feature:
 4. The Suggest Path Service shall set `grant` to `4` (owner only) for memo type suggestions when using the user home directory path.
 5. The Suggest Path Service shall provide a fixed descriptive text in the `description` field for memo type suggestions.
 
-### Requirement 3: Search-Based Path Suggestion (Phase 2)
+### Requirement 3: Search-Based Path Suggestion (Phase 2) — Revised
 
-**Objective:** As a user, I want save locations suggested near related existing pages, so that my content is organized alongside relevant material.
+**Objective:** As a user, I want save locations suggested based on search results that have been evaluated for relevance and content-destination fit, so that my content is organized alongside the most appropriate related material.
 
 #### Acceptance Criteria
 
 1. When keywords have been extracted from the content, the Suggest Path Service shall search for related existing pages using those keywords.
-2. When related pages are found, the Suggest Path Service shall return the parent directory of the most relevant page as a suggestion with type `search`.
-3. When related pages are found, the Suggest Path Service shall include related page titles in the `description` field as selection rationale.
-4. The Suggest Path Service shall include the parent page's `grant` value for `search` type suggestions.
-5. If no related pages are found, the Suggest Path Service shall omit the `search` type suggestion from the response.
+2. When search results are returned, the Suggest Path Service shall filter candidates using an Elasticsearch score threshold to retain only sufficiently relevant results.
+3. When multiple candidates pass the score threshold, the Suggest Path Service shall pass all candidates to AI-based evaluation (see Requirement 11) rather than mechanically selecting a single top result.
+4. The Suggest Path Service shall include the parent page's `grant` value for each search-based suggestion.
+5. If no related pages are found or no candidates pass the score threshold, the Suggest Path Service shall omit search-based suggestions from the response.
 
-### Requirement 4: Category-Based Path Suggestion (Phase 2)
+### Requirement 4: Category-Based Path Suggestion (Phase 2) — Under Review
 
 **Objective:** As a user, I want a top-level category directory suggested, so that content can be organized under broad topic areas.
 
-#### Acceptance Criteria
+> **Note**: This requirement has an existing implementation (Phase 2, prior revision). With the introduction of AI-based candidate evaluation (Requirements 11, 12), the `category` suggestion type may overlap with the search-based approach that now evaluates candidates holistically across all tree levels. Whether to retain `category` as a distinct type, merge it into `search`, or remove it will be determined after implementation and reviewer discussion. The existing implementation is maintained as-is until that decision is made.
+
+#### Acceptance Criteria (prior revision — retained)
 
 1. When keywords have been extracted from the content, the Suggest Path Service shall search for matching pages scoped to top-level directories.
 2. When matching pages are found, the Suggest Path Service shall extract the top-level path segment and return it as a suggestion with type `category`.
 3. The Suggest Path Service shall include the parent page's `grant` value for `category` type suggestions.
 4. If no matching top-level pages are found, the Suggest Path Service shall omit the `category` type suggestion from the response.
 
-### Requirement 5: Content Keyword Extraction (Phase 2)
+### Requirement 5: Content Analysis via GROWI AI (Phase 2) — Revised
 
-**Objective:** As a system operator, I want keyword extraction centralized in GROWI AI, so that suggestion quality is consistent regardless of the calling client's capabilities.
+**Objective:** As a system operator, I want content analysis (keyword extraction and information type classification) centralized in GROWI AI, so that suggestion quality is consistent regardless of the calling client's capabilities.
 
 #### Acceptance Criteria
 
-1. When the client sends content body, the Suggest Path Service shall delegate keyword extraction to GROWI AI rather than requiring the client to pre-extract keywords.
-2. The Suggest Path Service shall use extracted keywords (not raw content body) for search operations.
-3. If keyword extraction fails or produces no usable keywords, the Suggest Path Service shall still return the memo suggestion (Phase 1 fallback).
+1. When the client sends content body, the Suggest Path Service shall delegate content analysis to GROWI AI in a single AI call that performs both keyword extraction and flow/stock information type classification.
+2. The Suggest Path Service shall extract 3–5 keywords from the content, prioritizing proper nouns and technical terms.
+3. The Suggest Path Service shall use extracted keywords (not raw content body) for search operations.
+4. The Suggest Path Service shall classify the content as either flow information (time-bound: meeting notes, diaries, reports) or stock information (reference: documentation, knowledge base articles).
+5. If content analysis fails or produces no usable keywords, the Suggest Path Service shall still return the memo suggestion (Phase 1 fallback).
 
-### Requirement 6: Suggestion Description Generation
+### Requirement 6: Suggestion Description Generation — Revised
 
 **Objective:** As a user, I want each suggestion to include a meaningful description, so that I can make an informed choice about where to save my content.
 
@@ -84,9 +99,7 @@ The following are explicitly **not** part of this feature:
 
 1. The Suggest Path Service shall include a `description` field in each suggestion that provides rationale for selecting that save location.
 2. While in Phase 1, the Suggest Path Service shall use fixed descriptive text for `memo` type suggestions.
-3. While in Phase 2, when returning `search` type suggestions, the Suggest Path Service shall generate the `description` by listing titles of related pages found under the suggested directory.
-4. While in Phase 2, when returning `category` type suggestions, the Suggest Path Service shall generate the `description` from the top-level path segment name.
-5. The Suggest Path Service shall generate Phase 2 descriptions mechanically from search results without using GROWI AI.
+3. While in Phase 2, when returning search-based suggestions, the Suggest Path Service shall generate the `description` as part of AI-based candidate evaluation (see Requirement 11), providing context about content relevance and flow/stock alignment.
 
 ### Requirement 7: Grant Constraint Information
 
@@ -115,3 +128,53 @@ The following are explicitly **not** part of this feature:
 
 1. If the `body` field is missing or empty in the request, the Suggest Path Service shall return a validation error.
 2. If an internal error occurs during path suggestion generation, the Suggest Path Service shall return an appropriate error response without exposing internal system details.
+
+### Requirement 10: Flow/Stock Information Type Awareness (Phase 2)
+
+**Objective:** As a user, I want save location suggestions that consider whether my content is time-bound (flow) or reference (stock) information, so that content is placed in locations that match its information type.
+
+#### Acceptance Criteria
+
+1. When evaluating search candidates, the Suggest Path Service shall consider the flow/stock alignment between the content being saved and the candidate save locations.
+2. When a candidate's path or surrounding content suggests flow characteristics (date-based paths, meeting-related terms), the Suggest Path Service shall treat it as a flow-oriented location.
+3. When a candidate's path or surrounding content suggests stock characteristics (topic-based paths, reference material), the Suggest Path Service shall treat it as a stock-oriented location.
+4. The Suggest Path Service shall use flow/stock alignment as one factor in candidate ranking, not as a hard filter — suggestions may include both matching and mismatched information types.
+
+### Requirement 11: AI-Based Candidate Evaluation and Ranking (Phase 2)
+
+**Objective:** As a user, I want search result candidates evaluated by AI for content-destination fit, so that the most appropriate save locations are prioritized in the suggestions.
+
+#### Acceptance Criteria
+
+1. When multiple search candidates are available, the Suggest Path Service shall evaluate each candidate's suitability by passing the content body along with each candidate's path and search snippet to GROWI AI.
+2. The Suggest Path Service shall rank candidates based on content-destination fit, considering content relevance and flow/stock information type alignment.
+3. The Suggest Path Service shall generate a description for each suggestion as part of the evaluation, explaining why the location is suitable.
+4. If AI-based candidate evaluation fails, the Suggest Path Service shall fall back to memo-only response.
+
+### Requirement 12: Path Proposal Patterns (Phase 2)
+
+**Objective:** As a user, I want path suggestions that include not only existing directories but also newly generated paths, so that my content can be organized in the most logical location even when no perfect existing directory matches.
+
+#### Acceptance Criteria
+
+1. When proposing save locations based on search results, the Suggest Path Service shall consider three structural patterns relative to each matching page: (a) parent directory of the matching page, (b) subdirectory under the matching page, (c) sibling directory alongside the matching page.
+2. When the sibling directory pattern is selected, the Suggest Path Service shall generate an appropriate new directory name based on the content being saved. This path may not yet exist in GROWI.
+3. The Suggest Path Service shall determine which pattern(s) are most appropriate based on the content-destination fit evaluation.
+4. When the sibling directory pattern is selected, the generated path shall be at the same hierarchy level as the matching search candidate page. The AI shall not generate paths deeper or shallower than the candidate's level.
+
+### Requirement 13: Client LLM Independence (Phase 2)
+
+**Objective:** As a system operator, I want the API response to be usable by any AI client regardless of its reasoning capability, so that suggestion quality does not degrade when accessed by less capable LLM clients.
+
+#### Design Rationale
+
+The suggest-path API is consumed by MCP clients powered by various LLM models, which may differ significantly in reasoning capability. To minimize the impact of client-side model performance differences:
+
+- Heavy reasoning tasks (content analysis, candidate evaluation, path proposal) are centralized in GROWI AI on the server side.
+- The API response includes structured data fields (not just natural language descriptions) so that even less capable clients can make correct decisions through simple field access.
+
+#### Acceptance Criteria
+
+1. The Suggest Path Service shall include an `informationType` field (`'flow'` or `'stock'`) in each search-based suggestion, representing the content's information type as determined by GROWI AI.
+2. The Suggest Path Service shall provide both structured metadata (`informationType`, `type`, `grant`) and natural language context (`description`) in each suggestion, enabling clients to use whichever is appropriate for their capability level.
+3. The Suggest Path Service shall ensure that all reasoning-intensive operations (keyword extraction, flow/stock classification, candidate evaluation, path proposal, description generation) are performed server-side by GROWI AI, not delegated to the client.

+ 1 - 1
.kiro/specs/suggest-path/spec.json

@@ -1,7 +1,7 @@
 {
   "feature_name": "suggest-path",
   "created_at": "2026-02-10T12:00:00Z",
-  "updated_at": "2026-02-17T04:15:00Z",
+  "updated_at": "2026-02-20T08:15:00Z",
   "language": "en",
   "phase": "tasks-generated",
   "approvals": {

+ 101 - 64
.kiro/specs/suggest-path/tasks.md

@@ -1,8 +1,8 @@
 # Implementation Plan
 
-## Phase 1 (MVP)
+## Phase 1 (MVP) — Implemented
 
-- [x] 1. Phase 1 MVP — Shared types and memo path suggestion
+- [x] 1. Phase 1 MVP — Shared types, memo path suggestion, and endpoint registration
 - [x] 1.1 Define suggestion types and implement memo path generation
   - Define the suggestion response types used across both phases: suggestion type discriminator, individual suggestion structure with type/path/label/description/grant fields, and the response wrapper
   - Implement memo path generation: when user pages are enabled (default), generate path under the user's home directory with owner-only grant; when user pages are disabled, generate path under an alternative namespace with hardcoded owner-only grant (actual parent grant resolution deferred to Phase 2 task 2)
@@ -27,90 +27,127 @@
   - Verify response structure: correct fields, trailing slash on path, correct grant value
   - _Requirements: 1.1, 1.2, 1.3, 1.4, 2.1, 8.1, 8.2, 9.1, 9.2_
 
-## Phase 2
+## Phase 2 — Revised
 
-- [x] 2. (P) Implement parent page grant resolution
-  - Implement a function that accepts a directory path and returns the corresponding page's grant value as the upper bound for child page permissions
-  - When the parent page exists, return its grant value; when not found, return owner-only grant as a safe default
-  - Update memo suggestion generation for the user-pages-disabled case to use actual parent grant resolution instead of the Phase 1 hardcoded value
-  - Include unit tests for grant lookup with existing page, missing page, and various grant values
-  - _Requirements: 7.1, 7.2, 2.4_
+- [ ] 2. (P) Enhance grant resolver for ancestor path traversal
+  - Enhance the existing grant resolution to support paths that may not yet exist in GROWI, as required by the sibling pattern where new directory names are generated
+  - When the direct parent page exists, return its grant value as the upper bound for child page permissions
+  - When the direct parent page is not found, traverse upward through ancestor paths to find the nearest existing page's grant
+  - When no ancestor page is found at any level, return owner-only grant as a safe default
+  - Include unit tests for: direct parent found, ancestor found at various depths, no ancestor found (safe default), root-level paths, paths with trailing slashes
+  - _Requirements: 7.1, 7.2_
 
-- [x] 3. (P) Implement content keyword extraction via GROWI AI
-  - Implement a function that accepts content body and delegates keyword extraction to the existing AI feature module
-  - Return 3-5 keywords prioritizing proper nouns and technical terms, avoiding generic words
-  - On extraction failure, throw an error so the caller can handle fallback logic
-  - Include unit tests for successful extraction, empty results, and failure scenarios
-  - _Requirements: 5.1, 5.2_
+- [ ] 3. (P) Content analysis via GROWI AI (1st AI call)
+  - Implement content analysis that delegates to GROWI AI for a single AI call performing both keyword extraction and flow/stock information type classification
+  - Extract 1-5 keywords from the content, prioritizing proper nouns and technical terms over generic words
+  - Classify the content as either flow information (time-bound: meeting notes, diaries, reports) or stock information (reference: documentation, knowledge base articles)
+  - Reference the existing flow/stock classification guidance as a prompt reference, without treating it as the sole classification criterion
+  - On analysis failure or inability to produce usable keywords, throw an error so the caller can handle fallback logic
+  - Include unit tests for: successful keyword extraction with quality verification, correct flow/stock classification for representative content samples, edge cases (very short content, ambiguous content), and failure propagation
+  - _Requirements: 5.1, 5.2, 5.4_
 
-- [x] 4. Search and category suggestion generators
-- [x] 4.1 (P) Implement search-based path suggestion
-  - Implement a function that accepts extracted keywords and searches for related existing pages using the search service
-  - Select the most relevant result and extract its parent directory as the suggested save location
-  - Generate a description by listing titles of up to 3 top-scoring related pages found under the suggested directory — purely mechanical, no AI
-  - Resolve the parent page's grant value using the grant resolver
-  - Return null when no search results are found, so this suggestion type is omitted from the response
-  - Include unit tests for result selection, parent directory extraction, description generation, grant resolution, and empty-result handling
-  - _Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 5.2, 6.3, 6.5_
+- [ ] 4. (P) Search candidate retrieval with score threshold filtering
+  - Implement search candidate retrieval that searches for related pages using extracted keywords via the existing search service
+  - Use extracted keywords (not raw content body) for search operations
+  - Filter search results using an Elasticsearch score threshold to retain only sufficiently relevant candidates
+  - Return an array of candidates with page path, snippet, and score for downstream AI evaluation
+  - Return an empty array if no results pass the threshold, allowing the caller to omit search-based suggestions
+  - The score threshold value is configurable and will be tuned with real data during implementation
+  - Include unit tests for: multi-result retrieval, threshold filtering (candidates above/below/at threshold), empty result handling, and correct candidate structure
+  - _Requirements: 3.1, 3.2, 3.5, 5.3_
 
-- [x] 4.2 (P) Implement category-based path suggestion
-  - Implement a function that accepts extracted keywords and searches for matching pages scoped to top-level directories
+- [ ] 5. (P) AI-based candidate evaluation and path proposal (2nd AI call)
+  - Implement candidate evaluation that delegates to GROWI AI for a single AI call evaluating search candidates for content-destination fit
+  - Evaluate each candidate's suitability by passing the content body, the content analysis results (keywords and informationType from the 1st AI call), and each candidate's path and search snippet
+  - For each suitable candidate, propose a save location using one of three structural patterns relative to the matching page: (a) parent directory, (b) subdirectory under the matching page, (c) sibling directory alongside the matching page
+  - When the sibling pattern is selected, generate an appropriate new directory name based on the content being saved; the generated path must be at the same hierarchy level as the matching search candidate page
+  - Generate a description for each suggestion explaining why the location is suitable, considering content relevance and flow/stock alignment
+  - Rank suggestions by content-destination fit, using flow/stock information type alignment as a ranking factor rather than a hard filter
+  - Pass candidate paths and ES snippets to the AI context, not full page bodies, to manage AI context budget
+  - On evaluation failure, throw an error so the caller can handle fallback logic
+  - Include unit tests for: path pattern selection across all three patterns, sibling path generation at correct hierarchy level, AI-generated description quality, ranking order, flow/stock alignment consideration, and failure propagation
+  - _Requirements: 3.3, 6.3, 10.1, 10.2, 10.3, 10.4, 11.1, 11.2, 11.3, 12.1, 12.2, 12.3, 12.4_
+
+- [x] 6. (P) Category-based path suggestion (under review — prior implementation retained)
+  - This component has an existing implementation from the prior Phase 2 design; it is retained as-is pending reviewer discussion on whether to keep, merge, or remove
+  - Search for matching pages scoped to top-level directories using extracted keywords
   - Extract the top-level path segment from the most relevant result as the suggested category directory
-  - Generate a description from the top-level segment name — purely mechanical, no AI
-  - Resolve the parent page's grant value using the grant resolver
+  - Generate a description from the top-level segment name using mechanical text, not AI
+  - Resolve the parent page's grant value via grant resolution
   - Return null when no matching top-level pages are found, so this suggestion type is omitted from the response
-  - Include unit tests for top-level segment extraction, description generation, grant resolution, and empty-result handling
-  - _Requirements: 4.1, 4.2, 4.3, 4.4, 5.2, 6.4, 6.5_
+  - Include unit tests for: top-level segment extraction, description generation, grant resolution, and empty result handling
+  - _Requirements: 4.1, 4.2, 4.3, 4.4_
 
-- [x] 5. Phase 2 orchestration and integration
-- [x] 5.1 Wire suggestion generators into unified orchestration with graceful degradation
-  - Implement the orchestration function that invokes all suggestion generators: memo (always), then keyword extraction followed by search and category generators in parallel
-  - On keyword extraction or search service failure, fall back to memo-only response while logging the error
-  - Collect non-null suggestions into the response array, ensuring memo is always present
-  - Update the route handler to use the orchestration function with injected dependencies
-  - Include unit tests for successful multi-suggestion response, partial failures with graceful degradation, and complete Phase 2 failure falling back to memo only
-  - _Requirements: 5.3, 6.1, 9.2_
+- [ ] 7. Phase 2 revised orchestration and integration
+- [ ] 7.1 Rewrite orchestration for revised Phase 2 pipeline
+  - Rewrite the orchestration function to implement the revised Phase 2 pipeline: always generate memo suggestion first as guaranteed fallback, then invoke content analysis (1st AI call), pass keywords to search candidate retrieval, pass candidates to candidate evaluation (2nd AI call), and run category generation in parallel with the search-evaluate pipeline
+  - After candidate evaluation returns, resolve grant for each proposed path via grant resolver
+  - Map the informationType from content analysis onto each search-type suggestion in the final response, and add informationType as an optional field on the suggestion type
+  - Ensure the response includes both structured metadata (informationType, type, grant) and natural language context (description) for client LLM independence
+  - Ensure all reasoning-intensive operations (keyword extraction, flow/stock classification, candidate evaluation, path proposal, description generation) are performed server-side
+  - Handle graceful degradation at each failure point: content analysis failure skips the entire search pipeline (memo-only), candidate evaluation failure falls back to memo + category (if available), category failure is independent and does not affect the search pipeline
+  - Ensure the response always contains at least one suggestion (memo type)
+  - Update the route handler to use the revised orchestration function with injected dependencies
+  - Include unit tests for: full pipeline success with all suggestion types, partial failures at each stage with correct degradation, informationType mapping to PathSuggestion, dependency injection, and parallel execution of category vs search-evaluate pipeline
+  - _Requirements: 1.1, 1.2, 1.3, 3.3, 3.4, 5.3, 5.5, 8.3, 9.2, 11.4, 13.1, 13.2, 13.3_
 
-- [x] 5.2 Phase 2 integration verification
-  - Verify the complete flow: content body to keyword extraction to parallel search and category suggestions to unified response with all suggestion types
-  - Verify graceful degradation: when search returns no results, those suggestion types are omitted; when keyword extraction fails, memo-only response is returned
-  - Verify response structure across all suggestion types: correct fields, descriptions, grant values, and trailing slashes
-  - _Requirements: 3.1, 3.5, 4.4, 5.3, 6.3, 6.4_
+- [ ] 7.2 Phase 2 integration verification
+  - Verify the complete revised flow end-to-end: content body → content analysis (keywords + informationType) → search candidate retrieval (with score threshold) → candidate evaluation (path proposals + descriptions) → grant resolution → unified response with all suggestion types
+  - Verify informationType field is present in search-based suggestions and absent in memo and category suggestions
+  - Verify path proposal patterns work correctly: parent directory, subdirectory, and sibling with generated new paths at the correct hierarchy level
+  - Verify graceful degradation at each failure point: content analysis failure → memo-only, search returns empty → search suggestions omitted, candidate evaluation failure → memo + category, category failure → memo + search, all Phase 2 failures → memo-only
+  - Verify response structure across all suggestion types: correct fields, AI-generated descriptions for search type, fixed description for memo, mechanical description for category, valid grant values, and trailing slashes on all paths
+  - _Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 5.1, 5.4, 5.5, 6.1, 6.3, 10.1, 11.1, 11.4, 12.1, 13.1, 13.2_
 
 ## Requirements Coverage
 
 | Requirement | Task(s) |
 |-------------|---------|
-| 1.1 | 1.2, 1.3 |
-| 1.2 | 1.1 |
-| 1.3 | 1.1, 1.3 |
+| 1.1 | 1.2, 1.3, 7.1 |
+| 1.2 | 1.1, 1.3, 7.1 |
+| 1.3 | 1.1, 1.3, 7.1 |
 | 1.4 | 1.2, 1.3 |
 | 2.1 | 1.1, 1.3 |
 | 2.2 | 1.1 |
 | 2.3 | 1.1 |
 | 2.4 | 1.1 |
 | 2.5 | 1.1 |
-| 3.1 | 4.1, 5.2 |
-| 3.2 | 4.1 |
-| 3.3 | 4.1 |
-| 3.4 | 4.1 |
-| 3.5 | 4.1, 5.2 |
-| 4.1 | 4.2 |
-| 4.2 | 4.2 |
-| 4.3 | 4.2 |
-| 4.4 | 4.2, 5.2 |
-| 5.1 | 3 |
-| 5.2 | 3, 4.1, 4.2 |
-| 5.3 | 5.1, 5.2 |
-| 6.1 | 1.1, 5.1 |
+| 3.1 | 4, 7.2 |
+| 3.2 | 4, 7.2 |
+| 3.3 | 5, 7.1, 7.2 |
+| 3.4 | 7.1, 7.2 |
+| 3.5 | 4, 7.2 |
+| 4.1 | 6 |
+| 4.2 | 6 |
+| 4.3 | 6 |
+| 4.4 | 6 |
+| 5.1 | 3, 7.2 |
+| 5.2 | 3 |
+| 5.3 | 4, 7.1 |
+| 5.4 | 3, 7.2 |
+| 5.5 | 7.1, 7.2 |
+| 6.1 | 1.1, 7.2 |
 | 6.2 | 1.1 |
-| 6.3 | 4.1, 5.2 |
-| 6.4 | 4.2, 5.2 |
-| 6.5 | 4.1, 4.2 |
+| 6.3 | 5, 7.2 |
 | 7.1 | 2 |
 | 7.2 | 2 |
 | 8.1 | 1.2, 1.3 |
 | 8.2 | 1.2, 1.3 |
-| 8.3 | 1.2 |
+| 8.3 | 1.2, 7.1 |
 | 9.1 | 1.2, 1.3 |
-| 9.2 | 1.2, 5.1 |
+| 9.2 | 1.2, 7.1 |
+| 10.1 | 5, 7.2 |
+| 10.2 | 5 |
+| 10.3 | 5 |
+| 10.4 | 5 |
+| 11.1 | 5, 7.2 |
+| 11.2 | 5 |
+| 11.3 | 5 |
+| 11.4 | 7.1, 7.2 |
+| 12.1 | 5, 7.2 |
+| 12.2 | 5 |
+| 12.3 | 5 |
+| 12.4 | 5 |
+| 13.1 | 7.1, 7.2 |
+| 13.2 | 7.1, 7.2 |
+| 13.3 | 7.1 |