diff --git a/cmd/creator-web/chat_test.go b/cmd/creator-web/chat_test.go index 564805e..f4a423c 100644 --- a/cmd/creator-web/chat_test.go +++ b/cmd/creator-web/chat_test.go @@ -15,6 +15,8 @@ import ( ) func TestChatPageServedWithoutInternalIDs(t *testing.T) { + t.Setenv("ASSISTANT_BUILD_REVISION", "0123456789abcdef") + t.Setenv("ASSISTANT_BUILD_TIME", "2026-08-22T03:04:05Z") handler, err := newHandler("http://core", "http://continuity", "http://commit", http.DefaultClient, "http://cognitive") if err != nil { t.Fatal(err) @@ -30,11 +32,21 @@ func TestChatPageServedWithoutInternalIDs(t *testing.T) { t.Fatalf("/chat is missing human status marker %q", required) } } - for _, forbidden := range []string{"provider_id", "model_id", "binding_id", "workspace-id", "session-id"} { + for _, forbidden := range []string{"provider_id", "model_id", "binding_id", "workspace-id", "session-id", "__ASSISTANT_BUILD_REVISION__", "__ASSISTANT_BUILD_TIME__", "__ASSISTANT_BUILD_DATETIME__"} { if strings.Contains(body, forbidden) { t.Fatalf("/chat leaks internal field %q", forbidden) } } + if !strings.Contains(body, "версия 0123456789ab") || !strings.Contains(body, "обновлено 22.08.2026 03:04 UTC") || !strings.Contains(body, "datetime=\"2026-08-22T03:04:05Z\"") { + t.Fatalf("/chat does not render build metadata: %q", body) + } + for _, path := range []string{"/", "/chat.html"} { + alternate := httptest.NewRecorder() + handler.ServeHTTP(alternate, httptest.NewRequest(http.MethodGet, path, nil)) + if alternate.Code != http.StatusOK || strings.Contains(alternate.Body.String(), "__ASSISTANT_BUILD_REVISION__") { + t.Fatalf("%s did not render build metadata: status=%d", path, alternate.Code) + } + } } func TestChatScriptExplainsSessionAndRuntimeFailures(t *testing.T) { diff --git a/cmd/creator-web/server.go b/cmd/creator-web/server.go index 493bcb7..d419abf 100644 --- a/cmd/creator-web/server.go +++ b/cmd/creator-web/server.go @@ -133,7 +133,7 @@ func (g *gateway) ServeHTTP(w http.ResponseWriter, r *http.Request) { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } - if r.URL.Path == "/" { + if r.URL.Path == "/" || r.URL.Path == "/chat" || r.URL.Path == "/chat.html" { g.serveEmbeddedHTML(w, "chat.html") return } @@ -141,11 +141,6 @@ func (g *gateway) ServeHTTP(w http.ResponseWriter, r *http.Request) { g.serveEmbeddedHTML(w, "index.html") return } - if r.URL.Path == "/chat" { - r = r.Clone(r.Context()) - r.URL.Path = "/chat.html" - w.Header().Set("Cache-Control", "no-store") - } g.static.ServeHTTP(w, r) } diff --git a/dev/ACTIVE_TASK.json b/dev/ACTIVE_TASK.json index f97dbad..7d4294a 100644 --- a/dev/ACTIVE_TASK.json +++ b/dev/ACTIVE_TASK.json @@ -1,5 +1,5 @@ { - "task_file": "dev/tasks/DEV-000094-semantic-local-recovery-routing.json", - "task_id": "DEV-000094", + "task_file": "dev/tasks/DEV-000095-chat-build-metadata-rendering.json", + "task_id": "DEV-000095", "task_revision": 1 } diff --git a/dev/tasks/DEV-000095-chat-build-metadata-rendering.json b/dev/tasks/DEV-000095-chat-build-metadata-rendering.json new file mode 100644 index 0000000..9de3d7d --- /dev/null +++ b/dev/tasks/DEV-000095-chat-build-metadata-rendering.json @@ -0,0 +1,26 @@ +{ + "task_id": "DEV-000095", + "task_revision": 1, + "architecture_revision": "ADR-0018", + "base_repo_revision": "8c2989f", + "risk_tier": "E", + "intent": "Render actual Domovoy build revision and update time on every public chat route instead of exposing unresolved template markers.", + "requirement_ids": ["Chat displays version and update time", "Public build metadata is truthful"], + "allowed_change_scope": { + "modules": ["creator-web", "development-governance"], + "paths": ["cmd/creator-web/server.go", "cmd/creator-web/chat_test.go", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000095-chat-build-metadata-rendering.json"] + }, + "forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "contracts/**"]}, + "allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0}, + "contracts": {"consume": ["existing Creator Web HTTP surface"], "may_change": []}, + "semantic_owner": "none; presentation-only build metadata", + "canonical_state": [], + "state_transitions": [], + "side_effects_and_privileges": ["No mutation, provider call, credential access, or new privilege"], + "persistence_and_migrations": ["None"], + "failure_retry_restart": ["When build metadata environment values are absent, a safe development revision and startup timestamp remain renderable; no raw markers are sent"], + "assumptions": ["The existing container revision environment value is the deployment revision", "docker-test remains prohibited"], + "acceptance_scenarios": ["/chat renders revision and time instead of placeholders", "/chat.html cannot bypass metadata substitution", "public page does not expose internal IDs or unresolved build placeholders"], + "required_tests": ["go test ./cmd/creator-web", "go test ./...", "go vet ./...", "devctl preflight", "devctl check-diff", "git diff --check"], + "terminal_gap_states": ["BLOCKED_BY_ARCHITECTURE_GAP", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_CONFLICT"] +}