diff --git a/docs-archived/implplan/SPRINT_20260308_011_FE_search_clarify_guidance_not_query.md b/docs-archived/implplan/SPRINT_20260308_011_FE_search_clarify_guidance_not_query.md new file mode 100644 index 000000000..00283d08d --- /dev/null +++ b/docs-archived/implplan/SPRINT_20260308_011_FE_search_clarify_guidance_not_query.md @@ -0,0 +1,77 @@ +# Sprint 20260308-011 - FE Search Clarify Guidance Not Query + +## Topic & Scope +- Stop rendering clarify prompts as executable search suggestions when they are only asking the operator to narrow the target. +- Fix the release route wording so `/releases/**` no longer suggests dead natural-language queries like "Which environment or release should I narrow this to?". +- Cover the behavior with component and browser tests so dead clarify prompts do not regress back into the shipped search experience. +- Working directory: `src/Web/StellaOps.Web`. +- Expected evidence: component tests, Playwright regression, and updated UI contract docs. + +## Dependencies & Concurrency +- Depends on the archived search rollout/correction sprints and the archived live search setup lane. +- Safe parallelism: do not edit unrelated Router, release cutover, or audit-bundle files while executing this search-only fix. +- Cross-module allowance: documentation updates may touch `docs/modules/ui/**` when clarifying the search self-serve contract. + +## Documentation Prerequisites +- `src/Web/StellaOps.Web/AGENTS.md` +- `docs/modules/ui/search-self-serve-contract.md` +- `docs/modules/ui/search-zero-learning-primary-entry.md` + +## Delivery Tracker + +### FE-CLARIFY-001 - Treat clarify prompts as guidance, not runnable search chips +Status: DONE +Dependency: none +Owners: Developer (FE), QA +Task description: +- Update the global search answer panel so clarify prompts are rendered as guidance hints instead of clickable search chips. +- Keep grounded follow-up questions executable; only the clarify branch should stop behaving like a query launcher. + +Completion criteria: +- [x] Clarify prompts no longer execute searches when rendered from the answer panel. +- [x] Grounded follow-up questions remain executable. +- [x] Search UX still offers actionable next steps through viable starters and related searches. + +### FE-CLARIFY-002 - Fix release-route clarify wording and regression coverage +Status: DONE +Dependency: FE-CLARIFY-001 +Owners: Developer (FE), Test Automation +Task description: +- Replace the release clarify wording with guidance phrasing instead of a literal natural-language question. +- Add unit and Playwright coverage proving the clarify UI is guidance-only and release users do not see the old dead-query text as an executable suggestion. + +Completion criteria: +- [x] Release clarify copy is guidance-oriented. +- [x] Unit coverage proves clarify prompts are guidance-only. +- [x] Playwright regression covers the clarify-answer rendering path. + +### FE-CLARIFY-003 - Document and close +Status: DONE +Dependency: FE-CLARIFY-002 +Owners: Documentation, Project Manager +Task description: +- Update the UI search contract docs to state that clarify prompts are explanatory narrowing guidance, not executable searches. +- Archive the sprint after implementation and evidence are recorded. + +Completion criteria: +- [x] UI docs reflect the clarify-guidance behavior. +- [x] Execution log captures code/test evidence. +- [x] Sprint is archived only after all tasks are DONE. + +## Execution Log +| Date (UTC) | Update | Owner | +| --- | --- | --- | +| 2026-03-08 | Sprint created to stop dead clarify prompts from being surfaced like runnable searches. | Developer | +| 2026-03-08 | Updated global search clarify rendering to guidance-only rows, changed route clarifier copy to narrowing guidance, and kept grounded follow-up questions executable. | Developer | +| 2026-03-08 | Verified focused FE coverage: `ambient-context.service.spec.ts` + `global-search.component.spec.ts` passed `37/37`; Playwright clarify regression pack passed `18/18`. | QA | + +## Decisions & Risks +- Decision: fix this at the search contract level rather than patching only `/releases/versions`. +- Risk: clarify prompts currently share the same rendering path as grounded follow-up questions. +- Mitigation: split the clarify-answer rendering path while keeping grounded follow-ups and related searches untouched. +- Decision: clarify prompts are now guidance copy everywhere they are generated, so the UI never presents a dead natural-language query as an executable chip. +- Docs: `docs/modules/ui/search-self-serve-contract.md`, `docs/modules/ui/search-zero-learning-primary-entry.md`. + +## Next Checkpoints +- 2026-03-08: clarify answer panel updated and covered by tests. +- 2026-03-08: sprint archived after green regression coverage. diff --git a/docs/modules/ui/search-self-serve-contract.md b/docs/modules/ui/search-self-serve-contract.md index c468cf1e9..1c5a1669f 100644 --- a/docs/modules/ui/search-self-serve-contract.md +++ b/docs/modules/ui/search-self-serve-contract.md @@ -42,7 +42,7 @@ - `clarify` - `insufficient` - `grounded` states must expose visible evidence summary plus citations or top-result references. -- `clarify` states must offer narrowing questions instead of a blank panel. +- `clarify` states must offer narrowing guidance instead of a blank panel. - `insufficient` states must explain the lack of grounding and still provide credible next questions or next searches. ## Runtime behavior @@ -53,7 +53,7 @@ - fallback -> related follow-up question - Result-state answer panels use: - `commonQuestions[]` as follow-up questions when grounded evidence exists - - `clarifyingQuestions[]` when no grounded answer exists + - `clarifyingQuestions[]` as non-executable narrowing guidance when no grounded answer exists - "Related searches" remains driven by contextual chip logic so pages do not need to define a second parallel action system. ## Optional telemetry hooks @@ -85,7 +85,7 @@ 2. Add unit coverage for answer-state rendering in `global-search.component.spec.ts`. 3. Add Playwright coverage for: - grounded answer - - clarify recovery + - clarify guidance rendering - answer-to-AdvisoryAI handoff - priority-route rollout journeys (`findings`, `policy`, `doctor`, `timeline`, `releases`) 4. Keep route and API behavior mocked/deterministic; no live network dependencies. diff --git a/docs/modules/ui/search-zero-learning-primary-entry.md b/docs/modules/ui/search-zero-learning-primary-entry.md index f83f19b36..23cc900d3 100644 --- a/docs/modules/ui/search-zero-learning-primary-entry.md +++ b/docs/modules/ui/search-zero-learning-primary-entry.md @@ -18,12 +18,14 @@ - The current history contract cannot reliably remove old failed searches after reload because the local store still accepts legacy bare-string entries with no result outcome attached. - `Did you mean` is still visually tied to the results surface rather than the input correction moment. It needs to live immediately below the search field. - Suggestions are still too easy to surface without enough corpus proof. Search must treat corpus readiness and suggestion executability as a product requirement, not a test-only concern. +- Clarify prompts can still be mistaken for executable searches when they are phrased like literal user queries. Those prompts must behave as narrowing guidance, not dead-end starter searches. ## What still fails after direct operator usage - Search and assistant still feel like sibling products in some flows. The operator should always start from search; assistant is a secondary detail view opened from that same surface. - Any visible "scope" mechanic is wrong for the default path. Current route, visible entities, and recent actions should weight results automatically. If the best answer is outside the current page, show it as overflow only after trying the current page first. - The product still risks teaching internal mechanics through labels, panels, and helper copy. The operator should not need to learn Stella structure, search science, or result modes just to get a useful answer. - Suggestions are not acceptable unless they execute. A surfaced starter chip that leads to zero useful results is a product defect even when the service is healthy. +- Clarify text must not look like an executable starter search unless the backend has explicitly grounded it. Guidance such as "add an environment or release" belongs in the answer panel as guidance, not as a clickable query. - When multiple high-confidence results are close, search should summarize them automatically. The assistant exists to expand and deepen the answer, not to compensate for a weak primary result. ## Product rules diff --git a/src/Web/StellaOps.Web/src/app/core/services/search-context.registry.ts b/src/Web/StellaOps.Web/src/app/core/services/search-context.registry.ts index 03e457ee4..91207b53b 100644 --- a/src/Web/StellaOps.Web/src/app/core/services/search-context.registry.ts +++ b/src/Web/StellaOps.Web/src/app/core/services/search-context.registry.ts @@ -94,11 +94,11 @@ export const DEFAULT_COMMON_QUESTIONS: readonly SearchQuestionChip[] = withQuest export const DEFAULT_CLARIFYING_QUESTIONS: readonly SearchQuestionChip[] = withQuestionKind([ { key: 'ui.search.question.default.clarify_target', - fallback: 'Which workload, release, or policy should I narrow this to?', + fallback: 'Add the workload, release, or policy you want to inspect.', }, { key: 'ui.search.question.default.clarify_scope', - fallback: 'What exact blocker or symptom should I narrow this to?', + fallback: 'Add the exact blocker or symptom you want to inspect.', }, ], 'clarify'); @@ -153,8 +153,8 @@ const FINDINGS_SELF_SERVE: SearchSelfServeDefinition = { { key: 'ui.search.question.findings.remediation', fallback: 'What is the safest remediation path?' }, ], 'page'), clarifyingQuestions: withQuestionKind([ - { key: 'ui.search.question.findings.clarify_target', fallback: 'Which CVE, workload, or package should I narrow this to?' }, - { key: 'ui.search.question.findings.clarify_scope', fallback: 'What part of this finding matters most right now?' }, + { key: 'ui.search.question.findings.clarify_target', fallback: 'Add the CVE, workload, or package you want to inspect.' }, + { key: 'ui.search.question.findings.clarify_scope', fallback: 'Add the part of the finding that matters most right now.' }, ], 'clarify'), }; @@ -165,8 +165,8 @@ const VEX_SELF_SERVE: SearchSelfServeDefinition = { { key: 'ui.search.question.vex.conflicting_evidence', fallback: 'What evidence conflicts with this VEX?' }, ], 'page'), clarifyingQuestions: withQuestionKind([ - { key: 'ui.search.question.vex.clarify_statement', fallback: 'Which statement, component, or product range should I narrow this to?' }, - { key: 'ui.search.question.vex.clarify_need', fallback: 'Do you want exploitability meaning, coverage, or conflict evidence?' }, + { key: 'ui.search.question.vex.clarify_statement', fallback: 'Add the statement, component, or product range you want to inspect.' }, + { key: 'ui.search.question.vex.clarify_need', fallback: 'Add whether you need exploitability meaning, coverage, or conflict evidence.' }, ], 'clarify'), }; @@ -177,8 +177,8 @@ const POLICY_SELF_SERVE: SearchSelfServeDefinition = { { key: 'ui.search.question.policy.exception', fallback: 'What is the safest exception path?' }, ], 'page'), clarifyingQuestions: withQuestionKind([ - { key: 'ui.search.question.policy.clarify_target', fallback: 'Which rule, environment, or control should I narrow this to?' }, - { key: 'ui.search.question.policy.clarify_need', fallback: 'Do you want recent failures, exceptions, or promotion impact?' }, + { key: 'ui.search.question.policy.clarify_target', fallback: 'Add the rule, environment, or control you want to inspect.' }, + { key: 'ui.search.question.policy.clarify_need', fallback: 'Add whether you need recent failures, exceptions, or promotion impact.' }, ], 'clarify'), }; @@ -189,8 +189,8 @@ const DOCTOR_SELF_SERVE: SearchSelfServeDefinition = { { key: 'ui.search.question.doctor.changed', fallback: 'What changed before this health issue?' }, ], 'page'), clarifyingQuestions: withQuestionKind([ - { key: 'ui.search.question.doctor.clarify_check', fallback: 'Which check or symptom should I narrow this to?' }, - { key: 'ui.search.question.doctor.clarify_need', fallback: 'Do you want diagnosis, remediation, or verification steps?' }, + { key: 'ui.search.question.doctor.clarify_check', fallback: 'Add the check or symptom you want to inspect.' }, + { key: 'ui.search.question.doctor.clarify_need', fallback: 'Add whether you need diagnosis, remediation, or verification steps.' }, ], 'clarify'), }; @@ -201,8 +201,8 @@ const GRAPH_SELF_SERVE: SearchSelfServeDefinition = { { key: 'ui.search.question.graph.next_hop', fallback: 'What should I inspect next on this path?' }, ], 'page'), clarifyingQuestions: withQuestionKind([ - { key: 'ui.search.question.graph.clarify_node', fallback: 'Which node, package, or edge should I narrow this to?' }, - { key: 'ui.search.question.graph.clarify_need', fallback: 'Do you want reachability, impact, or next-step guidance?' }, + { key: 'ui.search.question.graph.clarify_node', fallback: 'Add the node, package, or edge you want to inspect.' }, + { key: 'ui.search.question.graph.clarify_need', fallback: 'Add whether you need reachability, impact, or next-step guidance.' }, ], 'clarify'), }; @@ -213,8 +213,8 @@ const OPS_MEMORY_SELF_SERVE: SearchSelfServeDefinition = { { key: 'ui.search.question.ops_memory.repeat', fallback: 'What repeated failures are related to this?' }, ], 'page'), clarifyingQuestions: withQuestionKind([ - { key: 'ui.search.question.ops_memory.clarify_job', fallback: 'Which job, incident, or recurring failure should I narrow this to?' }, - { key: 'ui.search.question.ops_memory.clarify_need', fallback: 'Do you want precedent, likely cause, or recommended recovery?' }, + { key: 'ui.search.question.ops_memory.clarify_job', fallback: 'Add the job, incident, or recurring failure you want to inspect.' }, + { key: 'ui.search.question.ops_memory.clarify_need', fallback: 'Add whether you need precedent, likely cause, or recommended recovery.' }, ], 'clarify'), }; @@ -225,8 +225,8 @@ const TIMELINE_SELF_SERVE: SearchSelfServeDefinition = { { key: 'ui.search.question.timeline.next_event', fallback: 'What else happened around this event?' }, ], 'page'), clarifyingQuestions: withQuestionKind([ - { key: 'ui.search.question.timeline.clarify_window', fallback: 'Which deployment, incident, or time window should I narrow this to?' }, - { key: 'ui.search.question.timeline.clarify_need', fallback: 'Do you want causes, impacts, or follow-up events?' }, + { key: 'ui.search.question.timeline.clarify_window', fallback: 'Add the deployment, incident, or time window you want to inspect.' }, + { key: 'ui.search.question.timeline.clarify_need', fallback: 'Add whether you need causes, impacts, or follow-up events.' }, ], 'clarify'), }; @@ -237,8 +237,8 @@ const RELEASES_SELF_SERVE: SearchSelfServeDefinition = { { key: 'ui.search.question.releases.next_step', fallback: 'What is the safest next step to ship?' }, ], 'page'), clarifyingQuestions: withQuestionKind([ - { key: 'ui.search.question.releases.clarify_target', fallback: 'Which environment or release should I narrow this to?' }, - { key: 'ui.search.question.releases.clarify_need', fallback: 'Do you want blockers, approvals, or policy impact?' }, + { key: 'ui.search.question.releases.clarify_target', fallback: 'Add the environment or release you want to inspect.' }, + { key: 'ui.search.question.releases.clarify_need', fallback: 'Add whether you need blockers, approvals, or policy impact.' }, ], 'clarify'), }; diff --git a/src/Web/StellaOps.Web/src/app/layout/global-search/global-search.component.ts b/src/Web/StellaOps.Web/src/app/layout/global-search/global-search.component.ts index 98b6d8aae..62952623a 100644 --- a/src/Web/StellaOps.Web/src/app/layout/global-search/global-search.component.ts +++ b/src/Web/StellaOps.Web/src/app/layout/global-search/global-search.component.ts @@ -78,6 +78,8 @@ type SearchAnswerView = { }>; questionLabel: string; questions: SearchQuestionView[]; + guidanceLabel: string | null; + guidance: SearchQuestionView[]; nextSearches: SearchSuggestionView[]; }; type SuccessfulSearchHistoryEntry = { @@ -202,6 +204,22 @@ type SuccessfulSearchHistoryEntry = { } + @if (answer.guidance.length > 0) { +