phase
- {strategySynthesis.phaseDecision.summary}
+ {selectedStrategyNarrative?.summary ?? strategySynthesis.phaseDecision.summary}
- {strategySynthesis.phaseDecision.allowedNow.slice(0, 4).map((action) => (
+ {(selectedStrategyNarrative?.doNow ?? strategySynthesis.phaseDecision.allowedNow).slice(0, 4).map((action) => (
now
{action}
@@ -9486,18 +9522,24 @@ export function App() {
Что нельзя выдавать как immediate route до зрелости сайта.
- {strategySynthesis.phaseDecision.prepareNext.slice(0, 3).map((action) => (
+ {(selectedStrategyNarrative?.prepareNext ?? strategySynthesis.phaseDecision.prepareNext).slice(0, 3).map((action) => (
prepare
{action}
))}
- {strategySynthesis.phaseDecision.hold.slice(0, 5).map((action) => (
+ {(selectedStrategyNarrative?.hold ?? strategySynthesis.phaseDecision.hold).slice(0, 5).map((action) => (
hold
{action}
))}
+ {(selectedStrategyNarrative?.nextEvidenceTasks ?? []).slice(0, 3).map((action) => (
+
+ evidence
+ {action}
+
+ ))}
diff --git a/seo_mode/seo_mode/app/src/api.ts b/seo_mode/seo_mode/app/src/api.ts
index 0a792d0..7f6d9d5 100644
--- a/seo_mode/seo_mode/app/src/api.ts
+++ b/seo_mode/seo_mode/app/src/api.ts
@@ -1318,6 +1318,36 @@ export type SeoStrategyContract = {
promotionSignals: string[];
risk: string;
}>;
+ phaseStrategies: Array<{
+ phase: SeoStrategyContract["siteMaturity"]["phase"];
+ title: string;
+ status: "current" | "future" | "locked" | "ready";
+ goal: string;
+ summary: string;
+ expectedImpact: "high" | "low" | "medium";
+ confidence: "high" | "low" | "medium";
+ keywordMix: Array<{
+ label: string;
+ description: string;
+ competitionTier: "head" | "long_tail" | "mid" | "unknown";
+ evidenceRefs: string[];
+ priority: "high" | "low" | "medium";
+ }>;
+ landingPlan: Array<{
+ title: string;
+ targetPath: string | null;
+ action: "defer" | "do_now" | "prepare";
+ reason: string;
+ evidenceRefs: string[];
+ priority: "high" | "low" | "medium";
+ }>;
+ doNow: string[];
+ prepareNext: string[];
+ hold: string[];
+ evidenceRefs: string[];
+ risks: string[];
+ nextEvidenceTasks: string[];
+ }>;
evidence: {
context: {
status: "missing" | "needs_review" | "ready";
@@ -1465,6 +1495,7 @@ export type StrategySynthesisContract = {
promotionSignals: string[];
phaseRoadmap: SeoStrategyContract["phasePlan"];
};
+ phaseStrategies: SeoStrategyContract["phaseStrategies"];
recommendedPath: {
id: string;
title: string;
diff --git a/seo_mode/seo_mode/server/src/strategy/seoStrategy.ts b/seo_mode/seo_mode/server/src/strategy/seoStrategy.ts
index a013f9d..e3f1f0a 100644
--- a/seo_mode/seo_mode/server/src/strategy/seoStrategy.ts
+++ b/seo_mode/seo_mode/server/src/strategy/seoStrategy.ts
@@ -73,6 +73,36 @@ export type SeoStrategyContract = {
promotionSignals: string[];
risk: string;
}>;
+ phaseStrategies: Array<{
+ phase: SeoMaturityPhase;
+ title: string;
+ status: "current" | "future" | "locked" | "ready";
+ goal: string;
+ summary: string;
+ expectedImpact: SeoStrategyPriority;
+ confidence: SeoStrategyConfidence;
+ keywordMix: Array<{
+ label: string;
+ description: string;
+ competitionTier: SeoCompetitionTier;
+ evidenceRefs: string[];
+ priority: SeoStrategyPriority;
+ }>;
+ landingPlan: Array<{
+ title: string;
+ targetPath: string | null;
+ action: SeoOpportunityAction;
+ reason: string;
+ evidenceRefs: string[];
+ priority: SeoStrategyPriority;
+ }>;
+ doNow: string[];
+ prepareNext: string[];
+ hold: string[];
+ evidenceRefs: string[];
+ risks: string[];
+ nextEvidenceTasks: string[];
+ }>;
evidence: {
context: {
status: "missing" | "needs_review" | "ready";
@@ -383,6 +413,264 @@ function buildPhasePlan(siteMaturity: SeoStrategyContract["siteMaturity"]): SeoS
});
}
+function getPhaseAllowedTiers(phase: SeoMaturityPhase): SeoCompetitionTier[] {
+ if (phase === "bootstrap_indexing") {
+ return ["long_tail", "unknown"];
+ }
+
+ if (phase === "traction") {
+ return ["long_tail", "mid", "unknown"];
+ }
+
+ if (phase === "growth") {
+ return ["long_tail", "mid", "head", "unknown"];
+ }
+
+ return ["head", "mid", "long_tail", "unknown"];
+}
+
+function getPhaseSummary(phase: SeoMaturityPhase, opportunities: SeoStrategyContract["opportunities"]) {
+ const phaseOpportunityCount = opportunities.length;
+
+ if (phase === "bootstrap_indexing") {
+ return `Стартовая стратегия для нового/нулевого сайта: закрепить понятный индексируемый смысл, выбрать low-competition фразы и не расходовать ресурс на head terms. Подходящих opportunity сейчас: ${phaseOpportunityCount}.`;
+ }
+
+ if (phase === "traction") {
+ return `Стратегия первых сигналов: расширять только те кластеры, где уже виден target-fit, индексируемость и первые признаки спроса. Подходящих opportunity сейчас: ${phaseOpportunityCount}.`;
+ }
+
+ if (phase === "growth") {
+ return `Стратегия роста: усиливать подтверждённые посадочные, добавлять support-контент и готовить конкурентные кластеры через SERP evidence. Подходящих opportunity сейчас: ${phaseOpportunityCount}.`;
+ }
+
+ return `Стратегия authority/head terms: атаковать широкие конкурентные запросы только после индексации, traction, external trust и performance signals. Подходящих opportunity сейчас: ${phaseOpportunityCount}.`;
+}
+
+function getPhaseDefaultTasks(
+ phase: SeoMaturityPhase,
+ missingEvidence: SeoStrategyContract["missingEvidence"]
+): Pick