Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5658bed520 | ||
|
|
7252296a74 | ||
|
|
348856a9c2 | ||
|
|
7779dc37d1 | ||
|
|
c1db571a39 | ||
|
|
5f65f3d570 | ||
|
|
bb18cb4f1c | ||
|
|
3f7b1f6e9c | ||
|
|
413a432e8c |
@@ -6,12 +6,16 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.cin.su/m/Assistant/adapters/postgres"
|
||||
secretcontracts "git.cin.su/m/Assistant/contracts/secrets"
|
||||
"git.cin.su/m/Assistant/contracts/work"
|
||||
"git.cin.su/m/Assistant/modules/work/application"
|
||||
"git.cin.su/m/Assistant/modules/work/ports"
|
||||
platformsecrets "git.cin.su/m/Assistant/platform/secrets"
|
||||
)
|
||||
|
||||
type memoryStore struct{ writes int }
|
||||
@@ -30,14 +34,24 @@ func main() {
|
||||
enabled := flag.Bool("enable-canary", false, "explicitly enable the manual canary")
|
||||
implementation := flag.String("implementation", "fake-worker", "allowlisted deterministic implementation")
|
||||
postgresMode := flag.Bool("postgres", false, "use the Work PostgreSQL repository instead of in-memory storage")
|
||||
runID := flag.String("run-id", "", "optional unique canary run suffix; generated when omitted")
|
||||
flag.Parse()
|
||||
|
||||
now := time.UnixMilli(1_700_000_000_000)
|
||||
intent := work.AgentDispatchIntent{DispatchID: "manual-dispatch-1", IdempotencyKey: "manual-idempotency-1", DelegationID: "manual-delegation-1", RunID: "manual-run-1", ImplementationID: *implementation, ImplementationRevision: 1, SourceRevision: "manual-source-1", WorktreeID: "manual-worktree-1", VerificationPlanRef: "manual-verification-1", CreatedAtUnixMillis: now.UnixMilli()}
|
||||
suffix := *runID
|
||||
if suffix == "" {
|
||||
suffix = fmt.Sprintf("%d", time.Now().UnixNano())
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
intent := work.AgentDispatchIntent{DispatchID: "manual-dispatch-" + suffix, IdempotencyKey: "manual-idempotency-" + suffix, DelegationID: work.DelegationID("manual-delegation-" + suffix), RunID: work.AgentRunID("manual-run-" + suffix), ImplementationID: *implementation, ImplementationRevision: 1, SourceRevision: "manual-source-" + suffix, WorktreeID: "manual-worktree-" + suffix, VerificationPlanRef: "manual-verification-" + suffix, CreatedAtUnixMillis: now.UnixMilli()}
|
||||
var store ports.ExecutionStore = &memoryStore{}
|
||||
var closeStore func()
|
||||
if *postgresMode {
|
||||
repository, err := postgres.OpenWork(context.Background(), postgres.ContinuityConfig{Host: os.Getenv("POSTGRES_HOST"), Port: os.Getenv("POSTGRES_PORT"), Database: os.Getenv("POSTGRES_DATABASE"), User: os.Getenv("WORK_POSTGRES_USER"), Password: os.Getenv("WORK_POSTGRES_PASSWORD")})
|
||||
password, err := resolveDatabasePassword()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
repository, err := postgres.OpenWork(context.Background(), postgres.ContinuityConfig{Host: os.Getenv("POSTGRES_HOST"), Port: os.Getenv("POSTGRES_PORT"), Database: os.Getenv("POSTGRES_DATABASE"), User: os.Getenv("WORK_POSTGRES_USER"), Password: password})
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
@@ -67,3 +81,27 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func resolveDatabasePassword() (string, error) {
|
||||
if _, ok := os.LookupEnv("WORK_POSTGRES_PASSWORD"); ok {
|
||||
return "", fmt.Errorf("WORK_POSTGRES_PASSWORD direct environment secret is forbidden")
|
||||
}
|
||||
path, ref := os.Getenv("WORK_POSTGRES_PASSWORD_FILE"), os.Getenv("WORK_POSTGRES_CREDENTIAL_REF")
|
||||
if path == "" || ref == "" {
|
||||
return "", fmt.Errorf("WORK_POSTGRES_PASSWORD_FILE and WORK_POSTGRES_CREDENTIAL_REF are required")
|
||||
}
|
||||
root := os.Getenv("SECRET_BACKEND_ROOT")
|
||||
if root == "" {
|
||||
root = "/run/secrets"
|
||||
}
|
||||
cleanRoot := filepath.Clean(root)
|
||||
cleanPath := filepath.Clean(path)
|
||||
if cleanPath != cleanRoot && !strings.HasPrefix(cleanPath, cleanRoot+string(os.PathSeparator)) {
|
||||
return "", fmt.Errorf("database password file escapes protected secret root")
|
||||
}
|
||||
resolver, err := platformsecrets.NewFileResolver(cleanRoot)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return resolver.ResolveFile(secretcontracts.SecretFileRef{CredentialRef: secretcontracts.CredentialRef(ref), Purpose: secretcontracts.SecretPurposeDatabasePassword, Consumer: secretcontracts.RuntimeConsumerWorkWorker, Path: cleanPath})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestResolveDatabasePasswordRejectsDirectEnvironmentSecret(t *testing.T) {
|
||||
t.Setenv("WORK_POSTGRES_PASSWORD", "literal-secret")
|
||||
if _, err := resolveDatabasePassword(); err == nil {
|
||||
t.Fatal("direct database password environment variable was accepted")
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveDatabasePasswordRejectsPathOutsideProtectedRoot(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
t.Setenv("SECRET_BACKEND_ROOT", root)
|
||||
t.Setenv("WORK_POSTGRES_PASSWORD_FILE", filepath.Join(root, "..", "outside"))
|
||||
t.Setenv("WORK_POSTGRES_CREDENTIAL_REF", "credential://domovoy/work/db-password/v1")
|
||||
if _, err := resolveDatabasePassword(); err == nil {
|
||||
t.Fatal("database password path outside protected root was accepted")
|
||||
}
|
||||
}
|
||||
@@ -99,8 +99,12 @@ services:
|
||||
POSTGRES_PORT: "5432"
|
||||
POSTGRES_DATABASE: ${POSTGRES_DB:-assistant}
|
||||
WORK_POSTGRES_USER: work_role
|
||||
WORK_POSTGRES_PASSWORD: ${WORK_DB_PASSWORD}
|
||||
WORK_POSTGRES_CREDENTIAL_REF: credential://domovoy/work/db-password/v1
|
||||
WORK_POSTGRES_PASSWORD_FILE: /run/secrets/work_db_password
|
||||
SECRET_BACKEND_ROOT: /run/secrets
|
||||
depends_on: { postgres: { condition: service_healthy } }
|
||||
volumes:
|
||||
- assistant_core_secrets:/run/secrets:ro
|
||||
|
||||
agent-worker:
|
||||
build: { context: .., dockerfile: deploy/images/runtime.Dockerfile, args: { TARGET: agent-worker, SOURCE_REVISION: "${ASSISTANT_SOURCE_REVISION:-unknown}" } }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"task_file": "dev/tasks/DEV-000191-cleanup-obsolete-audit-descriptions.json",
|
||||
"task_id": "DEV-000191",
|
||||
"task_file": "dev/tasks/DEV-000195-replayable-agent-canary-run-ids.json",
|
||||
"task_id": "DEV-000195",
|
||||
"task_revision": 1
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"task_id": "DEV-000192",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ADR-0018",
|
||||
"base_repo_revision": "413a432e8c661ff702779c5288049eaede948c1b",
|
||||
"risk_tier": "E",
|
||||
"intent": "Record live reconciliation evidence for the durable Learning reflection worker and update the audit direction without changing runtime behavior.",
|
||||
"requirement_ids": ["Learning reflection loop", "reflection backlog reconciliation", "live evidence provenance"],
|
||||
"allowed_change_scope": {"modules": [], "paths": ["docs/PROJECT_STATUS.md", "docs/evidence/reflection-runtime-reconciliation-2026-08-23-v1.json", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000192-reflection-live-reconciliation-evidence.json"]},
|
||||
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "modules/**", "cmd/**", "contracts/**", ".gitea/**"]},
|
||||
"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": ["reflection worker status contract", "epistemics reflection candidate state"], "may_change": ["audit status projection", "append-only live evidence record"]},
|
||||
"semantic_owner": "project-governance",
|
||||
"canonical_state": ["epistemics.reflection_candidates"],
|
||||
"state_transitions": ["open backlog assessment -> reconciled resolved state evidence"],
|
||||
"side_effects_and_privileges": ["documentation and evidence only; no database mutation"],
|
||||
"persistence_and_migrations": ["none"],
|
||||
"failure_retry_restart": ["worker no-op and durable state query are retained as evidence; no inferred candidate resolution"],
|
||||
"assumptions": ["the production worker's one-shot no-candidate result and the authoritative state query were captured from docker.cin.su"],
|
||||
"acceptance_scenarios": ["worker runs without a pending candidate", "database state reports 31 RESOLVED reflection candidates and no OPEN backlog", "status marks the reflection loop reconciled", "Agent Execution and UI gaps remain explicit"],
|
||||
"required_tests": ["go test ./...", "go vet ./...", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "go run ./cmd/devctl independent", "git diff --check", "CI", "production public health check"],
|
||||
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"task_id": "DEV-000193",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ADR-0018",
|
||||
"base_repo_revision": "bb18cb4f1c18aee7c22ddbe7259123f96f779b8c",
|
||||
"risk_tier": "E",
|
||||
"intent": "Record the current live model fleet degradation and truthful FAST-first fallback selection after the opportunistic HEAVY provider timeout.",
|
||||
"requirement_ids": ["model fleet/failover", "CPU-first current operation", "truthful degraded readiness"],
|
||||
"allowed_change_scope": {"modules": [], "paths": ["docs/PROJECT_STATUS.md", "docs/evidence/audit-runtime-status-2026-08-23-v1.json", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000193-fleet-live-degraded-evidence.json"]},
|
||||
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "modules/**", "cmd/**", "contracts/**", ".gitea/**"]},
|
||||
"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": ["live /v1/fleet/readiness response", "existing FleetReadiness and fallback policy"], "may_change": ["fleet evidence projection"]},
|
||||
"semantic_owner": "project-governance",
|
||||
"canonical_state": ["cognitive-worker fleet readiness projection"],
|
||||
"state_transitions": ["HEAVY ready -> timeout/unavailable", "preferred FAST remains selected", "LIFEBOAT remains fallback tier"],
|
||||
"side_effects_and_privileges": ["documentation and evidence only; no provider activation or deployment configuration mutation"],
|
||||
"persistence_and_migrations": ["none"],
|
||||
"failure_retry_restart": ["provider timeout remains degraded evidence; no unavailable model is claimed ready"],
|
||||
"assumptions": ["the live endpoint response was captured from docker.cin.su after the current production deployment"],
|
||||
"acceptance_scenarios": ["FAST/MEDIUM/LIFEBOAT are ready", "HEAVY timeout is explicit", "FAST is selected for CPU-first operation", "LIFEBOAT is the declared fallback", "Agent bridge and authenticated UI gaps remain unchanged"],
|
||||
"required_tests": ["go test ./...", "go vet ./...", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "go run ./cmd/devctl independent", "git diff --check", "CI", "production public health check"],
|
||||
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"task_id": "DEV-000194",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ADR-0018",
|
||||
"base_repo_revision": "c1db571a39514e65156123b7f9394645183cf2ab",
|
||||
"risk_tier": "E",
|
||||
"intent": "Move the explicit production Agent Execution dry-run canary from direct database secret environment input to protected file/CredentialRef storage and preserve durable lease/callback evidence.",
|
||||
"requirement_ids": ["Agent Execution runtime and recovery", "protected canary configuration", "durable lease/callback canary"],
|
||||
"allowed_change_scope": {"modules": ["agent-canary", "deployment"], "paths": ["cmd/agent-canary/main.go", "cmd/agent-canary/main_test.go", "deploy/docker-compose.prod.yml", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000194-agent-canary-protected-storage.json"]},
|
||||
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "modules/**", "contracts/**", ".gitea/**"]},
|
||||
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 1, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
|
||||
"contracts": {"consume": ["existing protected secret resolver", "existing ExecutionStore and dry-run canary contract"], "may_change": ["agent-canary runtime configuration"]},
|
||||
"semantic_owner": "work-agent-execution",
|
||||
"canonical_state": ["work.agent_dispatch_intents", "work.agent_leases", "work.agent_callback_heads"],
|
||||
"state_transitions": ["explicit canary -> durable dispatch intent -> lease -> heartbeat callback -> result callback"],
|
||||
"side_effects_and_privileges": ["manual opt-in dry-run only; protected database credential read; no external provider or source mutation"],
|
||||
"persistence_and_migrations": ["none"],
|
||||
"failure_retry_restart": ["missing/invalid protected secret fails before database open; direct secret env is rejected; canary remains explicit opt-in"],
|
||||
"assumptions": ["the production compose secret volume already contains the work database credential reference used by agent-worker"],
|
||||
"acceptance_scenarios": ["direct password environment is rejected", "path outside protected root is rejected", "compose renders canary with protected file/ref only", "production opt-in canary writes lease and callback evidence", "normal agent-worker remains recovery-only without an external bridge"],
|
||||
"required_tests": ["go test ./...", "go vet ./...", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "go run ./cmd/devctl independent", "git diff --check", "CI", "production canary and public health check"],
|
||||
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"task_id": "DEV-000195",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ADR-0018",
|
||||
"base_repo_revision": "7252296a74ab7ff236fae865d63692e0fc6a777d",
|
||||
"risk_tier": "E",
|
||||
"intent": "Make the explicit Agent Execution dry-run canary replayable by generating unique dispatch, lease and callback identities per invocation while retaining durable evidence.",
|
||||
"requirement_ids": ["Agent Execution runtime and recovery", "replayable durable canary", "lease/callback evidence"],
|
||||
"allowed_change_scope": {"modules": ["agent-canary"], "paths": ["cmd/agent-canary/main.go", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000195-replayable-agent-canary-run-ids.json"]},
|
||||
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "modules/**", "contracts/**", ".gitea/**"]},
|
||||
"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 canary dry-run contract", "existing durable Work execution records"], "may_change": ["canary identity generation"]},
|
||||
"semantic_owner": "work-agent-execution",
|
||||
"canonical_state": ["work.agent_dispatch_intents", "work.agent_leases", "work.agent_callback_heads"],
|
||||
"state_transitions": ["each explicit canary invocation creates a fresh dispatch identity and durable lease/callback chain"],
|
||||
"side_effects_and_privileges": ["manual dry-run only; no external execution or source mutation"],
|
||||
"persistence_and_migrations": ["none"],
|
||||
"failure_retry_restart": ["repeated invocation does not reuse prior canary identity; lease conflicts remain explicit errors"],
|
||||
"assumptions": ["the protected-storage canary configuration from DEV-000194 is already in main"],
|
||||
"acceptance_scenarios": ["default invocation generates a unique suffix", "operator-supplied run-id remains attributable", "two production canary invocations persist independent durable records"],
|
||||
"required_tests": ["go test ./...", "go vet ./...", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "go run ./cmd/devctl independent", "git diff --check", "CI", "production canary and public health check"],
|
||||
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
|
||||
}
|
||||
@@ -7,9 +7,9 @@
|
||||
- `MEDIUM_PROVIDER_STATUS = LIVE_AVAILABLE_UNQUALIFIED`; bounded qualification timed out, so no activation was attempted. Evidence: `docs/evidence/wp-200-medium-qualification-2026-08-22-v3.json`.
|
||||
- `HEAVY_PROVIDER_STATUS = LIVE_QUALIFIED_UNACTIVATED`; the qualified GPU binding remains inactive. Evidence: `docs/evidence/wp-200-heavy-qualification-2026-08-22-v2.json`.
|
||||
- `AGENT_EXECUTION_STATUS = RECOVERY_ONLY`; no independently operated bridge endpoint is configured. Durable recovery and status endpoints remain active.
|
||||
- `FLEET_EXECUTION_EVIDENCE = docs/evidence/audit-runtime-status-2026-08-23-v1.json`; FAST, MEDIUM, HEAVY and LIFEBOAT are readiness-ready, preferred and selected tier is FAST, and LIFEBOAT is the fallback tier.
|
||||
- `FLEET_EXECUTION_EVIDENCE = docs/evidence/audit-runtime-status-2026-08-23-v1.json`; FAST, MEDIUM and LIFEBOAT are ready, HEAVY is explicitly degraded by provider timeout, FAST remains preferred/selected, and LIFEBOAT is the fallback tier.
|
||||
- `TRUTH_RESEARCH_STATUS = LIVE_CANARY_PERSISTED`; the web research canary preserved an OBSERVED/COMPLETE/NO-CONFLICT source chain while leaving the claim UNVERIFIED and belief PROVISIONAL. Evidence: `docs/evidence/truth-runtime-canary-2026-08-23-v1.json`.
|
||||
- `LEARNING_REFLECTION_STATUS = RUNNING_FOUNDATION`; reflection worker is running, but historical candidate backlog closure is not claimed.
|
||||
- `LEARNING_REFLECTION_STATUS = LIVE_RECONCILED`; reflection worker returned no pending candidate and the durable state contains 31 RESOLVED candidates with no OPEN backlog. Evidence: `docs/evidence/reflection-runtime-reconciliation-2026-08-23-v1.json`.
|
||||
- `BACKUP_RESTORE_STATUS = VERIFIED`; latest checksum and isolated restore evidence is retained in `docs/evidence/wp-002-postgres-restore-2026-08-22-v1.json`.
|
||||
- `GOVERNED_SELF_DEVELOPMENT_STATUS = PROPOSAL_AND_SCAFFOLD_ONLY`; no bridge-backed source mutation, promotion or rollback is claimed.
|
||||
- `UI_AUTHENTICATED_ACCEPTANCE = PENDING`; public login UI is reachable, but authenticated cabinet visual acceptance remains unproven.
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"reflection_worker": "running",
|
||||
"agent_worker": {"ready": false, "mode": "recovery-only", "executor_configured": false, "reason": "protected agent execution bridge URL is not configured"}
|
||||
},
|
||||
"fleet": {"ready_tiers": ["FAST", "MEDIUM", "HEAVY", "LIFEBOAT"], "preferred_tier": "FAST", "selected_tier": "FAST", "fallback_tier": "LIFEBOAT"},
|
||||
"fleet": {"ready_tiers": ["FAST", "MEDIUM", "LIFEBOAT"], "preferred_tier": "FAST", "selected_tier": "FAST", "fallback_tier": "LIFEBOAT", "heavy": {"ready": false, "reason": "provider /v1/models request timed out"}},
|
||||
"work_graph_canary": {"endpoint": "internal work-graph-runtime /v1/work-graphs/synthesis", "result": "HTTP 200; candidates, conflict and VERIFIED synthesis persisted"},
|
||||
"interpretation": "Readiness and canary evidence prove runtime behavior only; they do not prove external Agent Execution bridge availability or authenticated UI acceptance."
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"captured_at": "2026-08-23",
|
||||
"runtime": "reflection-worker",
|
||||
"one_shot_result": "no reflection candidate",
|
||||
"durable_state": {"reflection_candidates": {"RESOLVED": 31, "OPEN": 0, "other": 0}},
|
||||
"interpretation": "The durable reflection candidate backlog is reconciled. This proves the current worker/state loop, not autonomous source mutation or skill promotion."
|
||||
}
|
||||
Reference in New Issue
Block a user