diff --git a/cmd/creator-web/chat_test.go b/cmd/creator-web/chat_test.go
index 3bd0a9e..5a41187 100644
--- a/cmd/creator-web/chat_test.go
+++ b/cmd/creator-web/chat_test.go
@@ -25,7 +25,7 @@ func TestChatPageServedWithoutInternalIDs(t *testing.T) {
t.Fatalf("expected /chat status 200, got %d", recorder.Code)
}
body := recorder.Body.String()
- for _, required := range []string{"cognitive-status", "aria-live=\"polite\""} {
+ for _, required := range []string{"cognitive-status", "aria-live=\"polite\"", "cognitive-console-link", "href=\"/admin\""} {
if !strings.Contains(body, required) {
t.Fatalf("/chat is missing human status marker %q", required)
}
diff --git a/cmd/creator-web/static/assets/chat.css b/cmd/creator-web/static/assets/chat.css
index 4a83344..a1ec7a6 100644
--- a/cmd/creator-web/static/assets/chat.css
+++ b/cmd/creator-web/static/assets/chat.css
@@ -35,11 +35,13 @@ textarea { resize: vertical; min-height: 84px; max-height: 240px; padding: 14px;
.chat-header span { color: var(--muted); font: 10px/1 var(--mono); letter-spacing: .1em; text-transform: uppercase; }
#active-focus { margin-left: auto; color: var(--good); }
.cognitive-status { display: inline-flex; align-items: center; gap: 7px; white-space: nowrap; }
+.chat-header .cognitive-status { width: auto; height: auto; padding: 0; border: 0; background: transparent; color: var(--muted); font: 10px/1 var(--mono); letter-spacing: .1em; text-transform: uppercase; }
.cognitive-status::before { content: ""; width: 7px; height: 7px; flex: 0 0 7px; border-radius: 50%; background: var(--signal); }
.cognitive-status[data-state="available"] { color: var(--good); }
.cognitive-status[data-state="available"]::before { background: var(--good); }
.cognitive-status[data-state="unavailable"] { color: var(--bad); }
.cognitive-status[data-state="unavailable"]::before { background: var(--bad); }
+.cognitive-console-link { color: var(--signal); font: 10px/1 var(--mono); letter-spacing: .08em; text-transform: uppercase; white-space: nowrap; }
.chat-header button { width: 38px; height: 38px; border: 1px solid var(--line); background: transparent; }
.human-nav { display: flex; align-items: center; gap: 8px; }
.nav-tab, .operator-link { height: 34px; padding: 0 12px; display: inline-flex; align-items: center; border: 1px solid var(--line); color: var(--muted); background: #0b110e; text-decoration: none; font: 11px/1 var(--mono); text-transform: uppercase; letter-spacing: .08em; }
diff --git a/cmd/creator-web/static/assets/chat.js b/cmd/creator-web/static/assets/chat.js
index f5613d1..77638a5 100644
--- a/cmd/creator-web/static/assets/chat.js
+++ b/cmd/creator-web/static/assets/chat.js
@@ -9,6 +9,7 @@ const sendButton = document.querySelector("#send");
const logoutButton = document.querySelector("#logout");
const activeFocus = document.querySelector("#active-focus");
const cognitiveStatus = document.querySelector("#cognitive-status");
+const cognitiveConsoleLink = document.querySelector("#cognitive-console-link");
const todayList = document.querySelector("#today-list");
const refreshToday = document.querySelector("#refresh-today");
const historyList = document.querySelector("#history-list");
@@ -32,10 +33,14 @@ function renderCognitiveStatus(state) {
};
cognitiveStatus.dataset.state = displayState;
cognitiveStatus.textContent = labels[displayState];
+ cognitiveStatus.title = displayState === "available" ? "Канал доступен. Нажмите, чтобы проверить ещё раз" : "Проверить канал ещё раз";
+ if (cognitiveConsoleLink) cognitiveConsoleLink.hidden = displayState === "available";
}
async function loadCognitiveStatus() {
if (!cognitiveStatus || !token) return;
+ cognitiveStatus.dataset.state = "checking";
+ cognitiveStatus.textContent = "проверяю канал мышления";
try {
const result = await api("/api/capabilities/cognitive-proposal/health", {method: "GET"});
renderCognitiveStatus(result.State || result.state || "UNKNOWN");
@@ -277,6 +282,7 @@ for (const tab of navTabs) {
}
if (refreshToday) refreshToday.addEventListener("click", loadToday);
+if (cognitiveStatus) cognitiveStatus.addEventListener("click", loadCognitiveStatus);
logoutButton.addEventListener("click", () => {
token = "";
diff --git a/cmd/creator-web/static/chat.html b/cmd/creator-web/static/chat.html
index 4457ba7..45d678c 100644
--- a/cmd/creator-web/static/chat.html
+++ b/cmd/creator-web/static/chat.html
@@ -33,7 +33,8 @@
Консоль
фокус: не выбран
- проверяю канал мышления
+
+ настроить в консоли
сессия активна
diff --git a/dev/ACTIVE_TASK.json b/dev/ACTIVE_TASK.json
index c5c79de..8d5b7d2 100644
--- a/dev/ACTIVE_TASK.json
+++ b/dev/ACTIVE_TASK.json
@@ -1,5 +1,5 @@
{
- "task_file": "dev/tasks/DEV-000076-creator-chat-cognitive-status.json",
- "task_id": "DEV-000076",
+ "task_file": "dev/tasks/DEV-000077-creator-chat-cognitive-bridge.json",
+ "task_id": "DEV-000077",
"task_revision": 1
}
diff --git a/dev/tasks/DEV-000077-creator-chat-cognitive-bridge.json b/dev/tasks/DEV-000077-creator-chat-cognitive-bridge.json
new file mode 100644
index 0000000..b307de6
--- /dev/null
+++ b/dev/tasks/DEV-000077-creator-chat-cognitive-bridge.json
@@ -0,0 +1,47 @@
+{
+ "task_id": "DEV-000077",
+ "task_revision": 1,
+ "architecture_revision": "DEV-000073-baseline",
+ "base_repo_revision": "75abfb204bb427a0500a8c37440c374576c2153c",
+ "risk_tier": "E",
+ "intent": "Make model configuration changes understandable from the human chat while keeping configuration mutation in the separate operator console.",
+ "requirement_ids": [
+ "User can manually re-check the cognitive channel from chat",
+ "Chat clearly directs configuration changes to the operator console",
+ "No configuration mutation is performed by the browser chat",
+ "The conversation remains free of technical runtime identifiers"
+ ],
+ "allowed_change_scope": {
+ "modules": ["cmd/creator-web"],
+ "paths": [
+ "cmd/creator-web/chat_test.go",
+ "cmd/creator-web/static/chat.html",
+ "cmd/creator-web/static/assets/chat.css",
+ "cmd/creator-web/static/assets/chat.js",
+ "dev/ACTIVE_TASK.json",
+ "dev/tasks/DEV-000077-creator-chat-cognitive-bridge.json"
+ ]
+ },
+ "forbidden_change_scope": {
+ "paths": ["architecture/**", "security/**", "database/**", "modules/**", "contracts/**", "adapters/**", "deploy/**"]
+ },
+ "allowed_architecture_delta": {
+ "new_cross_module_dependencies": 0,
+ "new_canonical_owners": 0,
+ "new_privileges": 0,
+ "new_third_party_dependencies": 0,
+ "new_provider_adapters": 0,
+ "new_database_owner_roles": 0
+ },
+ "contracts": {"consume": ["existing authenticated /api/capabilities/cognitive-proposal/health endpoint", "existing /admin operator surface"], "may_change": []},
+ "semantic_owner": "none-interface-adapter",
+ "canonical_state": [],
+ "state_transitions": ["User requests a fresh read-only health observation", "Browser presents console handoff when the channel is not ready"],
+ "side_effects_and_privileges": ["Read-only observation only", "No new privilege", "No provider/model routing change", "No chat mutation from status controls"],
+ "persistence_and_migrations": ["No persistence or migration"],
+ "failure_retry_restart": ["Manual check uses the same safe health route", "Polling remains the retry mechanism"],
+ "assumptions": ["The operator console remains the authorized place for runtime configuration changes", "The existing health route is sufficient to observe readiness"],
+ "acceptance_scenarios": ["Clicking status requests a fresh health observation", "Unavailable status exposes an operator-console handoff", "Available status hides the handoff", "Chat HTML remains free of internal identifiers"],
+ "required_tests": ["go test ./cmd/creator-web", "go test ./...", "go vet ./...", "devctl preflight", "devctl check-diff", "devctl check-architecture", "git diff --check", "node --check cmd/creator-web/static/assets/chat.js"],
+ "terminal_gap_states": ["BLOCKED_BY_ARCHITECTURE_GAP", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_CONFLICT"]
+}