phase 1 review fixes: dedup edges, real export, validation, tests
Review of aa2ca0a found and fixed:
- re-ingest duplicated ALL edges (upsert conflicted on valid_from=now(),
never fired) — migration 007 dedupes + partial unique index on current
edges; upsert now targets it. Regression-tested.
- export was a stub that overwrote seeds/*.yaml with 11-byte "version: 1"
files — implemented real deterministic export (ontology/inventory/policy,
cognition-layer excluded); round-trip is byte-stable (tested)
- DB password leaked in startup logs (slog JSON bypasses String()) —
Config now implements slog.LogValuer; regression-tested
- docker-compose had literal '***' as DB password — env-interpolated
- uuid.New() (v4) → uuid.NewV7() per ADR-0005
- no ontology validation on ingest — internal/ontology TypeTree: abstract
instantiation rejected, relationship endpoints hierarchy-validated,
cardinality enforced in-transaction, lifecycle states checked, default
state applied (Phase 1 gate items, R3-1)
- getOrCreateEntityID swallowed non-ErrNoRows errors
- migration runner now holds a session advisory lock on one connection
- Makefile: hardcoded /opt/homebrew/bin/go → go; test-db target
Tests: 4 unit suites + 7 integration tests (env-guarded, throwaway DB per
run): migrate idempotent, seed idempotent + no dup edges, abstract/edge/
cardinality rejection, blast_radius cycle termination, export round-trip.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
@@ -64,18 +65,41 @@ func FromEnv() Config {
|
||||
return c
|
||||
}
|
||||
|
||||
// String returns a human-safe representation (secrets redacted).
|
||||
func (c Config) String() string {
|
||||
// redactedDBURL masks credentials in a postgres:// URL.
|
||||
func (c Config) redactedDBURL() string {
|
||||
dbURL := c.DatabaseURL
|
||||
if i := strings.Index(dbURL, "@"); i >= 0 {
|
||||
if j := strings.Index(dbURL, "://"); j >= 0 && j < i {
|
||||
dbURL = dbURL[:j+3] + "***" + dbURL[i:]
|
||||
}
|
||||
}
|
||||
return dbURL
|
||||
}
|
||||
|
||||
// String returns a human-safe representation (secrets redacted).
|
||||
func (c Config) String() string {
|
||||
token := ""
|
||||
if c.MCPBearerToken != "" {
|
||||
token = "***"
|
||||
}
|
||||
return fmt.Sprintf("Config{DB=%s Listen=%s Env=%s Debug=%v MCPToken=%s SeedsDir=%s}",
|
||||
dbURL, c.APIListen, c.APIEnv, c.Debug, token, c.SeedsDir)
|
||||
c.redactedDBURL(), c.APIListen, c.APIEnv, c.Debug, token, c.SeedsDir)
|
||||
}
|
||||
|
||||
// LogValue implements slog.LogValuer so structured handlers (JSON) never
|
||||
// serialize raw secrets — without this, slog marshals struct fields
|
||||
// directly and String() is bypassed.
|
||||
func (c Config) LogValue() slog.Value {
|
||||
token := ""
|
||||
if c.MCPBearerToken != "" {
|
||||
token = "***"
|
||||
}
|
||||
return slog.GroupValue(
|
||||
slog.String("db", c.redactedDBURL()),
|
||||
slog.String("listen", c.APIListen),
|
||||
slog.String("env", c.APIEnv),
|
||||
slog.Bool("debug", c.Debug),
|
||||
slog.String("mcp_token", token),
|
||||
slog.String("seeds_dir", c.SeedsDir),
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user