Implement governance sentinel v2 #97

Merged
m merged 33 commits from codex/wp-301-canary-repository into main 2026-08-22 10:15:11 +00:00
89 changed files with 3055 additions and 25 deletions
+4
View File
@@ -15,6 +15,8 @@ jobs:
go-version: '1.26.6'
- name: Active task preflight
run: go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json
- name: Independent governance verifier
run: go run ./cmd/devctl/independent
- name: Exact diff scope
run: go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json
- name: Format check
@@ -25,3 +27,5 @@ jobs:
run: go test ./...
- name: Vet
run: go vet ./...
- name: Diff whitespace check
run: git diff --check
+63
View File
@@ -2,7 +2,9 @@ package postgres
import (
"context"
"errors"
"fmt"
"time"
"git.cin.su/m/Assistant/contracts/work"
"github.com/jackc/pgx/v5/pgxpool"
@@ -10,6 +12,12 @@ import (
type WorkRepository struct{ pool *pgxpool.Pool }
var (
ErrDispatchIntentConflict = errors.New("dispatch intent idempotency conflict")
ErrAgentLeaseUnavailable = errors.New("agent lease unavailable")
ErrAgentCallbackRejected = errors.New("agent callback rejected")
)
func OpenWork(ctx context.Context, config ContinuityConfig) (*WorkRepository, error) {
pool, err := openPool(ctx, config)
if err != nil {
@@ -39,6 +47,61 @@ func (r *WorkRepository) Load(ctx context.Context, id work.TaskID) (work.TaskTar
return target, nil
}
func (r *WorkRepository) SaveDispatchIntent(ctx context.Context, intent work.AgentDispatchIntent) error {
tx, err := r.pool.Begin(ctx)
if err != nil {
return err
}
defer tx.Rollback(ctx)
_, err = tx.Exec(ctx, `INSERT INTO work.agent_dispatch_intents(dispatch_id,idempotency_key,delegation_id,run_id,implementation_id,implementation_revision,source_revision,worktree_id,verification_plan_ref,created_at_unix_millis) VALUES($1,$2,$3,$4,$5,$6,$7,$8,$9,$10) ON CONFLICT(idempotency_key) DO NOTHING`, intent.DispatchID, intent.IdempotencyKey, intent.DelegationID, intent.RunID, intent.ImplementationID, intent.ImplementationRevision, intent.SourceRevision, intent.WorktreeID, intent.VerificationPlanRef, intent.CreatedAtUnixMillis)
if err != nil {
return fmt.Errorf("save dispatch intent: %w", err)
}
var existing work.AgentDispatchIntent
err = tx.QueryRow(ctx, `SELECT dispatch_id,idempotency_key,delegation_id,run_id,implementation_id,implementation_revision,source_revision,worktree_id,verification_plan_ref,created_at_unix_millis FROM work.agent_dispatch_intents WHERE idempotency_key=$1`, intent.IdempotencyKey).Scan(&existing.DispatchID, &existing.IdempotencyKey, &existing.DelegationID, &existing.RunID, &existing.ImplementationID, &existing.ImplementationRevision, &existing.SourceRevision, &existing.WorktreeID, &existing.VerificationPlanRef, &existing.CreatedAtUnixMillis)
if err != nil {
return fmt.Errorf("read dispatch intent: %w", err)
}
if existing != intent {
return ErrDispatchIntentConflict
}
return tx.Commit(ctx)
}
func (r *WorkRepository) SaveAgentLease(ctx context.Context, lease work.AgentLease) error {
err := r.pool.QueryRow(ctx, `INSERT INTO work.agent_leases(lease_id,run_id,worker_id,fencing_token,state,acquired_at_unix_millis,expires_at_unix_millis) VALUES($1,$2,$3,$4,$5,$6,$7) ON CONFLICT(run_id) DO UPDATE SET lease_id=EXCLUDED.lease_id,worker_id=EXCLUDED.worker_id,fencing_token=EXCLUDED.fencing_token,state=EXCLUDED.state,acquired_at_unix_millis=EXCLUDED.acquired_at_unix_millis,expires_at_unix_millis=EXCLUDED.expires_at_unix_millis WHERE work.agent_leases.state='EXPIRED' OR work.agent_leases.expires_at_unix_millis <= EXCLUDED.acquired_at_unix_millis RETURNING run_id`, lease.ID, lease.RunID, lease.WorkerID, lease.FencingToken, lease.State, lease.AcquiredAt.UnixMilli(), lease.ExpiresAt.UnixMilli()).Scan(new(string))
if err != nil {
return ErrAgentLeaseUnavailable
}
return nil
}
func (r *WorkRepository) RenewAgentLease(ctx context.Context, lease work.AgentLease, expiresAt time.Time) error {
command, err := r.pool.Exec(ctx, `UPDATE work.agent_leases SET expires_at_unix_millis=$1 WHERE run_id=$2 AND worker_id=$3 AND fencing_token=$4 AND state='ACTIVE' AND expires_at_unix_millis > $5`, expiresAt.UnixMilli(), lease.RunID, lease.WorkerID, lease.FencingToken, time.Now().UnixMilli())
if err != nil || command.RowsAffected() != 1 {
return ErrAgentLeaseUnavailable
}
return nil
}
func (r *WorkRepository) AcceptAgentCallback(ctx context.Context, callback work.AgentCallback) error {
tx, err := r.pool.Begin(ctx)
if err != nil {
return err
}
defer tx.Rollback(ctx)
var runID string
err = tx.QueryRow(ctx, `SELECT run_id FROM work.agent_leases WHERE run_id=$1 AND worker_id=$2 AND fencing_token=$3 AND state='ACTIVE' AND expires_at_unix_millis > $4 FOR UPDATE`, callback.RunID, callback.WorkerID, callback.FencingToken, callback.ObservedAt.UnixMilli()).Scan(&runID)
if err != nil {
return ErrAgentCallbackRejected
}
command, err := tx.Exec(ctx, `INSERT INTO work.agent_callback_heads(run_id,last_sequence,updated_at_unix_millis) VALUES($1,$2,$3) ON CONFLICT(run_id) DO UPDATE SET last_sequence=EXCLUDED.last_sequence,updated_at_unix_millis=EXCLUDED.updated_at_unix_millis WHERE work.agent_callback_heads.last_sequence < EXCLUDED.last_sequence`, runID, callback.Sequence, callback.ObservedAt.UnixMilli())
if err != nil || command.RowsAffected() != 1 {
return ErrAgentCallbackRejected
}
return tx.Commit(ctx)
}
func (r *WorkRepository) SavePlanningResult(ctx context.Context, result work.PlanningResult) error {
tx, err := r.pool.Begin(ctx)
if err != nil {
+6 -1
View File
@@ -65,7 +65,12 @@
"WorkPlan",
"PlanStep",
"CapabilityRequirement",
"DelegationCandidate"
"DelegationCandidate",
"Delegation",
"AgentRun",
"AgentCheckpoint",
"AgentResult",
"DelegationVerification"
],
"database_schema": "work",
"boundary_class": "remote_capable",
+12 -6
View File
@@ -25,17 +25,17 @@ modules:
situation:
id: mod.situation
owns: [Situation, FocusBinding]
owns: [Situation, FocusBinding, ProjectRegistry, ActiveProjectFocus]
database_schema: situation
boundary_class: remote_capable
may_depend_on: [contracts.identity, contracts.work, contracts.capability]
may_depend_on: [contracts.identity, contracts.authority, contracts.work, contracts.capability]
work:
id: mod.work
owns: [Goal, Task, Decision]
owns: [Goal, HelpPath, GoalLifecycle, Task, Decision, PlanCandidate, WorkPlan, PlanStep, CapabilityRequirement, DelegationCandidate, Delegation, AgentRun, AgentCheckpoint, AgentResult, DelegationVerification]
database_schema: work
boundary_class: remote_capable
may_depend_on: [contracts.identity, contracts.authority, platform.events]
may_depend_on: [contracts.identity, contracts.authority, contracts.epistemics, contracts.capability, platform.ids, platform.events]
epistemics:
id: mod.epistemics
@@ -53,7 +53,7 @@ modules:
capability:
id: mod.capability
owns: [Capability, CapabilityBinding, CapabilityHealth]
owns: [Capability, CapabilityBinding, CapabilityHealth, CapabilityImplementation, CapabilityResolution, CapabilitySelection, CapabilityGap, AcquisitionStrategyCandidate, CapabilityUsageObservation, SkillProfile, SkillEvidence, FastPathDecision]
database_schema: capability
boundary_class: remote_capable
may_depend_on: [contracts.identity, contracts.authority, contracts.artifact]
@@ -81,11 +81,12 @@ platform:
- platform.inbox
- platform.telemetry
- platform.errors
- platform.secrets
rule: "platform owns mechanisms, never domain meaning"
runtime_topology_v1:
core_runtime:
modules: [identity, situation, work, epistemics, capability, artifact]
modules: [identity, situation, work, epistemics, capability, artifact, interaction]
continuity_runtime:
modules: [continuity]
commit_runtime:
@@ -107,6 +108,7 @@ storage_topology_v1:
capability: capability_role
commit: commit_role
artifact: artifact_role
interaction: interaction_role
cross_owner_fk: forbidden_by_default
cross_owner_write: forbidden
cross_owner_application_join: forbidden
@@ -142,6 +144,10 @@ protected_paths:
- architecture/architecture-registry.yaml
- architecture/adrs/**
- ci/architecture/**
- .gitea/workflows/ci.yml
- cmd/devctl/**
- dev/ACTIVE_TASK.json
- ci/architecture/**
- security/**
default_feature_task_authority:
+1 -1
View File
@@ -27,7 +27,7 @@ modules:
work:
id: mod.work
owns: [Goal, HelpPath, GoalLifecycle, Task, Decision, PlanCandidate, WorkPlan, PlanStep, CapabilityRequirement, DelegationCandidate]
owns: [Goal, HelpPath, GoalLifecycle, Task, Decision, PlanCandidate, WorkPlan, PlanStep, CapabilityRequirement, DelegationCandidate, Delegation, AgentRun, AgentCheckpoint, AgentResult, DelegationVerification]
database_schema: work
boundary_class: remote_capable
may_depend_on: [contracts.identity, contracts.authority, contracts.epistemics, contracts.capability, platform.ids, platform.events]
+69
View File
@@ -0,0 +1,69 @@
package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"os"
"time"
"git.cin.su/m/Assistant/adapters/postgres"
"git.cin.su/m/Assistant/contracts/work"
"git.cin.su/m/Assistant/modules/work/application"
"git.cin.su/m/Assistant/modules/work/ports"
)
type memoryStore struct{ writes int }
func (s *memoryStore) SaveDispatchIntent(context.Context, work.AgentDispatchIntent) error {
s.writes++
return nil
}
func (s *memoryStore) SaveAgentLease(context.Context, work.AgentLease) error { s.writes++; return nil }
func (s *memoryStore) AcceptAgentCallback(context.Context, work.AgentCallback) error {
s.writes++
return nil
}
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")
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()}
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")})
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
store = repository
closeStore = repository.Close
}
if closeStore != nil {
defer closeStore()
}
result, err := application.ExecuteCanaryDryRun(context.Background(), store, application.CanaryConfig{Enabled: *enabled, ImplementationID: "fake-worker", MaxDispatches: 1}, 0, intent, "manual-worker", 1, now, 15*time.Minute)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
output := struct {
DispatchID string `json:"dispatch_id"`
RunID string `json:"run_id"`
CallbackCount int `json:"callback_count"`
StoreWrites int `json:"store_writes"`
}{result.Intent.DispatchID, string(result.Intent.RunID), result.CallbackCount(), 0}
if memory, ok := store.(*memoryStore); ok {
output.StoreWrites = memory.writes
}
if err := json.NewEncoder(os.Stdout).Encode(output); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
+9
View File
@@ -15,6 +15,7 @@ import (
"log"
"net/http"
"os"
"strings"
"time"
)
@@ -71,6 +72,14 @@ func main() {
}
gateway := cognitiveGateway{resolver: resolver, providers: providers, builtinLocal: builtinLocal, externalRecovery: externalRecovery, now: time.Now, allowCanary: os.Getenv("COGNITIVE_ALLOW_CANARY") == "true", canaryMaxOutputBytes: 4096}
mux.Handle("/healthz", health.Handler{Runtime: "cognitive-worker", SemanticGeneration: 1, ArchitectureGeneration: 1})
readinessClient := &http.Client{Timeout: 2 * time.Second}
lifeboatURL := strings.TrimSuffix(strings.TrimRight(envOr("LOCAL_LIFEBOAT_PROVIDER_URL", "http://cognitive-lifeboat:8086/v1"), "/"), "/v1") + "/health"
mux.Handle("/readyz", health.ReadinessHandler{Runtime: "cognitive-worker", Probes: []health.Probe{
health.HTTPProbe(readinessClient, strings.TrimRight(envOr("CORE_RUNTIME_URL", "http://core-runtime:8080"), "/")+"/healthz"),
health.HTTPProbe(readinessClient, strings.TrimRight(envOr("LOCAL_CPU_PROVIDER_URL", "http://docker.cin.su:18082/v1"), "/")+"/models"),
health.HTTPProbe(readinessClient, lifeboatURL),
health.HTTPProbe(readinessClient, strings.TrimRight(envOr("LOCAL_GPU_PROVIDER_URL", "http://192.168.220.91:11434"), "/")+"/api/tags"),
}})
mux.HandleFunc("POST /v1/proposals/deterministic", deterministicProposal)
mux.HandleFunc("POST /v1/advisory-policy", gateway.policyHandler)
mux.HandleFunc("POST /v1/advisories", gateway.advisoryHandler)
+7
View File
@@ -84,6 +84,13 @@ func main() {
dispatcher := operationDispatcher{continuityExecutor: continuityExecutor, continuityReconciler: continuityExecutor, bindings: creatorCore, secretRef: notificationSecretRef, secret: notificationSecret, client: creatorHTTPClient}
mux := http.NewServeMux()
mux.Handle("/healthz", health.Handler{Runtime: "commit-runtime", SemanticGeneration: 1, ArchitectureGeneration: 1})
readinessClient := &http.Client{Timeout: 2 * time.Second}
mux.Handle("/readyz", health.ReadinessHandler{Runtime: "commit-runtime", Probes: []health.Probe{
health.TCPProbe("commit-postgres", envOr("POSTGRES_HOST", "postgres"), envOr("POSTGRES_PORT", "5432")),
health.TCPProbe("authority-postgres", envOr("POSTGRES_HOST", "postgres"), envOr("POSTGRES_PORT", "5432")),
health.HTTPProbe(readinessClient, strings.TrimRight(coreURL, "/")+"/healthz"),
health.HTTPProbe(readinessClient, strings.TrimRight(envOr("CONTINUITY_RUNTIME_URL", "http://continuity-runtime:8081"), "/")+"/healthz"),
}})
registerExecutionRoutes(mux, repository, dispatcher, dispatcher, executorToken)
registerCreatorCommandRoutes(mux, creatorCommandHandler{
core: creatorCore, continuity: creatorContinuity, authority: authorityRepository, repository: repository,
+7
View File
@@ -7,6 +7,7 @@ import (
"log"
"net/http"
"os"
"strings"
"time"
"git.cin.su/m/Assistant/adapters/postgres"
@@ -31,6 +32,12 @@ func main() {
}
mux := http.NewServeMux()
mux.Handle("/healthz", health.Handler{Runtime: "continuity-runtime", SemanticGeneration: 1, ArchitectureGeneration: 1})
readinessClient := &http.Client{Timeout: 2 * time.Second}
mux.Handle("/readyz", health.ReadinessHandler{Runtime: "continuity-runtime", Probes: []health.Probe{
health.TCPProbe("postgres", envOr("POSTGRES_HOST", "postgres"), envOr("POSTGRES_PORT", "5432")),
health.HTTPProbe(readinessClient, strings.TrimRight(envOr("CORE_RUNTIME_URL", "http://core-runtime:8080"), "/")+"/healthz"),
health.HTTPProbe(readinessClient, strings.TrimRight(envOr("COMMIT_RUNTIME_URL", "http://commit-runtime:8082"), "/")+"/healthz"),
}})
executorToken := os.Getenv("COMMIT_EXECUTOR_TOKEN")
if executorToken == "" {
log.Fatal("COMMIT_EXECUTOR_TOKEN is required")
+6
View File
@@ -135,6 +135,12 @@ func main() {
return situationdomain.NewResolver(bindings).Resolve(runtime)
}
mux.Handle("/healthz", health.Handler{Runtime: "core-runtime", SemanticGeneration: 1, ArchitectureGeneration: 1})
readinessClient := &http.Client{Timeout: 2 * time.Second}
mux.Handle("/readyz", health.ReadinessHandler{Runtime: "core-runtime", Probes: []health.Probe{
health.TCPProbe("postgres", envOr("POSTGRES_HOST", "postgres"), envOr("POSTGRES_PORT", "5432")),
health.HTTPProbe(readinessClient, strings.TrimRight(envOr("CONTINUITY_RUNTIME_URL", "http://continuity-runtime:8081"), "/")+"/healthz"),
health.HTTPProbe(readinessClient, strings.TrimRight(envOr("COGNITIVE_RUNTIME_URL", "http://cognitive-worker:8083"), "/")+"/healthz"),
}})
mux.HandleFunc("POST /v1/creator/sessions", creatorSessionHandler(bootstrapToken, creatorUsername, creatorPassword, sessionKey, time.Now))
mux.HandleFunc("GET /v1/creator/sessions/current", func(w http.ResponseWriter, r *http.Request) {
session, ok := authenticateCreator(w, r)
+25 -1
View File
@@ -378,7 +378,7 @@ func (g *gateway) handleChatMessage(w http.ResponseWriter, r *http.Request) {
cognition = "unavailable"
requestID, idErr := ids.New()
if idErr == nil {
trusted := cognitive.GatewayAdvisoryRequest{Version: cognitive.CreatorAdvisoryV1, RequestID: cognitive.RequestID(requestID), ActorID: session.ActorID, PrincipalID: session.PrincipalID, ScopeID: "domovoy-global-scope", Message: browserRequest.Message, Context: []cognitive.ContextExcerpt{}, Locale: "ru-RU", MaxOutputBytes: 4096, DataPolicy: capability.DataPolicyLocalOnly, EgressAcknowledged: false, Meaning: cognitive.TaskMeaning{SpeechAct: string(saved.SpeechAct), CapabilityKind: saved.CapabilityKind, CapabilityHints: saved.CapabilityHints, RequestedOutcome: saved.RequestedOutcome, InformationRequestKind: saved.InformationRequestKind, ConfidenceMillis: saved.InterpretationConfidenceMillis}}
trusted := cognitive.GatewayAdvisoryRequest{Version: cognitive.CreatorAdvisoryV1, RequestID: cognitive.RequestID(requestID), ActorID: session.ActorID, PrincipalID: session.PrincipalID, ScopeID: "domovoy-global-scope", Message: browserRequest.Message, Context: localChatContext(saved), Locale: "ru-RU", MaxOutputBytes: 4096, DataPolicy: capability.DataPolicyLocalOnly, EgressAcknowledged: false, Meaning: cognitive.TaskMeaning{SpeechAct: string(saved.SpeechAct), CapabilityKind: saved.CapabilityKind, CapabilityHints: saved.CapabilityHints, RequestedOutcome: saved.RequestedOutcome, InformationRequestKind: saved.InformationRequestKind, ConfidenceMillis: saved.InterpretationConfidenceMillis}}
payload, err = json.Marshal(trusted)
if err == nil {
var advisory cognitive.GatewayAdvisoryResponse
@@ -415,6 +415,30 @@ func (g *gateway) handleChatMessage(w http.ResponseWriter, r *http.Request) {
})
}
func localChatContext(saved interaction.ChatMessageResponse) []cognitive.ContextExcerpt {
if strings.TrimSpace(saved.ActiveProjectName) == "" {
return []cognitive.ContextExcerpt{}
}
bundle, err := cognitive.CompileContext(cognitive.ContextCompileInput{
DataPolicy: capability.DataPolicyLocalOnly,
MaxBytes: 2048,
Candidates: []cognitive.ContextCandidate{{
Excerpt: cognitive.ContextExcerpt{
ID: cognitive.ContextExcerptID("active-project-focus"),
SourceKind: "situation-active-project",
SourceID: strings.TrimSpace(saved.ActiveProjectName),
Sensitivity: cognitive.SensitivityPrivateAllowed,
Text: "Текущий проект: " + strings.TrimSpace(saved.ActiveProjectName),
},
Relevance: 100,
}},
})
if err != nil {
return []cognitive.ContextExcerpt{}
}
return bundle.Excerpts
}
func asksAttention(message string) bool {
lower := strings.ToLower(message)
return strings.Contains(lower, "требует моего внимания") || strings.Contains(lower, "требует внимания") || strings.Contains(lower, "что сейчас важно")
+11
View File
@@ -10,8 +10,19 @@ import (
"testing"
"git.cin.su/m/Assistant/contracts/cognitive"
"git.cin.su/m/Assistant/contracts/interaction"
)
func TestLocalChatContextUsesOnlyActiveProject(t *testing.T) {
context := localChatContext(interaction.ChatMessageResponse{ActiveProjectName: "Домовой"})
if len(context) != 1 || context[0].SourceKind != "situation-active-project" || context[0].Text != "Текущий проект: Домовой" {
t.Fatalf("unexpected local context: %#v", context)
}
if empty := localChatContext(interaction.ChatMessageResponse{}); len(empty) != 0 {
t.Fatalf("expected empty context without active project: %#v", empty)
}
}
const legacyExternalMode = "legacy-external-test-mode"
func newHandlerWithAdvisoryMode(coreURL, continuityURL, commitURL string, client *http.Client, mode string, cognitiveURLs ...string) (http.Handler, error) {
+1 -1
View File
@@ -166,7 +166,7 @@ func loadSelectedTask(root string, selection taskSelection) (TaskPacket, string,
func loadTaskPacket(root, taskPath string) (TaskPacket, error) {
var taskPacket TaskPacket
if err := decodeJSONFile(filepath.Join(root, filepath.FromSlash(taskPath)), &taskPacket, false); err != nil {
if err := decodeJSONFile(filepath.Join(root, filepath.FromSlash(taskPath)), &taskPacket, true); err != nil {
return TaskPacket{}, fmt.Errorf("task packet: %w", err)
}
return taskPacket, nil
+64
View File
@@ -0,0 +1,64 @@
package main
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
)
var required = []string{
"task_id", "task_revision", "architecture_revision", "base_repo_revision", "risk_tier", "intent",
"requirement_ids", "allowed_change_scope", "forbidden_change_scope", "allowed_architecture_delta",
"contracts", "semantic_owner", "canonical_state", "state_transitions", "side_effects_and_privileges",
"persistence_and_migrations", "failure_retry_restart", "assumptions", "acceptance_scenarios",
"required_tests", "terminal_gap_states",
}
func main() {
activePath := filepath.FromSlash("dev/ACTIVE_TASK.json")
var active struct {
TaskFile string `json:"task_file"`
TaskID string `json:"task_id"`
TaskRevision int `json:"task_revision"`
}
if err := decode(activePath, &active); err != nil {
fail("active pointer: %v", err)
}
if active.TaskFile == "" || active.TaskID == "" || active.TaskRevision < 1 {
fail("active pointer is incomplete")
}
var raw map[string]json.RawMessage
if err := decode(filepath.FromSlash(active.TaskFile), &raw); err != nil {
fail("task packet: %v", err)
}
for _, key := range required {
if _, ok := raw[key]; !ok {
fail("task packet missing required field %q", key)
}
}
var packet struct {
TaskID string `json:"task_id"`
TaskRevision int `json:"task_revision"`
}
if err := decode(filepath.FromSlash(active.TaskFile), &packet); err != nil {
fail("task identity: %v", err)
}
if packet.TaskID != active.TaskID || packet.TaskRevision != active.TaskRevision {
fail("active pointer and packet identity differ")
}
fmt.Printf("independent governance verification: OK task=%s revision=%d\n", packet.TaskID, packet.TaskRevision)
}
func decode(path string, target any) error {
b, err := os.ReadFile(path)
if err != nil {
return err
}
return json.Unmarshal(b, target)
}
func fail(format string, args ...any) {
fmt.Fprintf(os.Stderr, "independent verifier: "+format+"\n", args...)
os.Exit(1)
}
+32 -11
View File
@@ -2,7 +2,6 @@ package main
import (
"encoding/json"
"errors"
"fmt"
"go/parser"
"go/token"
@@ -24,19 +23,41 @@ type Registry struct {
BoundaryClass string `json:"boundary_class"`
MayDependOn []string `json:"may_depend_on"`
Forbidden []string `json:"forbidden"`
Privileged bool `json:"privileged"`
} `json:"modules"`
ProtectedPaths []string `json:"protected_paths"`
}
type TaskPacket struct {
TaskID string `json:"task_id"`
TaskRevision int `json:"task_revision"`
BaseRepoRevision string `json:"base_repo_revision"`
RiskTier string `json:"risk_tier"`
Intent string `json:"intent"`
AllowedChangeScope struct {
Paths []string `json:"paths"`
TaskID string `json:"task_id"`
TaskRevision int `json:"task_revision"`
ArchitectureRevision string `json:"architecture_revision"`
BaseRepoRevision string `json:"base_repo_revision"`
RiskTier string `json:"risk_tier"`
Intent string `json:"intent"`
RequirementIDs []string `json:"requirement_ids"`
AllowedChangeScope struct {
Modules []string `json:"modules"`
Paths []string `json:"paths"`
} `json:"allowed_change_scope"`
ForbiddenChangeScope struct {
Paths []string `json:"paths"`
} `json:"forbidden_change_scope"`
AllowedArchitectureDelta map[string]int `json:"allowed_architecture_delta"`
Contracts struct {
Consume []string `json:"consume"`
MayChange []string `json:"may_change"`
} `json:"contracts"`
SemanticOwner string `json:"semantic_owner"`
CanonicalState []string `json:"canonical_state"`
StateTransitions []string `json:"state_transitions"`
SideEffectsAndPrivileges []string `json:"side_effects_and_privileges"`
PersistenceAndMigrations []string `json:"persistence_and_migrations"`
FailureRetryRestart []string `json:"failure_retry_restart"`
Assumptions []string `json:"assumptions"`
AcceptanceScenarios []string `json:"acceptance_scenarios"`
RequiredTests []string `json:"required_tests"`
TerminalGapStates []string `json:"terminal_gap_states"`
}
func main() {
@@ -207,12 +228,12 @@ func preflight(args []string) error {
if err != nil {
return err
}
taskPacket, _, _, err := loadSelectedTask(root, selection)
taskPacket, taskPath, _, err := loadSelectedTask(root, selection)
if err != nil {
return err
}
if taskPacket.TaskID == "" || taskPacket.Intent == "" || taskPacket.RiskTier == "" {
return errors.New("task packet missing task_id, intent, or risk_tier")
if err := validateTaskPacket(root, taskPacket, taskPath); err != nil {
return err
}
registry, err := loadRegistry()
if err != nil {
+215
View File
@@ -0,0 +1,215 @@
package main
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
)
var requiredPacketKeys = []string{
"task_id", "task_revision", "architecture_revision", "base_repo_revision", "risk_tier", "intent",
"requirement_ids", "allowed_change_scope", "forbidden_change_scope", "allowed_architecture_delta",
"contracts", "semantic_owner", "canonical_state", "state_transitions", "side_effects_and_privileges",
"persistence_and_migrations", "failure_retry_restart", "assumptions", "acceptance_scenarios",
"required_tests", "terminal_gap_states",
}
var requiredDeltaKeys = []string{
"new_cross_module_dependencies", "new_canonical_owners", "new_privileges",
"new_third_party_dependencies", "new_database_owner_roles",
}
func validateTaskPacket(root string, task TaskPacket, taskPath string) error {
if task.TaskID == "" || task.TaskRevision < 1 || task.ArchitectureRevision == "" || task.BaseRepoRevision == "" || task.RiskTier == "" || task.Intent == "" {
return fmt.Errorf("task %s is incomplete: identity, revision, architecture_revision, base_repo_revision, risk_tier and intent are required", task.TaskID)
}
if taskPath == "" || !matchesAny(taskPath, mustCompile([]string{"dev/tasks/*.json"})) {
return fmt.Errorf("task %s must be selected from dev/tasks", task.TaskID)
}
if task.RiskTier < "A" || task.RiskTier > "E" {
return fmt.Errorf("task %s has invalid risk_tier %q", task.TaskID, task.RiskTier)
}
if len(task.RequirementIDs) == 0 || len(task.AcceptanceScenarios) == 0 || len(task.RequiredTests) == 0 || len(task.TerminalGapStates) == 0 {
return fmt.Errorf("task %s must declare requirements, acceptance_scenarios, required_tests and terminal_gap_states", task.TaskID)
}
if len(task.AllowedChangeScope.Paths) == 0 {
return fmt.Errorf("task %s must declare allowed_change_scope.paths", task.TaskID)
}
if err := validateScopeOverlap(task.AllowedChangeScope.Paths, task.ForbiddenChangeScope.Paths); err != nil {
return fmt.Errorf("task %s scope: %w", task.TaskID, err)
}
if err := validateDelta(task.AllowedArchitectureDelta); err != nil {
return fmt.Errorf("task %s architecture delta: %w", task.TaskID, err)
}
if err := validateRequiredTests(task.RequiredTests); err != nil {
return fmt.Errorf("task %s required_tests: %w", task.TaskID, err)
}
if err := validateArchitectureRevision(root, task.ArchitectureRevision); err != nil {
return fmt.Errorf("task %s architecture_revision: %w", task.TaskID, err)
}
if _, err := resolveBaseCommit(root, task.BaseRepoRevision); err != nil {
return fmt.Errorf("task %s base_repo_revision: %w", task.TaskID, err)
}
if err := validateObservedDelta(root, task); err != nil {
return fmt.Errorf("task %s observed architecture delta: %w", task.TaskID, err)
}
return nil
}
func validateRequiredTests(tests []string) error {
joined := strings.Join(tests, "\n")
for _, required := range []string{"go test", "go vet", "preflight", "check-diff", "git diff --check", "independent"} {
if !strings.Contains(joined, required) {
return fmt.Errorf("missing required governance evidence %q", required)
}
}
return nil
}
func validateObservedDelta(root string, task TaskPacket) error {
changed, err := changedPaths(root, task.BaseRepoRevision)
if err != nil {
return err
}
if err := validateRegistryDelta(root, task); err != nil {
return err
}
for _, changedPath := range changed {
if (strings.HasPrefix(changedPath, "security/") || strings.HasPrefix(changedPath, "deploy/")) && task.AllowedArchitectureDelta["new_privileges"] == 0 {
return fmt.Errorf("privileged surface changed with new_privileges=0: %s", changedPath)
}
if strings.HasPrefix(changedPath, "database/") && task.AllowedArchitectureDelta["new_database_owner_roles"] == 0 {
return fmt.Errorf("database surface changed with new_database_owner_roles=0: %s", changedPath)
}
if (changedPath == "go.mod" || changedPath == "go.sum") && task.AllowedArchitectureDelta["new_third_party_dependencies"] == 0 {
return fmt.Errorf("dependency manifest changed with new_third_party_dependencies=0: %s", changedPath)
}
if strings.HasPrefix(changedPath, "contracts/") && len(task.Contracts.MayChange) == 0 {
return fmt.Errorf("contract surface changed but contracts.may_change is empty: %s", changedPath)
}
}
return nil
}
func validateRegistryDelta(root string, task TaskPacket) error {
baseBytes, err := runGit(root, "show", task.BaseRepoRevision+":architecture/architecture-registry.json")
if err != nil {
return fmt.Errorf("read base architecture registry: %w", err)
}
currentBytes, err := os.ReadFile(filepath.Join(root, "architecture", "architecture-registry.json"))
if err != nil {
return err
}
var base, current Registry
if err := json.Unmarshal(baseBytes, &base); err != nil {
return fmt.Errorf("parse base architecture registry: %w", err)
}
if err := json.Unmarshal(currentBytes, &current); err != nil {
return fmt.Errorf("parse current architecture registry: %w", err)
}
if task.AllowedArchitectureDelta["new_canonical_owners"] == 0 {
for name := range current.Modules {
if _, ok := base.Modules[name]; !ok {
return fmt.Errorf("new canonical module owner %q with new_canonical_owners=0", name)
}
}
}
if task.AllowedArchitectureDelta["new_cross_module_dependencies"] == 0 {
for name, module := range current.Modules {
previous, ok := base.Modules[name]
if !ok {
continue
}
old := map[string]bool{}
for _, dep := range previous.MayDependOn {
old[dep] = true
}
for _, dep := range module.MayDependOn {
if !old[dep] {
return fmt.Errorf("new dependency %q in module %q with new_cross_module_dependencies=0", dep, name)
}
}
}
}
if task.AllowedArchitectureDelta["new_privileges"] == 0 {
for name, module := range current.Modules {
if previous, ok := base.Modules[name]; ok && module.Privileged && !previous.Privileged {
return fmt.Errorf("new privilege in module %q with new_privileges=0", name)
}
}
}
return nil
}
func validateDelta(delta map[string]int) error {
if len(delta) != len(requiredDeltaKeys) {
return fmt.Errorf("must contain exactly %s", strings.Join(requiredDeltaKeys, ", "))
}
for _, key := range requiredDeltaKeys {
value, ok := delta[key]
if !ok || value < 0 {
return fmt.Errorf("%s must be a non-negative integer", key)
}
}
return nil
}
func validateArchitectureRevision(root, revision string) error {
if strings.HasPrefix(revision, "ADR-") {
matches, err := filepath.Glob(filepath.Join(root, "architecture", "adrs", revision+"-*.md"))
if err != nil || len(matches) != 1 {
return fmt.Errorf("accepted ADR %q is missing or ambiguous", revision)
}
return nil
}
if strings.HasPrefix(revision, "ARCH-") {
matches, err := filepath.Glob(filepath.Join(root, "dev", "tasks", revision+"*.json"))
if err != nil || len(matches) == 0 {
return fmt.Errorf("architecture task %q is missing", revision)
}
return nil
}
return fmt.Errorf("must reference an accepted ADR-NNNN or architecture task")
}
func validateScopeOverlap(allowed, forbidden []string) error {
allowedPatterns, err := compileScopePatterns(allowed)
if err != nil {
return err
}
forbiddenPatterns, err := compileScopePatterns(forbidden)
if err != nil {
return fmt.Errorf("forbidden_change_scope: %w", err)
}
for _, a := range allowedPatterns {
for _, f := range forbiddenPatterns {
if scopePatternsOverlap(a, f) {
return fmt.Errorf("allowed path %q overlaps forbidden path %q", a.raw, f.raw)
}
}
}
return nil
}
func scopePatternsOverlap(a, b scopePattern) bool {
if a.subtree != "" && b.subtree != "" {
return a.subtree == b.subtree || strings.HasPrefix(a.subtree, b.subtree+"/") || strings.HasPrefix(b.subtree, a.subtree+"/")
}
if a.subtree != "" {
return b.raw == a.subtree || strings.HasPrefix(b.raw, a.subtree+"/")
}
if b.subtree != "" {
return a.raw == b.subtree || strings.HasPrefix(a.raw, b.subtree+"/")
}
return a.raw == b.raw
}
func mustCompile(values []string) []scopePattern {
patterns, err := compileScopePatterns(values)
if err != nil {
panic(err)
}
return patterns
}
+27
View File
@@ -0,0 +1,27 @@
package main
import (
"strings"
"testing"
)
func TestValidateScopeOverlapRejectsAllowedForbiddenConflict(t *testing.T) {
err := validateScopeOverlap([]string{"cmd/creator-web/server.go"}, []string{"cmd/creator-web/**"})
if err == nil || !strings.Contains(err.Error(), "overlaps forbidden") {
t.Fatalf("overlap error = %v", err)
}
}
func TestValidateRequiredTestsRejectsIncompleteEvidence(t *testing.T) {
err := validateRequiredTests([]string{"go test ./..."})
if err == nil || !strings.Contains(err.Error(), "missing required governance evidence") {
t.Fatalf("required test error = %v", err)
}
}
func TestValidateDeltaRejectsUnknownOrMissingFields(t *testing.T) {
err := validateDelta(map[string]int{"new_privileges": 0})
if err == nil || !strings.Contains(err.Error(), "exactly") {
t.Fatalf("delta error = %v", err)
}
}
+19
View File
@@ -2,4 +2,23 @@ package artifact
import "git.cin.su/m/Assistant/platform/ids"
import "errors"
type ArtifactID ids.ID
type AgentArtifactAttestation struct {
ArtifactID ArtifactID
ProducerRunID string
SourceRevision string
WorktreeID string
ContentDigest string
TestEvidenceRef string
CreatedAtUnixMillis int64
}
func (a AgentArtifactAttestation) Validate() error {
if a.ArtifactID == "" || a.ProducerRunID == "" || a.SourceRevision == "" || a.WorktreeID == "" || a.ContentDigest == "" || a.TestEvidenceRef == "" || a.CreatedAtUnixMillis < 1 {
return errors.New("agent artifact attestation is incomplete")
}
return nil
}
+68
View File
@@ -0,0 +1,68 @@
package capability
type AdmissionComplexity string
type AdmissionDecisionState string
type AdmissionReason string
const (
AdmissionSimple AdmissionComplexity = "SIMPLE"
AdmissionNormal AdmissionComplexity = "NORMAL"
AdmissionHeavy AdmissionComplexity = "HEAVY"
AdmissionAdmitted AdmissionDecisionState = "ADMITTED"
AdmissionRejected AdmissionDecisionState = "REJECTED"
AdmissionReasonNone AdmissionReason = "NONE"
AdmissionReasonConcurrency AdmissionReason = "CONCURRENCY_LIMIT"
AdmissionReasonAcceptedHour AdmissionReason = "ACCEPTED_PER_HOUR_LIMIT"
AdmissionReasonDeadline AdmissionReason = "DEADLINE"
AdmissionReasonInputBudget AdmissionReason = "INPUT_BUDGET"
AdmissionReasonOutputBudget AdmissionReason = "OUTPUT_BUDGET"
)
type AdmissionPolicy struct {
ID string
MaxConcurrent int
MaxAcceptedPerHour int
MaxInputBytes int
MaxOutputBytes int
}
type AdmissionRequest struct {
Complexity AdmissionComplexity
InputBytes int
MaxOutputBytes int
DeadlineMillis int64
}
type AdmissionCounters struct {
Concurrent int
AcceptedLastHour int
}
type AdmissionDecision struct {
State AdmissionDecisionState
Reason AdmissionReason
PolicyID string
}
func Admit(request AdmissionRequest, policy AdmissionPolicy, counters AdmissionCounters, nowMillis int64) AdmissionDecision {
decision := AdmissionDecision{State: AdmissionAdmitted, Reason: AdmissionReasonNone, PolicyID: policy.ID}
if policy.ID == "" || policy.MaxConcurrent < 1 || policy.MaxAcceptedPerHour < 1 || policy.MaxInputBytes < 1 || policy.MaxOutputBytes < 1 {
return AdmissionDecision{State: AdmissionRejected, Reason: AdmissionReasonInputBudget, PolicyID: policy.ID}
}
if counters.Concurrent >= policy.MaxConcurrent {
return AdmissionDecision{State: AdmissionRejected, Reason: AdmissionReasonConcurrency, PolicyID: policy.ID}
}
if counters.AcceptedLastHour >= policy.MaxAcceptedPerHour {
return AdmissionDecision{State: AdmissionRejected, Reason: AdmissionReasonAcceptedHour, PolicyID: policy.ID}
}
if request.InputBytes < 0 || request.InputBytes > policy.MaxInputBytes {
return AdmissionDecision{State: AdmissionRejected, Reason: AdmissionReasonInputBudget, PolicyID: policy.ID}
}
if request.MaxOutputBytes < 1 || request.MaxOutputBytes > policy.MaxOutputBytes {
return AdmissionDecision{State: AdmissionRejected, Reason: AdmissionReasonOutputBudget, PolicyID: policy.ID}
}
if request.DeadlineMillis > 0 && request.DeadlineMillis <= nowMillis {
return AdmissionDecision{State: AdmissionRejected, Reason: AdmissionReasonDeadline, PolicyID: policy.ID}
}
return decision
}
+72
View File
@@ -0,0 +1,72 @@
package capability
import "errors"
type QualificationManifestID string
type QualificationDimension string
const (
DimensionDialogue QualificationDimension = "DIALOGUE"
DimensionInterpretation QualificationDimension = "INTERPRETATION"
DimensionCoding QualificationDimension = "CODING"
DimensionResearch QualificationDimension = "RESEARCH"
DimensionUncertainty QualificationDimension = "UNCERTAINTY"
DimensionContext QualificationDimension = "CONTEXT"
DimensionLatency QualificationDimension = "LATENCY"
DimensionResources QualificationDimension = "RESOURCES"
DimensionRestart QualificationDimension = "RESTART"
)
type QualificationMeasurement struct {
Dimension QualificationDimension
Result EvaluationResult
Artifact string
}
type QualificationManifest struct {
ID QualificationManifestID
ModelDescriptorID ModelDescriptorID
ModelRevision int64
ModelFingerprint ModelFingerprint
BenchmarkVersion string
HoldoutCorpusDigest string
LicenseEvidence string
DataPolicyEvidence string
Measurements []QualificationMeasurement
ObservedAtUnixMillis int64
}
func (m QualificationManifest) Validate() error {
if m.ID == "" || m.ModelDescriptorID == "" || m.ModelRevision < 1 || m.ModelFingerprint == "" || m.BenchmarkVersion == "" || m.HoldoutCorpusDigest == "" || m.LicenseEvidence == "" || m.DataPolicyEvidence == "" || m.ObservedAtUnixMillis < 1 {
return errors.New("qualification manifest identity and evidence are incomplete")
}
required := map[QualificationDimension]bool{DimensionDialogue: false, DimensionInterpretation: false, DimensionCoding: false, DimensionResearch: false, DimensionUncertainty: false, DimensionContext: false, DimensionLatency: false, DimensionResources: false, DimensionRestart: false}
for _, measurement := range m.Measurements {
if measurement.Dimension == "" || measurement.Artifact == "" || measurement.Result == EvaluationUnknown {
return errors.New("qualification measurement is incomplete")
}
if _, ok := required[measurement.Dimension]; ok {
required[measurement.Dimension] = true
}
}
for _, present := range required {
if !present {
return errors.New("qualification manifest lacks required measurement")
}
}
return nil
}
// QualificationState projects evidence into a qualification result. It does
// not activate a binding or mutate the model registry.
func (m QualificationManifest) QualificationState() QualificationState {
if m.Validate() != nil {
return QualificationRejected
}
for _, measurement := range m.Measurements {
if measurement.Result != EvaluationSupported {
return QualificationRejected
}
}
return QualificationValidated
}
+91
View File
@@ -0,0 +1,91 @@
package cognitive
import (
"errors"
"sort"
"strings"
"git.cin.su/m/Assistant/contracts/capability"
)
var ErrInvalidContextBudget = errors.New("context budget must be positive")
type ContextCandidate struct {
Excerpt ContextExcerpt
Relevance int
}
type ContextCompileInput struct {
Candidates []ContextCandidate
DataPolicy capability.DataPolicy
MaxBytes int
}
type ContextOmission struct {
ID ContextExcerptID
Reason string
}
type ContextBundle struct {
Excerpts []ContextExcerpt
Omissions []ContextOmission
Bytes int
}
// CompileContext deterministically selects permitted, relevant excerpts for a
// gateway request. It carries no provider identity and performs no I/O.
func CompileContext(input ContextCompileInput) (ContextBundle, error) {
if input.MaxBytes <= 0 {
return ContextBundle{}, ErrInvalidContextBudget
}
candidates := append([]ContextCandidate(nil), input.Candidates...)
sort.SliceStable(candidates, func(i, j int) bool {
if candidates[i].Relevance != candidates[j].Relevance {
return candidates[i].Relevance > candidates[j].Relevance
}
return string(candidates[i].Excerpt.ID) < string(candidates[j].Excerpt.ID)
})
bundle := ContextBundle{Excerpts: []ContextExcerpt{}, Omissions: []ContextOmission{}}
seen := make(map[ContextExcerptID]struct{}, len(candidates))
for _, candidate := range candidates {
excerpt := candidate.Excerpt
if _, ok := seen[excerpt.ID]; ok {
bundle.Omissions = append(bundle.Omissions, ContextOmission{ID: excerpt.ID, Reason: "duplicate"})
continue
}
seen[excerpt.ID] = struct{}{}
reason := permittedContextReason(excerpt, input.DataPolicy)
if reason != "" {
bundle.Omissions = append(bundle.Omissions, ContextOmission{ID: excerpt.ID, Reason: reason})
continue
}
if strings.TrimSpace(excerpt.Text) == "" {
bundle.Omissions = append(bundle.Omissions, ContextOmission{ID: excerpt.ID, Reason: "empty"})
continue
}
bytes := len([]byte(excerpt.Text))
if bundle.Bytes+bytes > input.MaxBytes {
bundle.Omissions = append(bundle.Omissions, ContextOmission{ID: excerpt.ID, Reason: "budget"})
continue
}
bundle.Excerpts = append(bundle.Excerpts, excerpt)
bundle.Bytes += bytes
}
return bundle, nil
}
func permittedContextReason(excerpt ContextExcerpt, policy capability.DataPolicy) string {
if excerpt.Sensitivity == SensitivitySecret {
return "secret"
}
if excerpt.Sensitivity == SensitivityUnknown {
return "unknown_sensitivity"
}
if policy == capability.DataPolicyExternalMessageOnly {
return "policy_message_only"
}
if policy == capability.DataPolicyExternalMinimizedContext && excerpt.Sensitivity != SensitivityPublic {
return "policy_public_only"
}
return ""
}
+111
View File
@@ -0,0 +1,111 @@
package epistemics
import (
"errors"
"strings"
)
var ErrCitationEvidenceMismatch = errors.New("citation evidence does not match source chain")
type ResearchPlanID string
type SourceID string
type SourceSnapshotID string
type SourceExcerptID string
type CitationID string
type FreshnessState string
type CoverageState string
type ConflictState string
type EvidenceKind string
const (
FreshnessCurrent FreshnessState = "CURRENT"
FreshnessStale FreshnessState = "STALE"
FreshnessUnknown FreshnessState = "UNKNOWN"
CoverageComplete CoverageState = "COMPLETE"
CoveragePartial CoverageState = "PARTIAL"
CoverageUnknown CoverageState = "UNKNOWN"
ConflictNone ConflictState = "NONE"
ConflictPresent ConflictState = "PRESENT"
ConflictUnknown ConflictState = "UNKNOWN"
EvidenceObserved EvidenceKind = "OBSERVED"
EvidenceAssumed EvidenceKind = "ASSUMED"
)
type ResearchPlan struct {
ID ResearchPlanID
Question string
SourceClasses []string
FreshnessPolicy string
CreatedAtUnixMillis int64
}
type Source struct {
ID SourceID
Kind string
Locator string
Title string
Publisher string
}
type SourceSnapshot struct {
ID SourceSnapshotID
SourceID SourceID
ContentDigest string
CapturedAtUnixMillis int64
Freshness FreshnessState
}
type SourceExcerpt struct {
ID SourceExcerptID
SnapshotID SourceSnapshotID
Text string
Start int64
End int64
}
type Citation struct {
ID CitationID
ExcerptID SourceExcerptID
Evidence EvidenceKind
Coverage CoverageState
Conflict ConflictState
ResolvedAtUnixMillis int64
}
// ResolveCitation creates evidence only when all identifiers form one exact
// source chain. It deliberately does not fetch, infer or rewrite source text.
func ResolveCitation(source Source, snapshot SourceSnapshot, excerpt SourceExcerpt, evidence EvidenceKind, coverage CoverageState, conflict ConflictState, resolvedAtUnixMillis int64) (Citation, error) {
if source.ID == "" || strings.TrimSpace(source.Locator) == "" || snapshot.SourceID != source.ID || excerpt.SnapshotID != snapshot.ID || excerpt.Validate() != nil || resolvedAtUnixMillis < 1 || evidence == "" || coverage == "" || conflict == "" {
return Citation{}, ErrCitationEvidenceMismatch
}
citation := Citation{ID: CitationID("citation:" + string(excerpt.ID)), ExcerptID: excerpt.ID, Evidence: evidence, Coverage: coverage, Conflict: conflict, ResolvedAtUnixMillis: resolvedAtUnixMillis}
return citation, citation.Validate()
}
func (p ResearchPlan) Validate() error {
if p.ID == "" || strings.TrimSpace(p.Question) == "" || len(p.SourceClasses) == 0 || strings.TrimSpace(p.FreshnessPolicy) == "" {
return errors.New("research plan requires question, source classes and freshness policy")
}
return nil
}
func (s SourceSnapshot) Validate() error {
if s.ID == "" || s.SourceID == "" || strings.TrimSpace(s.ContentDigest) == "" || s.CapturedAtUnixMillis < 1 || s.Freshness == "" {
return errors.New("source snapshot requires source, digest, capture time and freshness")
}
return nil
}
func (e SourceExcerpt) Validate() error {
if e.ID == "" || e.SnapshotID == "" || strings.TrimSpace(e.Text) == "" || e.Start < 0 || e.End < e.Start {
return errors.New("source excerpt requires snapshot, text and valid offsets")
}
return nil
}
func (c Citation) Validate() error {
if c.ID == "" || c.ExcerptID == "" || c.Evidence == "" || c.Coverage == "" || c.Conflict == "" || c.ResolvedAtUnixMillis < 1 {
return errors.New("citation requires exact excerpt, evidence, coverage, conflict and resolution time")
}
return nil
}
+18
View File
@@ -0,0 +1,18 @@
package work
import "time"
type DryRunDispatch struct {
Intent AgentDispatchIntent
Lease AgentLease
Callbacks []AgentCallback
}
func (value DryRunDispatch) CallbackCount() int { return len(value.Callbacks) }
func (value DryRunDispatch) LastObservedAt() time.Time {
if len(value.Callbacks) == 0 {
return time.Time{}
}
return value.Callbacks[len(value.Callbacks)-1].ObservedAt
}
+63
View File
@@ -0,0 +1,63 @@
package work
import "time"
type AgentLeaseID string
type AgentFencingToken uint64
type AgentLeaseState string
const (
AgentLeaseActive AgentLeaseState = "ACTIVE"
AgentLeaseExpired AgentLeaseState = "EXPIRED"
)
type AgentLease struct {
ID AgentLeaseID
RunID AgentRunID
WorkerID string
FencingToken AgentFencingToken
State AgentLeaseState
AcquiredAt time.Time
ExpiresAt time.Time
}
type AgentCallbackKind string
const (
AgentCallbackHeartbeat AgentCallbackKind = "HEARTBEAT"
AgentCallbackCheckpoint AgentCallbackKind = "CHECKPOINT"
AgentCallbackResult AgentCallbackKind = "RESULT"
AgentCallbackCancelAcknowledged AgentCallbackKind = "CANCEL_ACKNOWLEDGED"
)
type AgentCallback struct {
RunID AgentRunID
WorkerID string
FencingToken AgentFencingToken
Sequence uint64
Kind AgentCallbackKind
ObservedAt time.Time
}
type IndependentVerification struct {
ProducerRunID AgentRunID
VerifierRunID AgentRunID
VerifierIdentity string
ArtifactID string
TestEvidenceRef string
Verified bool
Summary string
}
type AgentDispatchIntent struct {
DispatchID string
IdempotencyKey string
DelegationID DelegationID
RunID AgentRunID
ImplementationID string
ImplementationRevision int64
SourceRevision string
WorktreeID string
VerificationPlanRef string
CreatedAtUnixMillis int64
}
@@ -0,0 +1,43 @@
BEGIN;
SET ROLE work_role;
CREATE TABLE IF NOT EXISTS work.agent_dispatch_intents (
dispatch_id TEXT PRIMARY KEY,
idempotency_key TEXT NOT NULL UNIQUE,
delegation_id TEXT NOT NULL,
run_id TEXT NOT NULL,
implementation_id TEXT NOT NULL,
implementation_revision BIGINT NOT NULL,
source_revision TEXT NOT NULL,
worktree_id TEXT NOT NULL,
verification_plan_ref TEXT NOT NULL,
created_at_unix_millis BIGINT NOT NULL,
CHECK (dispatch_id <> ''), CHECK (idempotency_key <> ''), CHECK (delegation_id <> ''),
CHECK (run_id <> ''), CHECK (implementation_id <> ''), CHECK (implementation_revision > 0),
CHECK (source_revision <> ''), CHECK (worktree_id <> ''), CHECK (verification_plan_ref <> ''),
CHECK (created_at_unix_millis > 0)
);
CREATE TABLE IF NOT EXISTS work.agent_leases (
lease_id TEXT PRIMARY KEY,
run_id TEXT NOT NULL UNIQUE,
worker_id TEXT NOT NULL,
fencing_token BIGINT NOT NULL,
state TEXT NOT NULL,
acquired_at_unix_millis BIGINT NOT NULL,
expires_at_unix_millis BIGINT NOT NULL,
CHECK (lease_id <> ''), CHECK (run_id <> ''), CHECK (worker_id <> ''),
CHECK (fencing_token > 0), CHECK (state IN ('ACTIVE','EXPIRED')),
CHECK (acquired_at_unix_millis > 0), CHECK (expires_at_unix_millis > acquired_at_unix_millis)
);
CREATE TABLE IF NOT EXISTS work.agent_callback_heads (
run_id TEXT PRIMARY KEY,
last_sequence BIGINT NOT NULL DEFAULT 0,
updated_at_unix_millis BIGINT NOT NULL,
CHECK (run_id <> ''), CHECK (last_sequence >= 0), CHECK (updated_at_unix_millis > 0)
);
RESET ROLE;
COMMIT;
+13
View File
@@ -84,6 +84,19 @@ services:
volumes:
- assistant_core_secrets:/run/secrets:ro
agent-canary:
profiles: ["agent-canary"]
build: { context: .., dockerfile: deploy/images/runtime.Dockerfile, args: { TARGET: agent-canary, SOURCE_REVISION: "${ASSISTANT_SOURCE_REVISION:-unknown}" } }
restart: "no"
command: ["-enable-canary", "-postgres"]
environment:
POSTGRES_HOST: postgres
POSTGRES_PORT: "5432"
POSTGRES_DATABASE: ${POSTGRES_DB:-assistant}
WORK_POSTGRES_USER: work_role
WORK_POSTGRES_PASSWORD: ${WORK_DB_PASSWORD}
depends_on: { postgres: { condition: service_healthy } }
continuity-runtime:
build: { context: .., dockerfile: deploy/images/runtime.Dockerfile, args: { TARGET: continuity-runtime, SOURCE_REVISION: "${ASSISTANT_SOURCE_REVISION:-unknown}" } }
restart: unless-stopped
+6 -1
View File
@@ -4,10 +4,15 @@ set -eu
backup_root="${ASSISTANT_BACKUP_DIR:-/home/m/assistant-backups/postgres}"
verify_script="${ASSISTANT_RESTORE_VERIFY_SCRIPT:-/home/m/assistant-ops/verify-postgres-restore.sh}"
latest="$(find "$backup_root" -mindepth 1 -maxdepth 1 -type d ! -name '.*' -printf '%f\n' | sort | tail -n 1)"
latest="$(find "$backup_root" -mindepth 1 -maxdepth 1 -type d -regextype posix-extended -regex '.*/[0-9]{8}T[0-9]{6}Z$' -printf '%f\n' | sort | tail -n 1)"
if [ -z "$latest" ]; then
echo "No completed PostgreSQL backup found in $backup_root" >&2
exit 1
fi
case "$latest" in
[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]T[0-9][0-9][0-9][0-9][0-9][0-9]Z) ;;
*) echo "Selected backup directory has invalid timestamp: $latest" >&2; exit 1 ;;
esac
exec "$verify_script" "$backup_root/$latest"
+1 -1
View File
@@ -45,7 +45,7 @@ $cipherPartial = $null
$manifestPartial = $null
try {
$latest = ((& ssh.exe $SshHost "find '$RemoteBackupRoot' -mindepth 1 -maxdepth 1 -type d ! -name '.*' -printf '%f\n' | sort | tail -n1") -join '').Trim()
$latest = ((& ssh.exe $SshHost "find '$RemoteBackupRoot' -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | grep -E '^[0-9]{8}T[0-9]{6}Z$' | sort | tail -n1") -join '').Trim()
if ($LASTEXITCODE -ne 0 -or $latest -notmatch '^\d{8}T\d{6}Z$') { throw 'No valid completed production backup found' }
New-Item -ItemType Directory -Force -Path $NasBackupRoot | Out-Null
+2 -2
View File
@@ -1,5 +1,5 @@
{
"task_file": "dev/tasks/DEV-000096-builtin-cognitive-usage-telemetry.json",
"task_id": "DEV-000096",
"task_file": "dev/tasks/DEV-000128-agent-canary-compose-profile.json",
"task_id": "DEV-000128",
"task_revision": 1
}
@@ -0,0 +1,34 @@
{
"task_id": "DEV-000098",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "b439a813e5932b1774cb3b6c2364c6cac3891314",
"risk_tier": "E",
"intent": "Implement Governance Sentinel V2 so task packets are fully validated and required governance evidence is enforced before promotion.",
"requirement_ids": ["WP-000", "CONSTITUTION-1.3.27", "CONSTITUTION-1.3.28"],
"allowed_change_scope": {
"modules": [],
"paths": ["cmd/devctl/**", ".gitea/workflows/ci.yml", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000098-governance-sentinel-v2.json"]
},
"forbidden_change_scope": {
"paths": ["architecture/ARCHITECTURE_CONSTITUTION.md", "architecture/CORE_SEMANTIC_MODEL_V1.md", "security/**", "database/**", "deploy/**", "modules/**", "contracts/**", "adapters/**", "platform/**"]
},
"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": ["Architecture registry", "Accepted ADR files", "Codex Task Packet JSON", "Git repository state"], "may_change": []},
"semantic_owner": "none-architecture-guardrail",
"canonical_state": [],
"state_transitions": ["Task packet validation returns pass or typed gap"],
"side_effects_and_privileges": ["Reads repository metadata and required test commands; no external mutation or secret access"],
"persistence_and_migrations": ["No database schema or migration changes"],
"failure_retry_restart": ["Malformed, incomplete, conflicting or unverifiable packets fail closed; checks are deterministic and rerunnable"],
"assumptions": ["ADR-0018 is the current accepted architecture revision", "Independent verification remains a CI/review responsibility", "docker-test remains prohibited"],
"acceptance_scenarios": ["Full packet fields are strictly parsed", "Allowed and forbidden scope overlap is rejected", "Architecture revision existence and base ancestry are checked", "Architecture deltas, dependencies, contracts, privileges and required tests are checked", "Tier E does not bypass validation", "CI executes the required governance checks"],
"required_tests": ["go test ./cmd/devctl", "go run ./cmd/devctl/independent", "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 vet ./...", "git diff --check"],
"terminal_gap_states": ["BLOCKED_BY_ARCHITECTURE_GAP", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_CONFLICT", "BLOCKED_BY_MISSING_CAPABILITY"]
}
@@ -0,0 +1,26 @@
{
"task_id": "DEV-000099",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "ff33a62",
"risk_tier": "E",
"intent": "Consolidate active task lineage, project status and architecture registry mirrors after the audit without changing runtime behavior or canonical ownership.",
"requirement_ids": ["WP-001", "ADR-0017", "Cold-agent governance reproducibility"],
"allowed_change_scope": {
"modules": [],
"paths": ["architecture/architecture-registry.yaml", "architecture/architecture-registry.json", "architecture/architecture-registry.md", "docs/PROJECT_STATUS.md", "docs/GOVERNANCE_LINEAGE_DECISION_2026-08-22.md", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000099-governance-consolidation.json", "cmd/devctl/**"]
},
"forbidden_change_scope": {"paths": ["architecture/ARCHITECTURE_CONSTITUTION.md", "architecture/adrs/**", "security/**", "database/**", "deploy/**", "modules/**", "contracts/**", "adapters/**", ".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": ["ADR-0017 ownership decision", "Architecture registry YAML/JSON", "Active task pointer", "Project status evidence"], "may_change": []},
"semantic_owner": "none-governance-projection",
"canonical_state": [],
"state_transitions": ["DEV-000098 completed -> DEV-000099 active -> DEV-000099 completed"],
"side_effects_and_privileges": ["Repository documentation and governance metadata only; no runtime or production mutation"],
"persistence_and_migrations": ["No database schema or migration changes"],
"failure_retry_restart": ["Historical checkpoints remain append-only; ambiguous lineage is recorded as a decision rather than silently rewritten"],
"assumptions": ["ADR-0017 remains accepted", "YAML and JSON registry are semantically equivalent authority mirrors", "docker-test remains prohibited"],
"acceptance_scenarios": ["Active pointer names the current packet and revision", "DEV-000073 stale lineage is explicitly resolved", "PROJECT_STATUS starts with current audit/WP-001 evidence while preserving historical checkpoints", "YAML/JSON/human registry agree on ADR-0017 ownership", "Cold agent can reconstruct current ownership and task ancestry from repository files"],
"required_tests": ["go test ./cmd/devctl", "go run ./cmd/devctl/independent", "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 vet ./...", "git diff --check", "registry YAML/JSON parity"],
"terminal_gap_states": ["BLOCKED_BY_ARCHITECTURE_GAP", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_CONFLICT", "BLOCKED_BY_MISSING_CAPABILITY"]
}
@@ -0,0 +1,22 @@
{
"task_id": "DEV-000100",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "8687c9b",
"risk_tier": "E",
"intent": "Repair production backup and restore selection so only complete timestamped backups are eligible, and make provenance drift degrade readiness.",
"requirement_ids": ["WP-002", "INV-CONT-001", "Production recovery must be reversible"],
"allowed_change_scope": {"modules": [], "paths": ["deploy/verify-latest-postgres-backup.sh", "deploy/windows/Sync-PostgresBackupToNas.ps1", "deploy/windows/Test-DomovoyProductionHealth.ps1", "tests/production_recovery/**", "docs/DEPLOYMENT.md", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000100-production-recovery-repair.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "contracts/**", "modules/**", "cmd/**", "adapters/**", "platform/**", ".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": ["Production backup directory contract", "Deployment manifest evidence", "Production health projection"], "may_change": []},
"semantic_owner": "none-operational-recovery", "canonical_state": [],
"state_transitions": ["ineligible backup -> ignored", "verified backup -> restore candidate", "provenance/backup drift -> degraded health"],
"side_effects_and_privileges": ["Local script changes are read-only until explicit production verification; no secret bytes are logged"],
"persistence_and_migrations": ["No database schema or migration changes"],
"failure_retry_restart": ["Partial/temp/pre-* directories remain untouched and are ignored; restore checks use isolated temporary PostgreSQL only"],
"assumptions": ["Production host is docker.cin.su via docker-prod/docker SSH configuration", "docker-test remains prohibited"],
"acceptance_scenarios": ["pre-*, partial and temp directories are ignored", "latest timestamp directory is selected deterministically", "missing/hash-invalid backup is rejected", "production health is degraded on backup or deployment provenance drift", "restore verification remains isolated and cleanup-safe"],
"required_tests": ["shell fixture tests", "PowerShell parser", "go run ./cmd/devctl/independent", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go test ./...", "go vet ./...", "git diff --check", "production read-only health evidence"],
"terminal_gap_states": ["BLOCKED_BY_ARCHITECTURE_GAP", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,22 @@
{
"task_id": "DEV-000101",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "15c9517",
"risk_tier": "E",
"intent": "Add dependency-aware readiness projections with typed degraded reasons for core, continuity, commit, cognitive and lifeboat runtimes without changing canonical ownership.",
"requirement_ids": ["WP-003", "Typed degraded dependency reasons", "Frontend health distinct from product readiness"],
"allowed_change_scope": {"modules": ["development-governance"], "paths": ["cmd/core-runtime/**", "cmd/continuity-runtime/**", "cmd/commit-runtime/**", "cmd/cognitive-worker/**", "runtime/health/**", "tests/readiness/**", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000101-dependency-aware-readiness.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "contracts/**", "modules/**", "adapters/**", ".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 runtime health handlers", "Existing owner-local repository health", "Existing cognitive capability health"], "may_change": []},
"semantic_owner": "none-readiness-projection", "canonical_state": [],
"state_transitions": ["dependency available -> ready", "dependency unavailable/unknown -> typed degraded readiness"],
"side_effects_and_privileges": ["Read-only dependency probes; no canonical mutation, external mutation or credential expansion"],
"persistence_and_migrations": ["No database schema or migration changes"],
"failure_retry_restart": ["Probe timeout is UNKNOWN/DEGRADED, not false healthy; recovery returns ready on next probe"],
"assumptions": ["Existing /healthz endpoints remain backward compatible", "Frontend health and product readiness are separate projections", "docker-test remains prohibited"],
"acceptance_scenarios": ["Stopping each dependency yields the correct typed degraded reason", "DB owner connectivity is represented without cross-owner writes", "cognitive binding/model freshness and lifeboat availability are visible", "recovery returns readiness without restart", "health endpoint does not claim product readiness"],
"required_tests": ["go test ./...", "go vet ./...", "go run ./cmd/devctl/independent", "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", "git diff --check", "readiness dependency-stop integration tests"],
"terminal_gap_states": ["BLOCKED_BY_ARCHITECTURE_GAP", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000102",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "3f2b008",
"risk_tier": "E",
"intent": "Promote the dependency-aware readiness implementation to a controlled production canary with explicit rollback evidence, without changing deployment topology or source contracts.",
"requirement_ids": ["WP-003", "Controlled canary promotion", "Production rollback evidence"],
"allowed_change_scope": {"modules": ["deployment-governance"], "paths": ["dev/ACTIVE_TASK.json", "dev/tasks/DEV-000102-readiness-canary-promotion.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "contracts/**", "modules/**", "adapters/**", ".gitea/**", "cmd/**", "runtime/**"]},
"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 production compose topology", "Existing runtime health endpoints", "Existing production backup and deployment verification"], "may_change": []},
"semantic_owner": "none-deployment-canary",
"canonical_state": [],
"state_transitions": ["built candidate -> canary verified", "canary failure -> rollback to recorded production revision"],
"side_effects_and_privileges": ["Controlled production container replacement only after build/config verification; no schema, credential, or external data mutation"],
"persistence_and_migrations": ["No database schema or migration changes"],
"failure_retry_restart": ["Rollback target is production revision b439a813e5932b1774cb3b6c2364c6cac3891314 and recorded core image digest sha256:4fb09995df8efbbb02359e30171f593f9cf9b90489d4ac0bf85a62ed2504ac92"],
"assumptions": ["Production control plane is docker-prod/docker.cin.su only", "Canary is limited to readiness-capable runtime services", "Existing secrets and persistent volumes remain unchanged"],
"acceptance_scenarios": ["Candidate images build from 3f2b008", "Compose configuration validates without secret disclosure", "Canary readiness returns HTTP 200 with dependency checks", "Existing health and HTTPS checks remain healthy", "Rollback command and pre-canary image identity are recorded"],
"required_tests": ["go test ./...", "go vet ./...", "go run ./cmd/devctl independent", "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", "git diff --check", "docker compose config on docker-prod", "production canary readiness and rollback verification"],
"terminal_gap_states": ["BLOCKED_BY_ARCHITECTURE_GAP", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000103",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "6fb9184",
"risk_tier": "E",
"intent": "Prepare the proposed ADR-0019 Truth V1 boundary for review without implementing runtime, schema, connector, source or authority changes.",
"requirement_ids": ["WP-100", "Observation/Claim/BeliefAssessment", "Source provenance freshness conflict"],
"allowed_change_scope": {"modules": ["epistemics-governance"], "paths": ["docs/proposals/ADR-0019-epistemics-truth-v1.md", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000103-epistemics-truth-v1-proposal.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "contracts/**", "database/**", "migrations/**", "modules/**", "cmd/**", "runtime/**", "adapters/**", "deploy/**", ".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": ["ADR-0016 Epistemics experience/reflection ownership", "ADR-0017 typed provenance references", "Core semantic model ownership boundaries"], "may_change": ["Proposed ADR-0019 only; no accepted contract changes"]},
"semantic_owner": "epistemics-proposal-only",
"canonical_state": [],
"state_transitions": ["observation/source evidence -> claim assessment", "assessment -> supported/corroborated/conflicted/stale/retracted/unknown", "correction -> superseding assessment"],
"side_effects_and_privileges": ["No runtime side effects, external mutation, connector access, credential access or authority expansion"],
"persistence_and_migrations": ["No schema, migration or retention job changes"],
"failure_retry_restart": ["Proposal failure leaves no canonical state; accepted implementation must preserve append-only evidence and rebuildable projections"],
"assumptions": ["Proposal is reviewed before acceptance", "Epistemics remains the only proposed truth-evidence owner", "Secrets and unapproved personal data are fail-closed"],
"acceptance_scenarios": ["Owner boundaries and typed references are explicit", "Unknown, assumed, stale and conflicted states cannot silently become facts", "Correction and supersession preserve history", "Freshness and coverage are separate from confidence", "No implementation or architecture registry delta is introduced"],
"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", "proposal scope and required-section review"],
"terminal_gap_states": ["BLOCKED_BY_ARCHITECTURE_GAP", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000104",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "339f6a6",
"risk_tier": "E",
"intent": "Prepare the proposed ADR-0020 Agent Execution Plane boundary for review without implementing dispatcher, worker, schema, callback or deployment changes.",
"requirement_ids": ["WP-300", "Durable dispatch lease heartbeat", "Independent verification and truthful UNKNOWN outcome"],
"allowed_change_scope": {"modules": ["work-execution-governance"], "paths": ["docs/proposals/ADR-0020-agent-execution-plane-v1.md", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000104-agent-execution-plane-proposal.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "contracts/**", "database/**", "migrations/**", "modules/**", "cmd/**", "runtime/**", "adapters/**", "deploy/**", ".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": ["ADR-0017 General Agent Delegation and Codex Bridge V1", "Proposed ADR-0019 Epistemics Truth V1", "Core semantic model Work/Capability/Artifact/Continuity boundaries"], "may_change": ["Proposed ADR-0020 only; no accepted contract changes"]},
"semantic_owner": "work-execution-plane-proposal-only",
"canonical_state": [],
"state_transitions": ["dispatch -> leased -> running -> result -> independently verified", "lease expiry/callback uncertainty -> UNKNOWN or RECOVERING", "cancel request -> acknowledged cancellation or typed unknown"],
"side_effects_and_privileges": ["No runtime side effects, external mutation, connector access, credential access or authority expansion"],
"persistence_and_migrations": ["No schema, migration, queue, worker or lease implementation changes"],
"failure_retry_restart": ["Retry creates a new run and preserves history; late or duplicate callbacks cannot become authoritative"],
"assumptions": ["Work remains canonical owner of durable delegation execution", "Artifact remains canonical owner of attestation metadata", "Production deployment remains outside this proposal"],
"acceptance_scenarios": ["Worker identity and dispatch idempotency are explicit", "Lease fencing and heartbeat expiry are explicit", "Callback authentication/replay handling is explicit", "Cancellation/retry/UNKNOWN semantics preserve history", "Independent verification gates Work completion", "No implementation or registry delta is introduced"],
"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", "proposal scope and required-section review"],
"terminal_gap_states": ["BLOCKED_BY_ARCHITECTURE_GAP", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000105",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "82ee6e5",
"risk_tier": "E",
"intent": "Prepare the proposed ADR-0021 Multi-Agent Work Graph boundary for review without implementing a scheduler, graph schema, workers or production changes.",
"requirement_ids": ["WP-400", "DAG revision and budgets", "Fan-out/fan-in conflict and independent verification"],
"allowed_change_scope": {"modules": ["work-graph-governance"], "paths": ["docs/proposals/ADR-0021-multi-agent-work-graph-v1.md", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000105-multi-agent-work-graph-proposal.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "contracts/**", "database/**", "migrations/**", "modules/**", "cmd/**", "runtime/**", "adapters/**", "deploy/**", ".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": ["ADR-0017 delegation ownership", "Proposed ADR-0020 Agent Execution Plane", "Proposed ADR-0019 Truth V1 provenance", "Core Work/Capability/Artifact/Continuity ownership"], "may_change": ["Proposed ADR-0021 only; no accepted contract changes"]},
"semantic_owner": "work-graph-proposal-only",
"canonical_state": [],
"state_transitions": ["graph draft -> admitted -> running -> verifying -> completed/partial/failed", "node prerequisite blocked -> ready -> leased/running -> result or recovery", "candidate disagreement -> conflict set -> verified synthesis or unresolved conflict"],
"side_effects_and_privileges": ["No runtime side effects, external mutation, connector access, credential access or authority expansion"],
"persistence_and_migrations": ["No graph schema, scheduler, queue, lease or migration implementation changes"],
"failure_retry_restart": ["Graph revisions are immutable; restart/replay uses durable history; unknown outcomes block unsafe replay"],
"assumptions": ["Work remains canonical graph owner", "Roles are capability profiles without authority", "Independent verification is required before Work completion"],
"acceptance_scenarios": ["Cycles are rejected before admission", "Budgets cover graph/user/resource concurrency and attempts", "Fan-out/fan-in and partial failure preserve attribution", "Cancellation/pause/resume/restart semantics are explicit", "Conflicts retain dissent and verifier identity is distinct", "No implementation or registry delta is introduced"],
"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", "proposal scope and required-section review"],
"terminal_gap_states": ["BLOCKED_BY_ARCHITECTURE_GAP", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000106",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "08deab8",
"risk_tier": "E",
"intent": "Prepare the proposed ADR-0022 Governed Self-Development boundary for review without implementing proposal generation, factories, candidate activation or production changes.",
"requirement_ids": ["WP-600", "Governed development control plane", "Candidate canary promotion rollback emergency stop"],
"allowed_change_scope": {"modules": ["governed-development-proposal"], "paths": ["docs/proposals/ADR-0022-governed-self-development-v1.md", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000106-governed-self-development-proposal.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "contracts/**", "database/**", "migrations/**", "modules/**", "cmd/**", "runtime/**", "adapters/**", "deploy/**", ".gitea/**", "skills/**"]},
"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": ["ADR-0016 Experience/Reflection/Skill Evolution", "Proposed ADR-0019 Truth V1 evidence", "Proposed ADR-0020 Agent Execution Plane", "Proposed ADR-0021 Work Graph", "existing Authority/Commit/Artifact boundaries"], "may_change": ["Proposed ADR-0022 only; no accepted contract changes"]},
"semantic_owner": "governed-development-proposal-only",
"canonical_state": [],
"state_transitions": ["evidence -> proposal -> sandbox -> evaluation -> canary -> promotion/rollback/retirement", "failed/conflicted/incomplete evaluation -> blocked"],
"side_effects_and_privileges": ["No runtime side effects, source mutation, deployment, external mutation, credential access or authority expansion"],
"persistence_and_migrations": ["No proposal store, candidate artifact store, schema, migration or telemetry implementation changes"],
"failure_retry_restart": ["Candidate evidence and lineage survive rejection/rollback; emergency stop disables candidate selection without deleting history"],
"assumptions": ["Human governance remains promotion authority", "Protected paths and secret policies cannot be modified by the control plane", "New code/model/skill never receives authority automatically"],
"acceptance_scenarios": ["One failure cannot create a proposal", "Candidate lineage and isolated scope are mandatory", "Independent evaluation and verifier are explicit", "Canary and rollback preserve previous implementation and evidence", "Emergency stop blocks candidate traffic", "No implementation or registry delta is introduced"],
"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", "proposal scope and required-section review"],
"terminal_gap_states": ["BLOCKED_BY_ARCHITECTURE_GAP", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000107",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "3e0be62",
"risk_tier": "E",
"intent": "Implement a typed context compiler for relevance, sensitivity, provenance and byte budgets at the cognitive contract boundary.",
"requirement_ids": ["WP-101", "Typed context bundle", "Sensitivity/egress filtering and budget"],
"allowed_change_scope": {"modules": ["cognitive-contracts"], "paths": ["contracts/cognitive/**", "tests/context_compiler/**", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000107-context-compiler-v1.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "database/**", "migrations/**", "deploy/**", "security/**", "modules/**", "cmd/**", "runtime/**", "adapters/**", ".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 cognitive ContextExcerpt and Gateway request boundary", "Capability DataPolicy values"], "may_change": ["Cognitive context bundle/compiler contract"]},
"semantic_owner": "cognitive-context-contract",
"canonical_state": [],
"state_transitions": ["candidate excerpts -> selected typed context bundle", "budget/sensitivity decision -> included or omitted with reason"],
"side_effects_and_privileges": ["Pure deterministic compilation; no provider call, persistence, external mutation or credential access"],
"persistence_and_migrations": ["No database schema or migration changes"],
"failure_retry_restart": ["Compilation is deterministic and repeatable; empty permitted context remains a valid bundle"],
"assumptions": ["Gateway remains responsible for provider selection", "Secret and unknown excerpts are never emitted", "Provider identity remains invocation provenance"],
"acceptance_scenarios": ["Relevant excerpts are selected deterministically", "Duplicate and low-value excerpts do not consume budget", "Secret/unknown data is omitted", "External minimized policy excludes private data", "UTF-8 byte budget is respected", "Provider identity is not represented as persona"],
"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", "context compiler tests"],
"terminal_gap_states": ["BLOCKED_BY_ARCHITECTURE_GAP", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000108",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "879de3b",
"risk_tier": "E",
"intent": "Use the typed context compiler for local chat cognition with the canonical active-project focus, keeping external advisory context empty unless explicitly supplied by a future policy-aware path.",
"requirement_ids": ["WP-101", "Follow-up context", "Active project isolation"],
"allowed_change_scope": {"modules": ["creator-gateway", "cognitive-contracts"], "paths": ["cmd/creator-web/**", "contracts/cognitive/**", "tests/context_compiler/**", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000108-context-compiler-gateway-integration.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "database/**", "migrations/**", "deploy/**", "security/**", "modules/**", "runtime/**", "adapters/**", ".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": ["ContextBundle compiler from DEV-000107", "Core ChatMessageResponse active project fields", "Existing local cognitive gateway request"], "may_change": ["Creator gateway context assembly"]},
"semantic_owner": "creator-gateway-context-projection",
"canonical_state": [],
"state_transitions": ["canonical active project -> local context candidate -> compiled gateway context", "missing active project -> empty context"],
"side_effects_and_privileges": ["Context assembly is read-only; no persistence, provider selection, credential access or external mutation"],
"persistence_and_migrations": ["No database schema or migration changes"],
"failure_retry_restart": ["Compiler failure or absent focus yields empty context and preserves the existing chat fallback"],
"assumptions": ["External advisory remains message-only until a separate policy-aware context path exists", "Active project name is already canonicalized by Core", "Current project context is not reused for unrelated requests"],
"acceptance_scenarios": ["Local cognitive request receives the active project as provenance-bearing context", "No active project yields empty context", "External advisory receives no implicit private context", "Project context does not leak into a different saved response", "Existing creator gateway and full tests remain green"],
"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", "context compiler gateway integration tests"],
"terminal_gap_states": ["BLOCKED_BY_ARCHITECTURE_GAP", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000109",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "e3b55c9",
"risk_tier": "E",
"intent": "Add typed read-only research and citation evidence contracts for plans, source snapshots, excerpts and freshness/conflict state without connecting external sources.",
"requirement_ids": ["WP-102", "Source snapshot and citation resolver", "UNKNOWN/ASSUMPTION freshness conflict"],
"allowed_change_scope": {"modules": ["epistemics-research-contracts"], "paths": ["contracts/epistemics/**", "tests/research_contracts/**", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000109-research-citation-contracts.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "database/**", "migrations/**", "deploy/**", "security/**", "modules/**", "cmd/**", "runtime/**", "adapters/**", ".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": ["Proposed ADR-0019 Truth V1 evidence/provenance", "Existing Epistemics owner", "Existing Artifact typed reference boundary"], "may_change": ["Epistemics research/citation contract"]},
"semantic_owner": "epistemics-research-contract",
"canonical_state": [],
"state_transitions": ["research plan -> source snapshot -> excerpt -> citation", "source conflict/freshness result -> explicit claim status"],
"side_effects_and_privileges": ["Typed value validation only; no connector calls, external mutation, credentials or persistence"],
"persistence_and_migrations": ["No schema or migration changes"],
"failure_retry_restart": ["Unknown source/retrieval state is retained as UNKNOWN and cannot become a citation silently"],
"assumptions": ["Connectors and durable storage arrive in later tasks", "Citation references an exact excerpt identity and snapshot", "Research output is read-only evidence"],
"acceptance_scenarios": ["Plan identifies question, source classes and freshness policy", "Snapshot has content digest and retrieval timestamp", "Citation requires exact excerpt and source snapshot", "stale/conflicted/unknown states remain explicit", "assumption is distinguishable from observed evidence"],
"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", "research contract tests"],
"terminal_gap_states": ["BLOCKED_BY_ARCHITECTURE_GAP", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000110",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "dc7f7b3",
"risk_tier": "E",
"intent": "Add a deterministic citation resolver that requires a matching source, immutable snapshot and exact excerpt before producing citation evidence.",
"requirement_ids": ["WP-102", "Citation resolver", "No fabricated excerpts"],
"allowed_change_scope": {"modules": ["epistemics-research-contracts"], "paths": ["contracts/epistemics/**", "tests/research_contracts/**", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000110-citation-resolver.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "database/**", "migrations/**", "deploy/**", "security/**", "modules/**", "cmd/**", "runtime/**", "adapters/**", ".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": ["DEV-000109 research/citation contracts", "Epistemics source snapshot and excerpt types"], "may_change": ["Citation resolution helper"]},
"semantic_owner": "epistemics-research-contract",
"canonical_state": [],
"state_transitions": ["source/snapshot/excerpt evidence -> resolved citation", "mismatched evidence -> resolution error"],
"side_effects_and_privileges": ["Pure deterministic validation; no connector calls, persistence, external mutation or credential access"],
"persistence_and_migrations": ["No schema or migration changes"],
"failure_retry_restart": ["Resolution is repeatable; failed resolution produces no citation"],
"assumptions": ["Source snapshots are immutable identifiers", "Exact excerpt offsets and text are supplied by a connector later", "Unknown/stale/conflicted evidence stays visible"],
"acceptance_scenarios": ["Matching source chain resolves", "Wrong source/snapshot/excerpt is rejected", "Invalid offsets are rejected", "Citation preserves stale/conflict/assumed state", "No fabricated excerpt can resolve"],
"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", "citation resolver tests"],
"terminal_gap_states": ["BLOCKED_BY_ARCHITECTURE_GAP", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000111",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "aec63de",
"risk_tier": "E",
"intent": "Record read-only production hardware and model inventory evidence for WP-200 without changing model bindings or deployment.",
"requirement_ids": ["WP-200", "Hardware/model inventory", "GPU/VRAM and protected runtime evidence"],
"allowed_change_scope": {"modules": ["model-qualification-evidence"], "paths": ["docs/evidence/WP-200-HARDWARE-MODEL-INVENTORY-2026-08-22.md", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000111-hardware-model-inventory-evidence.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "database/**", "migrations/**", "deploy/**", "security/**", "modules/**", "cmd/**", "runtime/**", "adapters/**", ".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": ["WP-200 audit requirements", "Production docker-prod read-only inventory"], "may_change": []},
"semantic_owner": "model-qualification-evidence",
"canonical_state": [],
"state_transitions": ["unqualified inventory -> evidence recorded -> qualification decision pending"],
"side_effects_and_privileges": ["Read-only SSH/docker inspection only; no model activation, deployment, credential access or production mutation"],
"persistence_and_migrations": ["Evidence document only; no database changes"],
"failure_retry_restart": ["Missing GPU/model evidence remains UNKNOWN and blocks heavy qualification"],
"assumptions": ["docker-prod/docker.cin.su is the only production target", "GPU absence on this host does not prove absence on every external host", "Model qualification requires separate holdout/latency/license evidence"],
"acceptance_scenarios": ["CPU/RAM/storage/network inventory recorded", "GPU/VRAM/runtime evidence recorded", "Healthy cognitive/lifeboat containers identified without secret output", "Heavy model remains unqualified until GPU/license evidence exists"],
"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", "production read-only inventory commands"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000112",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "93bc4a0",
"risk_tier": "E",
"intent": "Add an immutable model qualification manifest contract and validation for holdout, latency, resource, license and restart evidence without activating a model.",
"requirement_ids": ["WP-201", "Qualification evidence manifest", "No activation from model name alone"],
"allowed_change_scope": {"modules": ["capability-model-qualification"], "paths": ["contracts/capability/**", "tests/model_qualification/**", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000112-model-qualification-manifest.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "database/**", "migrations/**", "deploy/**", "security/**", "modules/**", "cmd/**", "runtime/**", "adapters/**", ".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 Capability model descriptor/evaluation types", "WP-201 audit acceptance"], "may_change": ["Capability qualification evidence manifest contract"]},
"semantic_owner": "capability-model-qualification",
"canonical_state": [],
"state_transitions": ["candidate model -> measured -> evidence complete/incomplete -> qualification decision pending"],
"side_effects_and_privileges": ["Pure validation; no provider call, model activation, deployment, credential access or external mutation"],
"persistence_and_migrations": ["No schema or migration changes"],
"failure_retry_restart": ["Incomplete or stale manifest remains unqualified; rerun creates a new manifest revision"],
"assumptions": ["A model digest and provider revision identify the tested artifact", "License/data policy evidence is explicit", "Qualification is separate from activation"],
"acceptance_scenarios": ["Manifest binds model revision and digest", "Holdout corpus and all required dimensions are recorded", "Latency/concurrency/resource/restart evidence is present", "License and data policy are present", "Incomplete manifest cannot claim validated"],
"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", "model qualification manifest tests"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000113",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "1f01b70",
"risk_tier": "E",
"intent": "Add a deterministic qualification gate that maps complete supported evidence to VALIDATED and incomplete/unsupported evidence to REJECTED without changing activation.",
"requirement_ids": ["WP-201", "Qualification decision", "GPU/resource evidence cannot be inferred"],
"allowed_change_scope": {"modules": ["capability-model-qualification"], "paths": ["contracts/capability/**", "tests/model_qualification/**", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000113-model-qualification-gate.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "database/**", "migrations/**", "deploy/**", "security/**", "modules/**", "cmd/**", "runtime/**", "adapters/**", ".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": ["QualificationManifest from DEV-000112", "Existing QualificationState and EvaluationResult values"], "may_change": ["Qualification gate projection"]},
"semantic_owner": "capability-model-qualification",
"canonical_state": [],
"state_transitions": ["complete supported manifest -> VALIDATED", "incomplete/unsupported/unknown manifest -> REJECTED"],
"side_effects_and_privileges": ["Pure validation; no binding activation, provider call, deployment, credential access or external mutation"],
"persistence_and_migrations": ["No schema or migration changes"],
"failure_retry_restart": ["Gate is deterministic; a new manifest revision is required after new measurements"],
"assumptions": ["Qualification and activation remain separate lifecycle decisions", "All required dimensions are mandatory", "Model names never substitute for evidence"],
"acceptance_scenarios": ["All supported measurements produce VALIDATED", "Unsupported resource/restart result produces REJECTED", "Unknown measurement produces REJECTED", "Gate does not mutate binding activation"],
"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", "qualification gate tests"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000114",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "a65ee37",
"risk_tier": "E",
"intent": "Add deterministic AgentRun liveness assessment for stale-run recovery without requeueing or mutating production records.",
"requirement_ids": ["WP-302", "Heartbeat timeout", "Typed stale evidence"],
"allowed_change_scope": {"modules": ["work-recovery"], "paths": ["modules/work/domain/**", "contracts/work/**", "tests/work_recovery/**", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000114-run-liveness-assessment.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "database/**", "migrations/**", "deploy/**", "security/**", "cmd/**", "runtime/**", "adapters/**", ".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 Work AgentRun lifecycle", "ADR-0017 delegation ownership"], "may_change": ["Work recovery liveness projection"]},
"semantic_owner": "work-recovery",
"canonical_state": [],
"state_transitions": ["run timestamp evidence -> LIVE/STALE/INVALID", "stale evidence -> recovery decision pending"],
"side_effects_and_privileges": ["Pure assessment; no requeue, resume, cancellation, persistence or external mutation"],
"persistence_and_migrations": ["No schema or migration changes"],
"failure_retry_restart": ["Clock/invalid timestamps produce INVALID rather than stale or healthy"],
"assumptions": ["UpdatedAt is the latest durable heartbeat/checkpoint projection available", "Recovery policy chooses the next action separately", "RUNNING alone is not proof of liveness"],
"acceptance_scenarios": ["Recent run is LIVE", "Old running run is STALE with age", "Completed/blocked run is not stale", "Invalid clock/timestamp is INVALID", "No production state changes"],
"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", "run liveness tests"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000115",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "e83cf35",
"risk_tier": "E",
"intent": "Add a deterministic cognitive admission policy for concurrency, accepted/hour, deadline and input/output budgets without changing provider routing or bindings.",
"requirement_ids": ["WP-203", "Bounded concurrency/rate limits", "Explainable admission decision"],
"allowed_change_scope": {"modules": ["cognitive-admission"], "paths": ["contracts/capability/**", "modules/capability/domain/**", "tests/cognitive_admission/**", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000115-cognitive-admission-control.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "database/**", "migrations/**", "deploy/**", "security/**", "cmd/**", "runtime/**", "adapters/**", ".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 cognitive capability registry and LimitPolicyRef", "ADR-0006 bounded advisory boundary"], "may_change": ["Typed admission request/policy/decision contract"]},
"semantic_owner": "capability-cognitive-admission",
"canonical_state": [],
"state_transitions": ["request + policy + observed counters -> admitted or typed rejection"],
"side_effects_and_privileges": ["Pure policy evaluation; no provider call, queue, persistence, binding mutation or external mutation"],
"persistence_and_migrations": ["No schema or migration changes"],
"failure_retry_restart": ["Rejected requests carry a reason; retry requires a fresh policy evaluation"],
"assumptions": ["Counters are supplied by a later durable admission implementation", "Policy does not choose or activate providers", "Deadline and budgets are request requirements"],
"acceptance_scenarios": ["Concurrency limit rejects excess request", "Accepted/hour limit rejects excess request", "Deadline and byte budget reject invalid request", "Admitted decision exposes reason and policy reference", "Simple/normal/heavy complexity remains a routing input, not authority"],
"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", "admission policy tests"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000116",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "cb92f95",
"risk_tier": "E",
"intent": "Add typed durable-agent lease and fencing-token transitions for worker start/heartbeat without implementing a dispatcher or persistence adapter.",
"requirement_ids": ["WP-301", "Worker lease confirms actual start", "Heartbeat fencing"],
"allowed_change_scope": {"modules": ["work-agent-execution"], "paths": ["contracts/work/**", "modules/work/domain/**", "tests/agent_execution/**", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000116-agent-lease-fencing.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "database/**", "migrations/**", "deploy/**", "security/**", "cmd/**", "runtime/**", "adapters/**", ".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": ["ADR-0017 delegation/run ownership", "Proposed ADR-0020 Agent Execution Plane", "Existing Work AgentRun identity"], "may_change": ["Work agent lease/fencing contract"]},
"semantic_owner": "work-agent-execution",
"canonical_state": [],
"state_transitions": ["dispatch -> lease acquired -> heartbeat renewed", "wrong worker/token/expired lease -> rejected callback"],
"side_effects_and_privileges": ["Pure transition helper; no queue, persistence, worker process, credential access or external mutation"],
"persistence_and_migrations": ["No schema or migration changes"],
"failure_retry_restart": ["Fencing token changes on a new lease; late old-token heartbeat is rejected"],
"assumptions": ["A durable repository will persist the lease atomically later", "Lease acquisition is separate from process-local execution", "Worker identity is authenticated by a later adapter boundary"],
"acceptance_scenarios": ["Valid worker acquires lease", "Matching worker renews before expiry", "Wrong worker/token rejected", "Expired lease cannot renew", "New token fences old run"],
"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", "agent lease tests"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000117",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "9766e53",
"risk_tier": "E",
"intent": "Add typed callback envelope validation for authenticated worker identity, fencing token, monotonic sequence and lease freshness.",
"requirement_ids": ["WP-301", "Callback auth binding", "Forged/out-of-order result rejection"],
"allowed_change_scope": {"modules": ["work-agent-execution"], "paths": ["contracts/work/**", "modules/work/domain/**", "tests/agent_execution/**", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000117-agent-callback-envelope.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "database/**", "migrations/**", "deploy/**", "security/**", "cmd/**", "runtime/**", "adapters/**", ".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": ["AgentLease/fencing contract from DEV-000116", "ADR-0020 callback ordering boundary"], "may_change": ["Agent callback envelope contract"]},
"semantic_owner": "work-agent-execution",
"canonical_state": [],
"state_transitions": ["lease + callback envelope -> accepted callback", "wrong identity/token/sequence/expiry -> rejected callback"],
"side_effects_and_privileges": ["Pure validation; no transport, persistence, result mutation or external mutation"],
"persistence_and_migrations": ["No schema or migration changes"],
"failure_retry_restart": ["Duplicate and late callbacks are rejected by sequence/fencing; accepted sequence is persisted later by adapter"],
"assumptions": ["Transport authenticates worker identity before this validation", "Sequence is monotonic within a run and lease token", "Result verification remains a separate step"],
"acceptance_scenarios": ["Matching heartbeat accepted", "Wrong worker/token rejected", "Duplicate/out-of-order sequence rejected", "Expired lease rejected", "Callback run mismatch rejected"],
"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", "agent callback tests"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000118",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "a6eb4ab",
"risk_tier": "E",
"intent": "Add typed artifact attestation for agent-produced evidence with digest, source/worktree, producer run and test provenance.",
"requirement_ids": ["WP-301", "Artifact attestation", "Independent evidence refs"],
"allowed_change_scope": {"modules": ["artifact-agent-evidence"], "paths": ["contracts/artifact/**", "tests/agent_execution/**", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000118-agent-artifact-attestation.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "database/**", "migrations/**", "deploy/**", "security/**", "cmd/**", "runtime/**", "adapters/**", ".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": ["ADR-0017 Artifact ownership", "ADR-0020 agent evidence attestation", "Existing AgentRun identity"], "may_change": ["Artifact agent attestation contract"]},
"semantic_owner": "artifact-agent-evidence",
"canonical_state": [],
"state_transitions": ["agent output -> attested artifact evidence", "missing provenance -> non-attested result"],
"side_effects_and_privileges": ["Pure validation; no filesystem access, merge, deployment, credential access or external mutation"],
"persistence_and_migrations": ["No schema or migration changes"],
"failure_retry_restart": ["Attestation is immutable; changed digest/source requires a new attestation"],
"assumptions": ["Artifact storage computes or verifies digest later", "Producer run and source revision are durable refs", "Verifier remains distinct from producer"],
"acceptance_scenarios": ["Complete attestation validates", "Missing digest/source/worktree/test evidence rejects", "Attestation binds to one producer run", "Changed artifact requires a new attestation"],
"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", "artifact attestation tests"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000119",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "02b472b",
"risk_tier": "E",
"intent": "Add independent verification contract requiring a distinct verifier identity/run and attested artifact evidence before Work completion.",
"requirement_ids": ["WP-301", "Independent verifier", "Work completion gate"],
"allowed_change_scope": {"modules": ["work-agent-verification"], "paths": ["contracts/work/**", "modules/work/domain/**", "tests/agent_execution/**", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000119-independent-verification-contract.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "database/**", "migrations/**", "deploy/**", "security/**", "cmd/**", "runtime/**", "adapters/**", ".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": ["ADR-0017 verification separation", "ADR-0020 independent verifier", "Agent artifact attestation from DEV-000118"], "may_change": ["Work independent verification contract"]},
"semantic_owner": "work-agent-verification",
"canonical_state": [],
"state_transitions": ["producer result + attestation -> verifier assessment -> verified/rejected"],
"side_effects_and_privileges": ["Pure validation; no merge, deployment, binding activation, credential access or external mutation"],
"persistence_and_migrations": ["No schema or migration changes"],
"failure_retry_restart": ["Rejected verification preserves producer evidence; a new verifier run is required"],
"assumptions": ["Verifier identity and run are authenticated later", "Producer and verifier cannot be the same run", "Artifact evidence is immutable"],
"acceptance_scenarios": ["Distinct verifier validates attested artifact", "Same run is rejected", "Missing artifact/test evidence is rejected", "Rejected verification does not erase producer result"],
"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", "independent verification tests"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000120",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "04dad79",
"risk_tier": "E",
"intent": "Add typed durable dispatch intent and idempotency contract for agent runs without implementing a dispatcher, queue or worker process.",
"requirement_ids": ["WP-301", "Durable dispatch", "Duplicate dispatch prevention"],
"allowed_change_scope": {"modules": ["work-agent-execution"], "paths": ["contracts/work/**", "modules/work/domain/**", "tests/agent_execution/**", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000120-agent-dispatch-intent.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "database/**", "migrations/**", "deploy/**", "security/**", "cmd/**", "runtime/**", "adapters/**", ".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": ["ADR-0017 delegation/run ownership", "ADR-0020 dispatch idempotency", "Existing Capability implementation identity"], "may_change": ["Work dispatch intent contract"]},
"semantic_owner": "work-agent-execution",
"canonical_state": [],
"state_transitions": ["dispatch intent -> accepted once by idempotency key -> lease acquisition", "same idempotency key -> same dispatch identity"],
"side_effects_and_privileges": ["Pure validation; no queue, process start, persistence adapter, credential access or external mutation"],
"persistence_and_migrations": ["No schema or migration changes"],
"failure_retry_restart": ["Retry reuses intent identity only when the durable store confirms the same payload; changed payload requires a new key"],
"assumptions": ["Dispatcher persists intent atomically later", "Exact source/worktree is resolved before worker start", "Verification plan is part of dispatch provenance"],
"acceptance_scenarios": ["Complete dispatch validates", "Missing idempotency/source/worktree/verification plan rejects", "Payload identity is explicit", "Worker implementation is typed and not inferred from free text"],
"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", "dispatch intent tests"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000121",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "f792d20",
"risk_tier": "E",
"intent": "Add work-owned durable persistence schema for dispatch intents, leases and callback sequence heads, with repository write boundaries.",
"requirement_ids": ["WP-301", "Durable dispatch persistence", "Atomic lease/fencing state"],
"allowed_change_scope": {"modules": ["work-agent-execution", "work-storage"], "paths": ["database/migrations/029_work_agent_execution_persistence.sql", "adapters/postgres/work_repository.go", "tests/agent_execution/**", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000121-agent-execution-persistence.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "deploy/**", "cmd/**", "runtime/**", "adapters/continuity*", "adapters/capability*", ".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": 1},
"contracts": {"consume": ["DEV-000120 dispatch intent", "DEV-000116 lease/fencing", "DEV-000117 callback sequence"], "may_change": ["Work-owned persistence schema and repository methods"]},
"semantic_owner": "work-agent-execution",
"canonical_state": ["work.agent_dispatch_intents", "work.agent_leases", "work.agent_callback_heads"],
"state_transitions": ["dispatch intent persisted idempotently", "lease acquired/renewed with fencing token", "callback sequence advanced atomically"],
"side_effects_and_privileges": ["Work schema writes only through Work repository; no cross-owner SQL"],
"persistence_and_migrations": ["Append-only migration 029; work_role only"],
"failure_retry_restart": ["Duplicate idempotency key is reconciled; lease conflict is rejected; callback sequence uses row lock/conditional update"],
"assumptions": ["Migration is reviewed and applied before runtime enablement", "Production deployment is a later canary task", "No secrets are stored in dispatch payload"],
"acceptance_scenarios": ["Duplicate dispatch cannot create a second intent", "Expired/wrong lease cannot advance", "Out-of-order callback sequence is rejected", "Work owner boundaries remain intact"],
"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", "migration/repository tests"],
"terminal_gap_states": ["BLOCKED_BY_MIGRATION_DECISION", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000122",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "8081b4d",
"risk_tier": "E",
"intent": "Implement an opt-in deterministic dry-run dispatcher and fake worker cycle over the Agent Execution Plane contracts.",
"requirement_ids": ["WP-301", "Dry-run dispatcher", "Deterministic fake worker"],
"allowed_change_scope": {"modules": ["work-agent-execution"], "paths": ["contracts/work/dry_run_dispatch.go", "modules/work/domain/dry_run_dispatch.go", "tests/agent_execution/dry_run_dispatch_test.go", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000122-agent-dry-run-dispatch.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "deploy/**", "cmd/**", "runtime/**", "adapters/**", "database/**", ".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": ["DEV-000120 dispatch intent", "DEV-000116 lease/fencing", "DEV-000117 callback sequence"], "may_change": ["Dry-run dispatch result value" ]},
"semantic_owner": "work-agent-execution",
"canonical_state": ["DryRunDispatch value; no production state"],
"state_transitions": ["validated intent -> deterministic lease -> heartbeat callback -> result callback"],
"side_effects_and_privileges": ["Pure domain construction only; no external mutation"],
"persistence_and_migrations": ["Uses DEV-000121 persistence contract but does not apply migration"],
"failure_retry_restart": ["Invalid intent, worker, token, time or TTL is rejected"],
"assumptions": ["Runtime dispatcher and production worker remain separate follow-up work"],
"acceptance_scenarios": ["Same input produces byte-for-byte equivalent typed output", "Invalid intent is rejected", "Callbacks are ordered and inside lease window"],
"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"],
"terminal_gap_states": ["BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000123",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "fa62322",
"risk_tier": "E",
"intent": "Connect the opt-in deterministic dry-run dispatcher to a narrow Work-owned execution persistence port and application coordinator.",
"requirement_ids": ["WP-301", "Work execution port", "Dry-run orchestration"],
"allowed_change_scope": {"modules": ["work-agent-execution"], "paths": ["modules/work/ports/execution.go", "modules/work/application/dry_run_dispatch.go", "tests/agent_execution/dry_run_orchestration_test.go", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000123-agent-dry-run-orchestration.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "deploy/**", "cmd/**", "runtime/**", "adapters/**", "database/**", ".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": ["DEV-000121 persistence methods", "DEV-000122 dry-run dispatch"], "may_change": ["Work execution port and application coordinator"]},
"semantic_owner": "work-agent-execution",
"canonical_state": ["Persistence remains owned by Work repository"],
"state_transitions": ["dispatch intent -> lease -> ordered callbacks"],
"side_effects_and_privileges": ["Explicit Work-owned port only; no runtime entrypoint or production enablement"],
"persistence_and_migrations": ["Reuses DEV-000121 schema; no migration"],
"failure_retry_restart": ["Idempotent repository methods make retries safe; partial failure is returned to caller"],
"assumptions": ["Production dispatcher remains disabled until a separate runtime canary task"],
"acceptance_scenarios": ["All persistence operations are invoked in deterministic order", "Invalid dispatch never reaches the store"],
"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"],
"terminal_gap_states": ["BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000124",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "7f82240",
"risk_tier": "E",
"intent": "Add an explicit opt-in canary gate for the dry-run agent dispatcher with implementation allowlist and bounded dispatch count.",
"requirement_ids": ["WP-301", "Opt-in canary", "Bounded dispatch"],
"allowed_change_scope": {"modules": ["work-agent-execution"], "paths": ["modules/work/application/canary.go", "tests/agent_execution/canary_test.go", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000124-agent-canary-gate.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "deploy/**", "cmd/**", "runtime/**", "adapters/**", "database/**", ".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": ["DEV-000123 dry-run orchestration"], "may_change": ["Canary configuration and gate errors"]},
"semantic_owner": "work-agent-execution",
"canonical_state": ["Canary configuration is caller-owned; dispatch state remains Work-owned"],
"state_transitions": ["disabled/unauthorized/limited -> rejected; enabled and allowed -> dry-run orchestration"],
"side_effects_and_privileges": ["No side effect before all canary gates pass"],
"persistence_and_migrations": ["Reuses existing persistence; no migration"],
"failure_retry_restart": ["Caller receives typed gate failure; retries remain bounded by used dispatch count"],
"assumptions": ["Runtime wiring and production configuration are separate follow-up work"],
"acceptance_scenarios": ["Disabled canary writes nothing", "Wrong implementation writes nothing", "Limit is enforced", "Allowed canary persists four 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"],
"terminal_gap_states": ["BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000125",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "d2338b1",
"risk_tier": "E",
"intent": "Add a manually invoked CLI harness for the opt-in deterministic agent canary using an in-memory store.",
"requirement_ids": ["WP-301", "Manual canary entrypoint", "Dry-run verification"],
"allowed_change_scope": {"modules": ["work-agent-execution"], "paths": ["cmd/agent-canary/main.go", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000125-agent-canary-cli.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "deploy/**", "runtime/**", "adapters/**", "database/**", ".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": ["DEV-000124 canary gate"], "may_change": ["Manual CLI entrypoint"]},
"semantic_owner": "work-agent-execution",
"canonical_state": ["No durable state; in-memory canary evidence only"],
"state_transitions": ["CLI flags -> canary gate -> deterministic callbacks -> JSON summary"],
"side_effects_and_privileges": ["No network, database, deployment or production mutation"],
"persistence_and_migrations": ["Explicitly does not use production repository"],
"failure_retry_restart": ["Disabled or unauthorized invocation exits nonzero without writes"],
"assumptions": ["Real WorkRepository wiring requires a separate approved runtime task"],
"acceptance_scenarios": ["Default invocation is rejected", "Explicit enablement produces JSON summary", "No secrets or external connections are required"],
"required_tests": ["go test ./...", "go vet ./...", "go run ./cmd/agent-canary", "go run ./cmd/agent-canary -enable-canary", "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"],
"terminal_gap_states": ["BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000126",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "c30e0c7",
"risk_tier": "E",
"intent": "Wire the manual agent canary CLI to the existing Work PostgreSQL repository behind an explicit flag while preserving in-memory default behavior.",
"requirement_ids": ["WP-301", "Repository canary wiring", "Credential-safe adapter use"],
"allowed_change_scope": {"modules": ["work-agent-execution", "work-storage"], "paths": ["cmd/agent-canary/main.go", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000126-agent-canary-repository.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "deploy/**", "runtime/**", "database/**", ".gitea/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 1, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["DEV-000125 manual canary CLI", "DEV-000121 WorkRepository methods"], "may_change": ["Manual CLI storage selection"]},
"semantic_owner": "work-agent-execution",
"canonical_state": ["WorkRepository remains canonical for postgres mode"],
"state_transitions": ["CLI -postgres -> OpenWork -> canary coordinator -> repository writes"],
"side_effects_and_privileges": ["Only explicit -postgres mode connects; credentials come from existing environment variables and are never printed"],
"persistence_and_migrations": ["Requires migration 029 to be applied separately; this task does not apply migrations"],
"failure_retry_restart": ["Connection or missing migration errors are returned; no automatic retry or deployment"],
"assumptions": ["Default mode remains in-memory and production entrypoints are unchanged"],
"acceptance_scenarios": ["Default mode remains deterministic", "Postgres mode uses Work repository through ExecutionStore", "No secret values appear in output"],
"required_tests": ["go test ./...", "go vet ./...", "go run ./cmd/agent-canary", "go run ./cmd/agent-canary -enable-canary", "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"],
"terminal_gap_states": ["BLOCKED_BY_MIGRATION_DECISION", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000127",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "8e26826",
"risk_tier": "E",
"intent": "Apply the already reviewed additive Work migration 029 on the canonical docker-prod PostgreSQL instance for controlled canary preparation.",
"requirement_ids": ["WP-301", "Migration canary", "Durable execution readiness"],
"allowed_change_scope": {"modules": ["work-storage"], "paths": ["dev/ACTIVE_TASK.json", "dev/tasks/DEV-000127-apply-agent-execution-migration.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "deploy/**", "cmd/**", "runtime/**", "adapters/**", ".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": ["DEV-000121 migration 029", "DEV-000126 repository wiring"], "may_change": ["Canonical production Work schema state"]},
"semantic_owner": "work-agent-execution",
"canonical_state": ["docker-prod assistant PostgreSQL work schema"],
"state_transitions": ["tables absent -> tables present and verified"],
"side_effects_and_privileges": ["One additive transactional migration through existing bootstrap/superuser path; no secrets printed"],
"persistence_and_migrations": ["Apply database/migrations/029_work_agent_execution_persistence.sql exactly once"],
"failure_retry_restart": ["ON CONFLICT-safe DDL; ON_ERROR_STOP; verify before any retry"],
"assumptions": ["docker-prod is the canonical target", "migration file is already committed and reviewed", "runtime remains disabled until a later canary"],
"acceptance_scenarios": ["All three Work tables exist after apply", "No non-Work schema changes", "Existing production containers remain running"],
"required_tests": ["go test ./...", "go vet ./...", "preflight", "check-diff", "check-architecture", "independent", "pre-apply table absence evidence", "migration apply exit 0", "post-apply table existence verification", "docker-prod container health read-only check", "git diff --check"],
"terminal_gap_states": ["BLOCKED_BY_MIGRATION_DECISION", "BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000128",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "790fd7e",
"risk_tier": "E",
"intent": "Add an explicitly profiled production compose service for the manual agent canary container.",
"requirement_ids": ["WP-301", "Canary container profile", "Disabled-by-default runtime"],
"allowed_change_scope": {"modules": ["work-agent-execution", "production-compose"], "paths": ["deploy/docker-compose.prod.yml", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000128-agent-canary-compose-profile.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "runtime/**", "adapters/**", ".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": ["DEV-000126 agent-canary postgres mode", "DEV-000127 migration 029"], "may_change": ["Production compose canary profile"]},
"semantic_owner": "work-agent-execution",
"canonical_state": ["No new state; uses Work schema"],
"state_transitions": ["profile absent -> explicit profile service available; default stack unchanged"],
"side_effects_and_privileges": ["Credential environment is injected by deployment system; service is disabled unless profile is explicitly selected"],
"persistence_and_migrations": ["Requires migration 029 already present; no migration in this task"],
"failure_retry_restart": ["restart=no; one bounded canary invocation"],
"assumptions": ["Deployment of this profile is a separate approved canary action", "No normal service depends on agent-canary"],
"acceptance_scenarios": ["Compose validates", "Default config does not start agent-canary", "Profile builds target cmd/agent-canary", "No secret literal is added"],
"required_tests": ["go test ./...", "go vet ./...", "docker compose -f deploy/docker-compose.prod.yml config --quiet", "preflight", "check-diff", "check-architecture", "independent", "git diff --check"],
"terminal_gap_states": ["BLOCKED_BY_DEPLOYMENT_DECISION", "BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,27 @@
# Governance lineage decision — 2026-08-22
Status: accepted as part of DEV-000099.
## DEV-000073 resolution
The repository history contains more than one DEV-000073 packet variant: a
production-deployment packet and an earlier durable-bootstrap work-in-progress.
Neither is the current active task, and the current main lineage does not
contain one uniquely authoritative DEV-000073 packet. References such as
`DEV-000073-baseline` in later historical packets are retained as evidence of
their implementation baseline, not treated as an accepted current architecture
revision or active work pointer.
The accepted architecture authority for the current project is the accepted
ADR chain, currently ADR-0017 and ADR-0018. A future task that needs the
DEV-000073 deployment lineage must name an exact packet/revision and establish
its ancestry before activation. No historical checkpoint is rewritten.
## Current governance authority
`dev/ACTIVE_TASK.json` selects exactly one current packet. The packet declares
its own path and the active pointer in scope. `architecture-registry.yaml` is
the human-maintained registry mirror; `architecture-registry.json` is the
machine-readable equivalent consumed by `devctl`; `architecture-registry.md`
is the cold-agent-readable projection. Their semantic content is synchronized
for the current ownership/topology baseline.
+13
View File
@@ -1,5 +1,18 @@
# Assistant project status
Audit date: 2026-08-22. Active implementation: DEV-000099 revision 1.
## Current governance checkpoint
- `DEV_000099_STATUS = COMPLETED`
- `LAST_COMPLETED = DEV-000098 Governance Sentinel V2: full task-packet validation, scope-conflict rejection, architecture/base checks, required-test enforcement and independent verifier`
- `ACTIVE_TASK = dev/tasks/DEV-000099-governance-consolidation.json revision 1`
- `LINEAGE_DECISION = docs/GOVERNANCE_LINEAGE_DECISION_2026-08-22.md; DEV-000073 historical variants are preserved as evidence and are not current authority`
- `REGISTRY_STATUS = YAML and JSON semantic mirrors agree; human registry projection synchronized with ADR-0017 ownership and current protected paths`
- `PRODUCTION_DOMAIN = docker.cin.su; shared docker-test remains prohibited`
- `RUNTIME_CHANGE = none; this governance consolidation changes repository authority/projections only`
- `NEXT_RECOMMENDED_STEP = create a separately scoped WP-002 production recovery task packet; production changes remain pending that packet`
Audit date: 2026-08-17. Active implementation: DEV-000072 revision 1.
## DEV-000072 checkpoint
@@ -0,0 +1,43 @@
# WP-200 Hardware and model inventory evidence
Task: DEV-000111
Evidence date: 2026-08-22
Target: `docker-prod` / `docker.cin.su`
## Host evidence
Read-only `ssh docker` inspection returned:
- host: `docker`
- CPU: `128` logical processors
- memory: `251 GiB` total, `203 GiB` available at inspection time
- swap: `8 GiB`, `256 KiB` used
- root storage: `1.6T` total, `359G` used, `1.2T` available, `24%`
- `nvidia-smi`: unavailable
- Docker server: `29.4.0`, `128` CPUs, approximately `252 GiB` Docker memory
- Docker runtimes: `runc`/`containerd`; no NVIDIA runtime reported
## Runtime evidence
Read-only `docker --context docker-prod` inspection found:
- `assistant-cognitive-worker-1`: production revision `b439a813...`, `/healthz` returned `status=ok`
- `assistant-cognitive-lifeboat-1`: `ghcr.io/ggml-org/llama.cpp:server`, Docker health `healthy`
- `llm-local-api`: `ghcr.io/ggml-org/llama.cpp:server`, Docker health `healthy`
- no model activation, routing change or deployment was performed by this inventory task
## Qualification conclusion
This host has no verified GPU/VRAM capability in the inspected runtime. CPU
and lifeboat health are evidence of availability, not model qualification.
WP-201 still requires immutable model digests, holdout quality tests,
latency/concurrency measurements, license/data-policy evidence and restart
behavior. Heavy-class activation remains a qualification decision, not an
inventory inference.
## Evidence limitations
This record does not establish the topology of any separate GPU host, does not
read secrets, and does not claim that a model is qualified from its image name
or health endpoint alone.
@@ -0,0 +1,110 @@
# Proposal ADR-0019 — Epistemics Truth V1
Status: Proposed
Task: DEV-000103
## Problem and evidence
The audit found that Domovoy has experience and reflection evidence, but no
small, explicit truth model for observations, claims, source provenance,
freshness or conflicts. Without that boundary, research, context compilation
and future learning could silently turn an unverified statement into durable
fact. The proposal is architecture-only: it does not add tables, runtime
handlers, connectors or belief mutation.
## Decision proposed
1. Do not create a new canonical owner. Epistemics owns truth evidence and
rebuildable truth projections; Interaction, Situation, Work, Capability and
Artifact retain ownership of their canonical records.
2. Define six typed records, with immutable identity and append-only history:
`Observation`, `Claim`, `BeliefAssessment`, `Source` and `ProvenanceEdge`,
plus a typed `VerificationState` value. Records reference other owners by
typed IDs and never copy their canonical payloads.
3. An `Observation` is a bounded acquired datum with acquisition time,
source snapshot reference, excerpt/hash reference, collector identity and
privacy/egress classification. It is evidence, not truth.
4. A `Claim` is a proposition with explicit subject/predicate/object or
proposition payload, scope, language, creator and claim status. It is not a
fact merely because it is repeated or model-generated.
5. A `BeliefAssessment` is a revisable, scoped assessment over one claim,
carrying verification state, confidence band, freshness result, coverage
result, conflict set and supporting/contradicting provenance edges.
6. `Source` identifies the origin and retrieval policy; a source snapshot is
the immutable artifact used for a particular observation. Source identity
alone is never a citation.
7. `ProvenanceEdge` records typed support, contradiction, derivation,
correction or supersession between evidence and claims. A projection may
be rebuilt from these edges and append-only records.
8. Verification states are explicit: `UNVERIFIED`, `SUPPORTED`,
`CORROBORATED`, `CONFLICTED`, `STALE`, `RETRACTED` and `UNKNOWN`.
`UNKNOWN` and `ASSUMED` remain visible and cannot be promoted implicitly.
9. Freshness and coverage are evidence dimensions, not confidence shortcuts.
A stale or incomplete source may support a claim only with that limitation
attached. Conflicts are retained and surfaced; they are not resolved by
latest-write-wins.
10. Privacy, authorization and egress filtering happen before observation
persistence or external citation. Secrets and unapproved personal data
are fail-closed and cannot become source snapshots.
## Alternatives considered and rationale
- Store truth directly in Situation or Interaction: rejected because those
owners would become provenance/verification authorities and would mix
canonical operational state with revisable evidence.
- Use a single confidence score or latest-write-wins: rejected because it
hides freshness, incomplete coverage and contradiction, and cannot explain
why an assessment changed.
- Introduce a universal ontology engine: rejected because it expands scope,
makes ownership ambiguous and is unnecessary for the bounded claims needed
by the research and context work packages.
The proposed small typed set keeps evidence historical, makes uncertainty
visible and allows projections to be rebuilt without granting models or
connectors canonical authority.
## Lifecycle and correction
Observation and source snapshots are append-only and retention-governed.
Claims and assessments are superseded or retracted by new typed records;
history is never deleted to hide an earlier conclusion. Human correction is
strong evidence with explicit provenance, but does not become a permanent
rule without a later assessment. Rebuildable projections may be compacted
only while preserving their source record references and retention policy.
## Boundaries and non-goals
- No universal ontology, generic `BeliefManager` or `map[string]any` truth
store.
- No model output directly mutates claims, assessments, authority or source
snapshots.
- No web/repository connector, citation resolver, migration or UI change is
included in this proposal.
- External research remains read-only until a separate WP-102 contract and
connector task is accepted.
## Consequences
Truthful product surfaces can distinguish evidence, assessment, uncertainty,
staleness and conflict. Research and context compilation gain stable
provenance inputs, while implementation must carry typed references and
explicit freshness/coverage results. The proposal adds review and storage
discipline but intentionally provides no immediate runtime capability.
## Migration and rollback
There is no migration in this proposal. If accepted, implementation must add
append-only records and projections behind owner-local interfaces, with a
rebuild harness and a feature flag/canary before any existing response path
consumes assessments. Rejection or rollback disables the projection consumer;
historical source evidence remains intact and no canonical operational state
is mutated.
## Acceptance and revisit triggers
The proposal is ready for acceptance when owner boundaries, identifiers,
retention/privacy rules, correction/supersession lifecycle, rebuildable
projections and conflict semantics have implementation-level contract tests
specified without introducing a new owner. Revisit if a source requires
write access, if retention law changes, if a second canonical owner is
proposed, or if confidence is used as an authority/permission decision.
@@ -0,0 +1,97 @@
# Proposal ADR-0020 — Agent Execution Plane V1
Status: Proposed
Task: DEV-000104
## Problem and evidence
ADR-0017 defines durable delegation semantics, but the audit found no
automatic dispatcher/worker plane, lease/heartbeat lifecycle, callback
authentication or independent verification boundary. A worker must be able
to stop, restart and resume without turning a missing callback into success or
silently duplicating external work.
## Decision proposed
1. Keep `work` as the canonical owner of `Delegation`, `AgentRun`,
`AgentCheckpoint`, `AgentResult` and verification gating. `capability`
owns executor-neutral agent implementations; adapters own transport and
process details.
2. Every dispatch has a stable `dispatch_id`, delegation id, run id,
implementation id, exact source/worktree identity, bounded instruction
artifact reference, requested capability set, authority/credential refs,
deadline and idempotency key. The derived instruction must preserve
OriginalHumanIntent, inferred goal, added context and verification plan as
separate provenance fields.
3. A worker must acquire a durable lease before execution. Lease ownership is
a fencing token, not an in-memory mutex. Heartbeats extend the lease only
for the same run and fencing token; an expired lease makes late callbacks
non-authoritative.
4. Dispatch and callback endpoints authenticate the worker identity and bind
every message to delegation, run, dispatch, implementation and fencing
token. Forged, replayed, out-of-order or duplicate callbacks are rejected
or recorded as non-authoritative evidence. Idempotent acknowledgement is
allowed only for the exact previously accepted event.
5. Checkpoints and artifacts are content-addressed metadata owned by
`artifact`, with typed refs from Work. Artifact attestation records source
revision/worktree, producer, digest, test command/result and capture time;
an un-attested path or log is not completion evidence.
6. Cancellation is a durable Work decision. The dispatcher sends cancellation
to the leased worker, records acknowledgement or timeout, and never erases
history. A worker that cannot prove cancellation remains `UNKNOWN` or
`RECOVERING` until reconciled.
7. Retry is policy-driven and distinguishes retryable transport/worker loss,
execution-window exhaustion, human blocker, invalid result and unknown
external outcome. Retry creates a new run under the same delegation and
preserves prior checkpoints/results; it never reuses an idempotency key for
an unproven external mutation.
8. A run result cannot complete Work directly. Independent verification must
consume the declared artifacts and tests through a separate verifier
identity and produce a typed verification result. Verification failure,
missing evidence and verifier unavailability remain distinct states.
9. Secrets are resolved only through approved `CredentialRef` boundaries and
are not copied into instructions, callbacks, artifacts, logs or task
packets. Worker startup has no ambient credential inheritance.
10. The plane is durable and restartable: dispatch intent, lease, heartbeat,
cancellation, checkpoint, result, verification and reconciliation events
are persisted before their projections claim progress.
## Lifecycle proposal
Delegation remains the higher-level human/work intent. A run progresses through
`DISPATCHED`, `LEASED`, `RUNNING`, `CHECKPOINTED`, `SUCCEEDED`, `FAILED`,
`CANCEL_REQUESTED`, `CANCELLED`, `EXHAUSTED`, `WAITING_HUMAN`, `UNKNOWN` or
`RECOVERING`, subject to explicit transition guards. `SUCCEEDED` means only
that the worker returned an accepted result; Work completion additionally
requires independent verification. Lease expiry and missing heartbeat never
become success.
## Alternatives and rationale
- An in-memory queue or goroutine was rejected because it loses dispatch state
on restart and violates durable timer/lease requirements.
- A single generic `AgentManager` was rejected because it collapses Work,
Capability, Artifact and Continuity ownership.
- Trusting worker-reported success was rejected because the worker and
verifier must remain separate authorities.
- Reusing the same run on retry was rejected because it makes duplicate
external effects and late callbacks indistinguishable.
## Boundaries, consequences and non-goals
No new canonical owner, provider, deployment authority, database migration,
Codex-specific domain contract or automatic self-modification is proposed.
The design enables restartable external work and truthful failure handling,
but requires durable event ordering, fencing, artifact storage and verifier
capacity before WP-301 can claim production readiness.
## Migration, rollback and revisit triggers
Implementation must begin behind an opt-in dispatch path with a dry-run
verifier and deterministic fake worker. Existing delegation records remain
readable; unknown legacy RUNNING records are classified by WP-302 rather than
silently resumed. Rollback disables new dispatch and drains/recovers leases;
history and artifacts remain. Revisit if a worker needs authority beyond its
declared capability, if a second transport changes callback guarantees, or if
artifact attestation cannot be made independent of the worker.
@@ -0,0 +1,80 @@
# Proposal ADR-0021 — Multi-Agent Work Graph V1
Status: Proposed
Task: DEV-000105
## Problem and evidence
The audit found durable Work and Delegation records but no multi-agent DAG,
fan-out/fan-in scheduler, budgets, leases, conflict resolution or independent
verifier. Adding parallel agents without those controls would create duplicate
work, unbounded cost, hidden disagreement and false completion.
## Decision proposed
1. `work` owns the durable graph: `WorkGraph`, `GraphRevision`, `WorkNode`,
`NodeDependency`, `ResultCandidate`, `ConflictSet` and synthesis/verification
gating. Capability remains the source of eligible agent implementations;
Artifact owns result metadata; Continuity owns human attention.
2. A graph is immutable by revision. Each revision contains typed nodes,
dependency edges, requested capabilities, budgets, cancellation policy,
synthesis policy and verifier requirements. Acyclicity and ownership are
validated before dispatch; edits create a new revision rather than mutating
a running graph.
3. Node identity and idempotency are stable within a revision. Independent
nodes may fan out only after graph admission checks. A dependent node is
dispatchable only when all required prerequisites have accepted results.
Partial results and failed nodes remain attributable and do not erase
sibling results.
4. Scheduling enforces durable per-goal, per-user, per-resource and per-graph
budgets for concurrency, attempts, time, tokens and external mutation
class. Backpressure and starvation detection are explicit; a role or agent
cannot bypass a budget.
5. Assignment uses the Agent Execution Plane lease/fencing rules. Pause,
cancel, reprioritize and resume are durable graph decisions propagated to
eligible nodes. Restart/replay rebuilds from graph revision and execution
events, not process-local queues.
6. Agent outputs are `ResultCandidate` records with author run, node, graph
revision, artifact refs, evidence and claims. Conflicting candidates form a
`ConflictSet`; dissent and unresolved conflict remain visible.
7. Synthesis is a candidate, never an authority shortcut. A verifier with a
distinct run/identity from the author consumes declared evidence and emits
a typed verification result. Work completion requires the graph's verifier
policy and does not occur from fan-in text alone.
8. Specialized roles are capability profiles and evidence-based selection
hints, not hardcoded authority classes. One agent may hold multiple roles;
role selection never grants credentials or permission.
## Lifecycle and failure semantics
Graph states are `DRAFT`, `ADMITTED`, `RUNNING`, `PAUSED`, `CANCELLING`,
`PARTIAL`, `VERIFYING`, `COMPLETED`, `FAILED`, `UNKNOWN` and `RECOVERING`.
Node states distinguish `BLOCKED_PREREQUISITE`, `READY`, `LEASED`, `RUNNING`,
`SUCCEEDED`, `FAILED`, `CANCELLED`, `UNKNOWN` and `RECOVERING`. A failed node
may trigger policy-defined retry or human attention; it never deletes other
node evidence. Unknown external outcomes block unsafe replay until reconciled.
## Alternatives and rationale
- A shared in-memory task queue was rejected because restart loses graph state
and violates durable scheduling semantics.
- A flat list of independent agents was rejected because dependencies,
fan-in, cycle rejection and partial completion cannot be represented safely.
- Majority vote without provenance was rejected because disagreement and
source quality would be hidden.
- Agent roles as privileged classes were rejected because capability evidence,
not labels, must determine eligibility and authority remains separate.
## Consequences, migration and rollback
The graph enables bounded parallel work and attributable synthesis, at the
cost of durable event ordering, budget accounting and verifier capacity. No
runtime, schema or scheduler is included in this proposal. Implementation
must begin with a deterministic fake graph harness, cycle/budget/conflict
tests and opt-in dispatch. Rollback disables new graph admission and drains
or recovers active leases without deleting graph history or artifacts.
Revisit if a graph node needs authority outside its capability, if cross-owner
joins become necessary, if synthesis cannot preserve dissent, or if the
verifier cannot be made independent of the author run.
@@ -0,0 +1,79 @@
# Proposal ADR-0022 — Governed Self-Development V1
Status: Proposed
Task: DEV-000106
## Problem and evidence
The audit found ReflectionCandidates and capability evidence, but no safe
pipeline for turning repeated evidence into a reviewed change. Direct
self-editing would let model output alter source, skills, authority or
production without provenance, evaluation or rollback.
## Decision proposed
1. Introduce a governed development control plane. `ExtensionProposal` is an
evidence-bearing request that moves through review, implementation and
promotion stages.
2. A proposal must reference repeated or corroborated evidence, desired
outcome, smallest change layer, affected owners/contracts, assumptions,
alternatives, risk tier, evaluation plan, rollback and emergency-stop
conditions. One accidental failure is insufficient; duplicate evidence is
deduplicated without deleting its history.
3. Candidate artifacts are immutable, content-addressed and lineage-linked to
the proposal, task packet, source revision, generator/author identity,
tests, evaluations and environment. Candidate code, skill, adapter or
configuration is created only in an isolated branch/worktree/sandbox with
no protected-main, production or ambient-secret access.
4. A human-authorized governance boundary approves task scope and promotion.
Cognitive components may propose and summarize evidence; promotion remains
an explicit governance decision.
5. Evaluation is layered: contract/architecture/security checks, deterministic
unit/integration tests, adversarial and regression corpus, migration/
recovery checks, and independent verifier review. Failed, conflicting or
incomplete evidence blocks promotion and remains attributable.
6. Candidate lifecycle is explicit: `PROPOSED`, `REJECTED`, `SANDBOXED`,
`EVALUATING`, `CANARY`, `PROMOTED`, `ROLLED_BACK`, `RETIRED` and `BLOCKED`.
Promotion is bounded by capability registration, traffic, budget and
telemetry policy. A candidate never receives authority merely by passing
model evaluation.
7. Canary state keeps the previous implementation addressable. Rollback is a
durable, reversible selection change with an emergency stop that disables
candidate traffic without deleting evidence, artifacts or lineage.
8. Generated skills, modules, adapters and configuration follow the same
ordinary development path: typed owners, task scope, architecture checks,
tests, security review, migration/recovery plan and independent
verification.
## Ownership and boundaries
Epistemics owns evidence and ReflectionCandidates; Capability owns
implementation/skill registration and lifecycle; Work owns task/delegation
execution; Artifact owns candidate and evaluation metadata; Authority owns
human promotion permission; Commit/Executor owns external mutation. No new
universal LearningManager or self-development owner is introduced.
Three boundaries are essential: candidate work uses the declared task scope,
secret access follows existing CredentialRef policy, and candidate promotion
does not itself grant new authority. Original intent, truth assessments and
historical evidence retain their existing owners and provenance.
## Rationale
Evidence, scoped implementation, independent evaluation and reversible
promotion provide a practical improvement loop while keeping authority and
provenance explicit. The design uses existing Epistemics, Capability, Work,
Artifact and Authority boundaries instead of adding a new universal manager.
## Consequences, migration and rollback
The proposal creates an auditable path for improvement while adding review,
evaluation and artifact-retention cost. There is no implementation, schema or
production change here. Implementation starts with a deterministic proposal
fixture and fake candidate, then uses opt-in canary traffic. Rollback disables
candidate selection and restores the previous implementation reference;
evidence and failed candidates remain available for analysis.
Revisit if a candidate requires new authority, cross-owner direct storage,
unreviewed external mutation, or if rollback cannot restore the previous
implementation deterministically.
+35
View File
@@ -0,0 +1,35 @@
package application
import (
"context"
"errors"
"time"
"git.cin.su/m/Assistant/contracts/work"
"git.cin.su/m/Assistant/modules/work/ports"
)
var (
ErrCanaryDisabled = errors.New("agent canary is disabled")
ErrCanaryNotAllowed = errors.New("agent implementation is not canary-allowed")
ErrCanaryLimitReached = errors.New("agent canary dispatch limit reached")
)
type CanaryConfig struct {
Enabled bool
ImplementationID string
MaxDispatches int
}
func ExecuteCanaryDryRun(ctx context.Context, store ports.ExecutionStore, config CanaryConfig, usedDispatches int, intent work.AgentDispatchIntent, workerID string, token work.AgentFencingToken, now time.Time, ttl time.Duration) (work.DryRunDispatch, error) {
if !config.Enabled {
return work.DryRunDispatch{}, ErrCanaryDisabled
}
if config.ImplementationID == "" || intent.ImplementationID != config.ImplementationID {
return work.DryRunDispatch{}, ErrCanaryNotAllowed
}
if config.MaxDispatches <= 0 || usedDispatches >= config.MaxDispatches {
return work.DryRunDispatch{}, ErrCanaryLimitReached
}
return ExecuteDryRunDispatch(ctx, store, intent, workerID, token, now, ttl)
}
@@ -0,0 +1,29 @@
package application
import (
"context"
"time"
"git.cin.su/m/Assistant/contracts/work"
"git.cin.su/m/Assistant/modules/work/domain"
"git.cin.su/m/Assistant/modules/work/ports"
)
func ExecuteDryRunDispatch(ctx context.Context, store ports.ExecutionStore, intent work.AgentDispatchIntent, workerID string, token work.AgentFencingToken, now time.Time, ttl time.Duration) (work.DryRunDispatch, error) {
dispatch, err := domain.BuildDryRunDispatch(intent, workerID, token, now, ttl)
if err != nil {
return work.DryRunDispatch{}, err
}
if err := store.SaveDispatchIntent(ctx, dispatch.Intent); err != nil {
return work.DryRunDispatch{}, err
}
if err := store.SaveAgentLease(ctx, dispatch.Lease); err != nil {
return work.DryRunDispatch{}, err
}
for _, callback := range dispatch.Callbacks {
if err := store.AcceptAgentCallback(ctx, callback); err != nil {
return work.DryRunDispatch{}, err
}
}
return dispatch, nil
}
+26
View File
@@ -0,0 +1,26 @@
package domain
import (
"errors"
"time"
"git.cin.su/m/Assistant/contracts/work"
)
var ErrDryRunDispatchRejected = errors.New("dry-run dispatch rejected")
func BuildDryRunDispatch(intent work.AgentDispatchIntent, workerID string, token work.AgentFencingToken, now time.Time, ttl time.Duration) (work.DryRunDispatch, error) {
if ValidateDispatchIntent(intent) != nil || workerID == "" || token == 0 || now.IsZero() || ttl <= 0 {
return work.DryRunDispatch{}, ErrDryRunDispatchRejected
}
lease, err := AcquireAgentLease(intent.RunID, workerID, token, now, ttl)
if err != nil {
return work.DryRunDispatch{}, ErrDryRunDispatchRejected
}
heartbeat := work.AgentCallback{RunID: intent.RunID, WorkerID: workerID, FencingToken: token, Sequence: 1, Kind: work.AgentCallbackHeartbeat, ObservedAt: now.Add(ttl / 3)}
result := work.AgentCallback{RunID: intent.RunID, WorkerID: workerID, FencingToken: token, Sequence: 2, Kind: work.AgentCallbackResult, ObservedAt: now.Add((ttl * 2) / 3)}
if ValidateAgentCallback(heartbeat, lease, 0) != nil || ValidateAgentCallback(result, lease, heartbeat.Sequence) != nil {
return work.DryRunDispatch{}, ErrDryRunDispatchRejected
}
return work.DryRunDispatch{Intent: intent, Lease: lease, Callbacks: []work.AgentCallback{heartbeat, result}}, nil
}
+48
View File
@@ -0,0 +1,48 @@
package domain
import (
"errors"
"time"
"git.cin.su/m/Assistant/contracts/work"
)
var ErrLeaseRejected = errors.New("agent lease rejected")
var ErrCallbackRejected = errors.New("agent callback rejected")
func AcquireAgentLease(runID work.AgentRunID, workerID string, token work.AgentFencingToken, now time.Time, ttl time.Duration) (work.AgentLease, error) {
if runID == "" || workerID == "" || token == 0 || now.IsZero() || ttl <= 0 {
return work.AgentLease{}, ErrLeaseRejected
}
return work.AgentLease{ID: work.AgentLeaseID(string(runID) + ":" + workerID), RunID: runID, WorkerID: workerID, FencingToken: token, State: work.AgentLeaseActive, AcquiredAt: now, ExpiresAt: now.Add(ttl)}, nil
}
func RenewAgentLease(lease work.AgentLease, workerID string, token work.AgentFencingToken, now time.Time, ttl time.Duration) (work.AgentLease, error) {
if lease.State != work.AgentLeaseActive || lease.WorkerID != workerID || lease.FencingToken != token || now.IsZero() || !now.Before(lease.ExpiresAt) || ttl <= 0 {
return work.AgentLease{}, ErrLeaseRejected
}
lease.ExpiresAt = now.Add(ttl)
return lease, nil
}
func ValidateAgentCallback(callback work.AgentCallback, lease work.AgentLease, lastSequence uint64) error {
if lease.State != work.AgentLeaseActive || callback.RunID == "" || callback.RunID != lease.RunID || callback.WorkerID == "" || callback.WorkerID != lease.WorkerID || callback.FencingToken == 0 || callback.FencingToken != lease.FencingToken || callback.Sequence <= lastSequence || callback.Kind == "" || callback.ObservedAt.IsZero() || !callback.ObservedAt.Before(lease.ExpiresAt) {
return ErrCallbackRejected
}
return nil
}
func ValidateIndependentVerification(value work.IndependentVerification) error {
if value.ProducerRunID == "" || value.VerifierRunID == "" || value.ProducerRunID == value.VerifierRunID || value.VerifierIdentity == "" || value.ArtifactID == "" || value.TestEvidenceRef == "" || value.Summary == "" {
return ErrCallbackRejected
}
return nil
}
func ValidateDispatchIntent(value work.AgentDispatchIntent) error {
if value.DispatchID == "" || value.IdempotencyKey == "" || value.DelegationID == "" || value.RunID == "" || value.ImplementationID == "" || value.ImplementationRevision < 1 || value.SourceRevision == "" || value.WorktreeID == "" || value.VerificationPlanRef == "" || value.CreatedAtUnixMillis < 1 {
return ErrCallbackRejected
}
return nil
}
+45
View File
@@ -0,0 +1,45 @@
package domain
import (
"time"
"git.cin.su/m/Assistant/contracts/work"
)
type RunLivenessState string
const (
RunLivenessLive RunLivenessState = "LIVE"
RunLivenessStale RunLivenessState = "STALE"
RunLivenessInvalid RunLivenessState = "INVALID"
RunLivenessNotRun RunLivenessState = "NOT_RUNNING"
)
type RunLiveness struct {
State RunLivenessState
Age time.Duration
ObservedAt time.Time
}
func AssessRunLiveness(run work.AgentRun, observedAt time.Time, heartbeatTimeout time.Duration) RunLiveness {
result := RunLiveness{ObservedAt: observedAt}
if observedAt.IsZero() || heartbeatTimeout <= 0 || run.UpdatedAtUnixMillis < 1 {
result.State = RunLivenessInvalid
return result
}
if run.State != work.AgentRunStarted && run.State != work.AgentRunRunning {
result.State = RunLivenessNotRun
return result
}
updated := time.UnixMilli(run.UpdatedAtUnixMillis)
result.Age = observedAt.Sub(updated)
if result.Age < 0 {
result.State = RunLivenessInvalid
return result
}
result.State = RunLivenessLive
if result.Age > heartbeatTimeout {
result.State = RunLivenessStale
}
return result
}
+13
View File
@@ -0,0 +1,13 @@
package ports
import (
"context"
"git.cin.su/m/Assistant/contracts/work"
)
type ExecutionStore interface {
SaveDispatchIntent(context.Context, work.AgentDispatchIntent) error
SaveAgentLease(context.Context, work.AgentLease) error
AcceptAgentCallback(context.Context, work.AgentCallback) error
}
+85
View File
@@ -1,8 +1,12 @@
package health
import (
"context"
"encoding/json"
"fmt"
"net"
"net/http"
"time"
)
type Handler struct {
@@ -20,3 +24,84 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
"architecture_generation": h.ArchitectureGeneration,
})
}
type Probe struct {
Name string
Check func(context.Context) error
}
type Result struct {
Name string `json:"name"`
State string `json:"state"`
Reason string `json:"reason"`
Detail string `json:"detail,omitempty"`
}
type ReadinessHandler struct {
Runtime string
Probes []Probe
Timeout time.Duration
}
func (h ReadinessHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
timeout := h.Timeout
if timeout <= 0 {
timeout = 2 * time.Second
}
results := make([]Result, 0, len(h.Probes))
ready := true
for _, probe := range h.Probes {
result := Result{Name: probe.Name, State: "READY", Reason: "AVAILABLE"}
if probe.Check == nil {
result.State, result.Reason, result.Detail = "DEGRADED", "PROBE_NOT_CONFIGURED", "probe is not configured"
} else {
ctx, cancel := context.WithTimeout(r.Context(), timeout)
err := probe.Check(ctx)
cancel()
if err != nil {
ready = false
result.State, result.Reason, result.Detail = "DEGRADED", "DEPENDENCY_UNAVAILABLE", err.Error()
}
}
if result.State != "READY" {
ready = false
}
results = append(results, result)
}
status := "ready"
code := http.StatusOK
if !ready {
status, code = "degraded", http.StatusServiceUnavailable
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
_ = json.NewEncoder(w).Encode(map[string]any{"status": status, "runtime": h.Runtime, "checks": results})
}
func HTTPProbe(client *http.Client, endpoint string) Probe {
return Probe{Name: endpoint, Check: func(ctx context.Context) error {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
if err != nil {
return err
}
response, err := client.Do(req)
if err != nil {
return err
}
defer response.Body.Close()
if response.StatusCode < 200 || response.StatusCode >= 300 {
return fmt.Errorf("status=%s", response.Status)
}
return nil
}}
}
func TCPProbe(name, host, port string) Probe {
return Probe{Name: name, Check: func(ctx context.Context) error {
connection, err := (&net.Dialer{}).DialContext(ctx, "tcp", net.JoinHostPort(host, port))
if err != nil {
return err
}
return connection.Close()
}}
}
+28
View File
@@ -0,0 +1,28 @@
package health
import (
"context"
"errors"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestReadinessReturns503WithTypedDependencyReason(t *testing.T) {
handler := ReadinessHandler{Runtime: "test", Probes: []Probe{{Name: "postgres", Check: func(context.Context) error { return errors.New("connection refused") }}}}
recorder := httptest.NewRecorder()
handler.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/readyz", nil))
if recorder.Code != http.StatusServiceUnavailable || !strings.Contains(recorder.Body.String(), `"reason":"DEPENDENCY_UNAVAILABLE"`) {
t.Fatalf("readiness response = %d %s", recorder.Code, recorder.Body.String())
}
}
func TestReadinessReturns200WhenAllProbesPass(t *testing.T) {
handler := ReadinessHandler{Runtime: "test", Probes: []Probe{{Name: "postgres", Check: func(context.Context) error { return nil }}}}
recorder := httptest.NewRecorder()
handler.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/readyz", nil))
if recorder.Code != http.StatusOK || !strings.Contains(recorder.Body.String(), `"status":"ready"`) {
t.Fatalf("readiness response = %d %s", recorder.Code, recorder.Body.String())
}
}
+18
View File
@@ -0,0 +1,18 @@
package agent_execution_test
import (
"testing"
"git.cin.su/m/Assistant/contracts/artifact"
)
func TestAgentArtifactAttestationRequiresProvenance(t *testing.T) {
attestation := artifact.AgentArtifactAttestation{ArtifactID: "artifact-1", ProducerRunID: "run-1", SourceRevision: "commit-1", WorktreeID: "worktree-1", ContentDigest: "sha256:artifact", TestEvidenceRef: "tests:1", CreatedAtUnixMillis: 10}
if err := attestation.Validate(); err != nil {
t.Fatal(err)
}
attestation.ContentDigest = ""
if err := attestation.Validate(); err == nil {
t.Fatal("expected missing digest rejection")
}
}
+54
View File
@@ -0,0 +1,54 @@
package agent_execution_test
import (
"context"
"testing"
"time"
"git.cin.su/m/Assistant/contracts/work"
"git.cin.su/m/Assistant/modules/work/application"
)
type canaryStore struct{ writes int }
func (s *canaryStore) SaveDispatchIntent(context.Context, work.AgentDispatchIntent) error {
s.writes++
return nil
}
func (s *canaryStore) SaveAgentLease(context.Context, work.AgentLease) error { s.writes++; return nil }
func (s *canaryStore) AcceptAgentCallback(context.Context, work.AgentCallback) error {
s.writes++
return nil
}
func canaryIntent() work.AgentDispatchIntent {
return work.AgentDispatchIntent{DispatchID: "dispatch-1", IdempotencyKey: "idem-1", DelegationID: "delegation-1", RunID: "run-1", ImplementationID: "fake-worker", ImplementationRevision: 1, SourceRevision: "source-1", WorktreeID: "worktree-1", VerificationPlanRef: "verify-1", CreatedAtUnixMillis: 1_700_000_000_000}
}
func TestCanaryRequiresExplicitEnablementAndAllowlist(t *testing.T) {
store := &canaryStore{}
config := application.CanaryConfig{Enabled: false, ImplementationID: "fake-worker", MaxDispatches: 1}
_, err := application.ExecuteCanaryDryRun(context.Background(), store, config, 0, canaryIntent(), "worker-1", 7, time.UnixMilli(1_700_000_000_000), time.Minute)
if err != application.ErrCanaryDisabled || store.writes != 0 {
t.Fatalf("disabled canary wrote state or returned wrong error: %v/%d", err, store.writes)
}
config.Enabled = true
config.ImplementationID = "other-worker"
_, err = application.ExecuteCanaryDryRun(context.Background(), store, config, 0, canaryIntent(), "worker-1", 7, time.UnixMilli(1_700_000_000_000), time.Minute)
if err != application.ErrCanaryNotAllowed || store.writes != 0 {
t.Fatalf("unallowed canary wrote state or returned wrong error: %v/%d", err, store.writes)
}
}
func TestCanaryHonorsDispatchLimit(t *testing.T) {
store := &canaryStore{}
config := application.CanaryConfig{Enabled: true, ImplementationID: "fake-worker", MaxDispatches: 1}
_, err := application.ExecuteCanaryDryRun(context.Background(), store, config, 1, canaryIntent(), "worker-1", 7, time.UnixMilli(1_700_000_000_000), time.Minute)
if err != application.ErrCanaryLimitReached || store.writes != 0 {
t.Fatalf("limited canary wrote state or returned wrong error: %v/%d", err, store.writes)
}
_, err = application.ExecuteCanaryDryRun(context.Background(), store, config, 0, canaryIntent(), "worker-1", 7, time.UnixMilli(1_700_000_000_000), time.Minute)
if err != nil || store.writes != 4 {
t.Fatalf("enabled canary did not persist expected cycle: %v/%d", err, store.writes)
}
}
+19
View File
@@ -0,0 +1,19 @@
package agent_execution_test
import (
"testing"
"git.cin.su/m/Assistant/contracts/work"
workdomain "git.cin.su/m/Assistant/modules/work/domain"
)
func TestDispatchIntentRequiresStableExecutionIdentity(t *testing.T) {
intent := work.AgentDispatchIntent{DispatchID: "dispatch-1", IdempotencyKey: "idem-1", DelegationID: "delegation-1", RunID: "run-1", ImplementationID: "agent-1", ImplementationRevision: 2, SourceRevision: "commit-1", WorktreeID: "worktree-1", VerificationPlanRef: "verify-plan-1", CreatedAtUnixMillis: 10}
if err := workdomain.ValidateDispatchIntent(intent); err != nil {
t.Fatal(err)
}
intent.IdempotencyKey = ""
if err := workdomain.ValidateDispatchIntent(intent); err == nil {
t.Fatal("expected missing idempotency rejection")
}
}
@@ -0,0 +1,39 @@
package agent_execution_test
import (
"testing"
"time"
"git.cin.su/m/Assistant/contracts/work"
workdomain "git.cin.su/m/Assistant/modules/work/domain"
)
func TestBuildDryRunDispatchProducesDeterministicCallbacks(t *testing.T) {
now := time.UnixMilli(1_700_000_000_000)
intent := work.AgentDispatchIntent{
DispatchID: "dispatch-1", IdempotencyKey: "idem-1", DelegationID: "delegation-1", RunID: "run-1",
ImplementationID: "fake-worker", ImplementationRevision: 1, SourceRevision: "source-1",
WorktreeID: "worktree-1", VerificationPlanRef: "verify-1", CreatedAtUnixMillis: now.UnixMilli(),
}
first, err := workdomain.BuildDryRunDispatch(intent, "worker-1", 7, now, 15*time.Minute)
if err != nil {
t.Fatal(err)
}
second, err := workdomain.BuildDryRunDispatch(intent, "worker-1", 7, now, 15*time.Minute)
if err != nil {
t.Fatal(err)
}
if first.CallbackCount() != 2 || first.Lease != second.Lease || first.Callbacks[0] != second.Callbacks[0] || first.Callbacks[1] != second.Callbacks[1] {
t.Fatal("dry-run dispatch is not deterministic")
}
if first.Callbacks[1].Sequence != 2 || !first.LastObservedAt().Before(first.Lease.ExpiresAt) {
t.Fatal("callback sequence or lease window is invalid")
}
}
func TestBuildDryRunDispatchRejectsInvalidIntent(t *testing.T) {
_, err := workdomain.BuildDryRunDispatch(work.AgentDispatchIntent{}, "worker-1", 1, time.UnixMilli(1_700_000_000_000), time.Minute)
if err != workdomain.ErrDryRunDispatchRejected {
t.Fatalf("expected rejection, got %v", err)
}
}
@@ -0,0 +1,40 @@
package agent_execution_test
import (
"context"
"testing"
"time"
"git.cin.su/m/Assistant/contracts/work"
"git.cin.su/m/Assistant/modules/work/application"
)
type recordingExecutionStore struct {
operations []string
}
func (s *recordingExecutionStore) SaveDispatchIntent(context.Context, work.AgentDispatchIntent) error {
s.operations = append(s.operations, "intent")
return nil
}
func (s *recordingExecutionStore) SaveAgentLease(context.Context, work.AgentLease) error {
s.operations = append(s.operations, "lease")
return nil
}
func (s *recordingExecutionStore) AcceptAgentCallback(context.Context, work.AgentCallback) error {
s.operations = append(s.operations, "callback")
return nil
}
func TestExecuteDryRunDispatchPersistsInOrder(t *testing.T) {
now := time.UnixMilli(1_700_000_000_000)
intent := work.AgentDispatchIntent{DispatchID: "dispatch-1", IdempotencyKey: "idem-1", DelegationID: "delegation-1", RunID: "run-1", ImplementationID: "fake-worker", ImplementationRevision: 1, SourceRevision: "source-1", WorktreeID: "worktree-1", VerificationPlanRef: "verify-1", CreatedAtUnixMillis: now.UnixMilli()}
store := &recordingExecutionStore{}
result, err := application.ExecuteDryRunDispatch(context.Background(), store, intent, "worker-1", 7, now, 15*time.Minute)
if err != nil {
t.Fatal(err)
}
if result.CallbackCount() != 2 || len(store.operations) != 4 || store.operations[0] != "intent" || store.operations[1] != "lease" || store.operations[2] != "callback" || store.operations[3] != "callback" {
t.Fatalf("unexpected persistence sequence: %#v", store.operations)
}
}
+50
View File
@@ -0,0 +1,50 @@
package agent_execution_test
import (
"testing"
"time"
"git.cin.su/m/Assistant/contracts/work"
workdomain "git.cin.su/m/Assistant/modules/work/domain"
)
func TestAgentLeaseFencesWorkerAndToken(t *testing.T) {
now := time.Unix(100, 0)
lease, err := workdomain.AcquireAgentLease("run-1", "worker-a", 7, now, time.Minute)
if err != nil || lease.State != work.AgentLeaseActive {
t.Fatalf("lease = %#v %v", lease, err)
}
if _, err := workdomain.RenewAgentLease(lease, "worker-b", 7, now.Add(time.Second), time.Minute); err == nil {
t.Fatal("expected worker fencing")
}
if _, err := workdomain.RenewAgentLease(lease, "worker-a", 8, now.Add(time.Second), time.Minute); err == nil {
t.Fatal("expected token fencing")
}
if renewed, err := workdomain.RenewAgentLease(lease, "worker-a", 7, now.Add(time.Second), time.Minute); err != nil || !renewed.ExpiresAt.After(lease.ExpiresAt) {
t.Fatalf("renewed = %#v %v", renewed, err)
}
}
func TestExpiredLeaseCannotRenew(t *testing.T) {
now := time.Unix(100, 0)
lease, _ := workdomain.AcquireAgentLease("run-1", "worker-a", 1, now, time.Second)
if _, err := workdomain.RenewAgentLease(lease, "worker-a", 1, now.Add(time.Second), time.Second); err == nil {
t.Fatal("expected expired lease rejection")
}
}
func TestAgentCallbackRejectsReplayAndWrongFencing(t *testing.T) {
now := time.Unix(100, 0)
lease, _ := workdomain.AcquireAgentLease("run-1", "worker-a", 1, now, time.Minute)
callback := work.AgentCallback{RunID: "run-1", WorkerID: "worker-a", FencingToken: 1, Sequence: 2, Kind: work.AgentCallbackHeartbeat, ObservedAt: now.Add(time.Second)}
if err := workdomain.ValidateAgentCallback(callback, lease, 1); err != nil {
t.Fatal(err)
}
if err := workdomain.ValidateAgentCallback(callback, lease, 2); err == nil {
t.Fatal("expected replay rejection")
}
callback.FencingToken = 2
if err := workdomain.ValidateAgentCallback(callback, lease, 1); err == nil {
t.Fatal("expected fencing rejection")
}
}
@@ -0,0 +1,33 @@
package agent_execution_test
import (
"os"
"strings"
"testing"
)
func TestDurableAgentExecutionMigrationDefinesOwnedState(t *testing.T) {
data, err := os.ReadFile("../../database/migrations/029_work_agent_execution_persistence.sql")
if err != nil {
t.Fatal(err)
}
sql := string(data)
for _, table := range []string{
"work.agent_dispatch_intents",
"work.agent_leases",
"work.agent_callback_heads",
} {
if !strings.Contains(sql, "CREATE TABLE IF NOT EXISTS "+table) {
t.Fatalf("migration does not define %s", table)
}
}
for _, marker := range []string{
"UNIQUE",
"CHECK (state IN ('ACTIVE','EXPIRED'))",
"CHECK (fencing_token > 0)",
} {
if !strings.Contains(sql, marker) {
t.Fatalf("migration/repository contract marker missing: %s", marker)
}
}
}
@@ -0,0 +1,19 @@
package agent_execution_test
import (
"testing"
"git.cin.su/m/Assistant/contracts/work"
workdomain "git.cin.su/m/Assistant/modules/work/domain"
)
func TestIndependentVerificationRequiresDistinctRun(t *testing.T) {
verification := work.IndependentVerification{ProducerRunID: "producer-1", VerifierRunID: "verifier-1", VerifierIdentity: "verifier", ArtifactID: "artifact-1", TestEvidenceRef: "tests-1", Verified: true, Summary: "passed"}
if err := workdomain.ValidateIndependentVerification(verification); err != nil {
t.Fatal(err)
}
verification.VerifierRunID = verification.ProducerRunID
if err := workdomain.ValidateIndependentVerification(verification); err == nil {
t.Fatal("expected same-run rejection")
}
}
@@ -0,0 +1,37 @@
package cognitive_admission_test
import (
"testing"
"git.cin.su/m/Assistant/contracts/capability"
)
func TestAdmitReturnsExplainableDecision(t *testing.T) {
policy := capability.AdmissionPolicy{ID: "local-v1", MaxConcurrent: 2, MaxAcceptedPerHour: 10, MaxInputBytes: 1000, MaxOutputBytes: 500}
request := capability.AdmissionRequest{Complexity: capability.AdmissionNormal, InputBytes: 100, MaxOutputBytes: 100, DeadlineMillis: 2000}
if decision := capability.Admit(request, policy, capability.AdmissionCounters{}, 1000); decision.State != capability.AdmissionAdmitted || decision.PolicyID != policy.ID {
t.Fatalf("admitted = %#v", decision)
}
checks := []struct {
counters capability.AdmissionCounters
reason capability.AdmissionReason
}{
{capability.AdmissionCounters{Concurrent: 2}, capability.AdmissionReasonConcurrency},
{capability.AdmissionCounters{AcceptedLastHour: 10}, capability.AdmissionReasonAcceptedHour},
}
for _, check := range checks {
if decision := capability.Admit(request, policy, check.counters, 1000); decision.State != capability.AdmissionRejected || decision.Reason != check.reason {
t.Fatalf("rejected = %#v want %s", decision, check.reason)
}
}
}
func TestAdmitRejectsBudgetAndDeadline(t *testing.T) {
policy := capability.AdmissionPolicy{ID: "local-v1", MaxConcurrent: 1, MaxAcceptedPerHour: 1, MaxInputBytes: 10, MaxOutputBytes: 10}
if decision := capability.Admit(capability.AdmissionRequest{InputBytes: 11, MaxOutputBytes: 1}, policy, capability.AdmissionCounters{}, 1); decision.Reason != capability.AdmissionReasonInputBudget {
t.Fatalf("input decision = %#v", decision)
}
if decision := capability.Admit(capability.AdmissionRequest{InputBytes: 1, MaxOutputBytes: 1, DeadlineMillis: 1}, policy, capability.AdmissionCounters{}, 1); decision.Reason != capability.AdmissionReasonDeadline {
t.Fatalf("deadline decision = %#v", decision)
}
}
@@ -0,0 +1,59 @@
package context_compiler_test
import (
"testing"
"git.cin.su/m/Assistant/contracts/capability"
"git.cin.su/m/Assistant/contracts/cognitive"
)
func TestCompileContextOrdersDeduplicatesAndBudgets(t *testing.T) {
bundle, err := cognitive.CompileContext(cognitive.ContextCompileInput{
MaxBytes: 4,
Candidates: []cognitive.ContextCandidate{
{Excerpt: cognitive.ContextExcerpt{ID: "low", Sensitivity: cognitive.SensitivityPublic, Text: "low"}, Relevance: 1},
{Excerpt: cognitive.ContextExcerpt{ID: "high", Sensitivity: cognitive.SensitivityPublic, Text: "éé"}, Relevance: 2},
{Excerpt: cognitive.ContextExcerpt{ID: "high", Sensitivity: cognitive.SensitivityPublic, Text: "hi"}, Relevance: 3},
},
})
if err != nil {
t.Fatal(err)
}
if len(bundle.Excerpts) != 1 || bundle.Excerpts[0].ID != "high" || bundle.Bytes != 2 {
t.Fatalf("unexpected bundle: %#v", bundle)
}
if len(bundle.Omissions) != 2 || bundle.Omissions[0].Reason != "duplicate" || bundle.Omissions[1].Reason != "budget" {
t.Fatalf("unexpected omissions: %#v", bundle.Omissions)
}
}
func TestCompileContextFiltersSensitivityByPolicy(t *testing.T) {
bundle, err := cognitive.CompileContext(cognitive.ContextCompileInput{
MaxBytes: 100,
DataPolicy: capability.DataPolicyExternalMinimizedContext,
Candidates: []cognitive.ContextCandidate{
{Excerpt: cognitive.ContextExcerpt{ID: "public", Sensitivity: cognitive.SensitivityPublic, Text: "ok"}},
{Excerpt: cognitive.ContextExcerpt{ID: "private", Sensitivity: cognitive.SensitivityPrivateAllowed, Text: "private"}},
{Excerpt: cognitive.ContextExcerpt{ID: "secret", Sensitivity: cognitive.SensitivitySecret, Text: "secret"}},
},
})
if err != nil {
t.Fatal(err)
}
if len(bundle.Excerpts) != 1 || bundle.Excerpts[0].ID != "public" {
t.Fatalf("unexpected excerpts: %#v", bundle.Excerpts)
}
if len(bundle.Omissions) != 2 {
t.Fatalf("unexpected omissions: %#v", bundle.Omissions)
}
}
func TestCompileContextAllowsEmptyResultAndRejectsInvalidBudget(t *testing.T) {
if _, err := cognitive.CompileContext(cognitive.ContextCompileInput{}); err == nil {
t.Fatal("expected invalid budget")
}
bundle, err := cognitive.CompileContext(cognitive.ContextCompileInput{MaxBytes: 10, Candidates: []cognitive.ContextCandidate{{Excerpt: cognitive.ContextExcerpt{ID: "secret", Sensitivity: cognitive.SensitivitySecret, Text: "x"}}}})
if err != nil || len(bundle.Excerpts) != 0 || len(bundle.Omissions) != 1 {
t.Fatalf("unexpected empty result: %#v %v", bundle, err)
}
}
@@ -0,0 +1,43 @@
package model_qualification_test
import (
"testing"
"git.cin.su/m/Assistant/contracts/capability"
)
func TestQualificationManifestRequiresAllEvidenceDimensions(t *testing.T) {
dimensions := []capability.QualificationDimension{capability.DimensionDialogue, capability.DimensionInterpretation, capability.DimensionCoding, capability.DimensionResearch, capability.DimensionUncertainty, capability.DimensionContext, capability.DimensionLatency, capability.DimensionResources, capability.DimensionRestart}
measurements := make([]capability.QualificationMeasurement, 0, len(dimensions))
for _, dimension := range dimensions {
measurements = append(measurements, capability.QualificationMeasurement{Dimension: dimension, Result: capability.EvaluationSupported, Artifact: "artifact:" + string(dimension)})
}
manifest := capability.QualificationManifest{ID: "manifest-1", ModelDescriptorID: "model-1", ModelRevision: 2, ModelFingerprint: "sha256:model", BenchmarkVersion: "holdout-v1", HoldoutCorpusDigest: "sha256:corpus", LicenseEvidence: "license:approved", DataPolicyEvidence: "policy:local", Measurements: measurements, ObservedAtUnixMillis: 10}
if err := manifest.Validate(); err != nil {
t.Fatal(err)
}
manifest.Measurements = manifest.Measurements[:len(manifest.Measurements)-1]
if err := manifest.Validate(); err == nil {
t.Fatal("expected incomplete manifest to be rejected")
}
}
func TestQualificationGateRejectsUnsupportedEvidence(t *testing.T) {
manifest := completeManifest()
if state := manifest.QualificationState(); state != capability.QualificationValidated {
t.Fatalf("state = %s", state)
}
manifest.Measurements[0].Result = capability.EvaluationUnsupported
if state := manifest.QualificationState(); state != capability.QualificationRejected {
t.Fatalf("unsupported state = %s", state)
}
}
func completeManifest() capability.QualificationManifest {
dimensions := []capability.QualificationDimension{capability.DimensionDialogue, capability.DimensionInterpretation, capability.DimensionCoding, capability.DimensionResearch, capability.DimensionUncertainty, capability.DimensionContext, capability.DimensionLatency, capability.DimensionResources, capability.DimensionRestart}
measurements := make([]capability.QualificationMeasurement, 0, len(dimensions))
for _, dimension := range dimensions {
measurements = append(measurements, capability.QualificationMeasurement{Dimension: dimension, Result: capability.EvaluationSupported, Artifact: "artifact:" + string(dimension)})
}
return capability.QualificationManifest{ID: "manifest-1", ModelDescriptorID: "model-1", ModelRevision: 2, ModelFingerprint: "sha256:model", BenchmarkVersion: "holdout-v1", HoldoutCorpusDigest: "sha256:corpus", LicenseEvidence: "license:approved", DataPolicyEvidence: "policy:local", Measurements: measurements, ObservedAtUnixMillis: 10}
}
@@ -0,0 +1,10 @@
$ErrorActionPreference = 'Stop'
Set-StrictMode -Version Latest
$script = Get-Content -LiteralPath (Join-Path $PSScriptRoot '../../deploy/verify-latest-postgres-backup.sh') -Raw
if ($script -notmatch "-regex '\.\*/\[0-9\]\{8\}T\[0-9\]\{6\}Z\$'") {
throw 'restore selector does not restrict candidates to timestamp directories'
}
if ($script -notmatch 'invalid timestamp') { throw 'restore selector lacks timestamp fail-closed guard' }
if ($script -match "! -name '\.\*'") { throw 'restore selector still accepts arbitrary non-hidden directories' }
Write-Output 'verify-latest backup selector tests: PASS'
+52
View File
@@ -0,0 +1,52 @@
package research_contracts_test
import (
"testing"
"git.cin.su/m/Assistant/contracts/epistemics"
)
func TestResearchContractsRequireExactEvidence(t *testing.T) {
plan := epistemics.ResearchPlan{ID: "plan-1", Question: "What changed?", SourceClasses: []string{"repository"}, FreshnessPolicy: "same-day"}
if err := plan.Validate(); err != nil {
t.Fatal(err)
}
snapshot := epistemics.SourceSnapshot{ID: "snapshot-1", SourceID: "source-1", ContentDigest: "sha256:abc", CapturedAtUnixMillis: 1, Freshness: epistemics.FreshnessCurrent}
if err := snapshot.Validate(); err != nil {
t.Fatal(err)
}
excerpt := epistemics.SourceExcerpt{ID: "excerpt-1", SnapshotID: snapshot.ID, Text: "exact evidence", Start: 0, End: 14}
if err := excerpt.Validate(); err != nil {
t.Fatal(err)
}
citation := epistemics.Citation{ID: "citation-1", ExcerptID: excerpt.ID, Evidence: epistemics.EvidenceObserved, Coverage: epistemics.CoverageComplete, Conflict: epistemics.ConflictNone, ResolvedAtUnixMillis: 2}
if err := citation.Validate(); err != nil {
t.Fatal(err)
}
}
func TestResearchContractsPreserveUnknownAndAssumedStates(t *testing.T) {
snapshot := epistemics.SourceSnapshot{ID: "snapshot-unknown", SourceID: "source-1", ContentDigest: "sha256:abc", CapturedAtUnixMillis: 1, Freshness: epistemics.FreshnessUnknown}
if err := snapshot.Validate(); err != nil {
t.Fatal(err)
}
citation := epistemics.Citation{ID: "citation-assumed", ExcerptID: "excerpt-1", Evidence: epistemics.EvidenceAssumed, Coverage: epistemics.CoveragePartial, Conflict: epistemics.ConflictPresent, ResolvedAtUnixMillis: 2}
if err := citation.Validate(); err != nil {
t.Fatal(err)
}
}
func TestResolveCitationRequiresMatchingSourceChain(t *testing.T) {
source := epistemics.Source{ID: "source-1", Locator: "https://example.test/report"}
snapshot := epistemics.SourceSnapshot{ID: "snapshot-1", SourceID: source.ID, ContentDigest: "sha256:abc", CapturedAtUnixMillis: 1, Freshness: epistemics.FreshnessStale}
excerpt := epistemics.SourceExcerpt{ID: "excerpt-1", SnapshotID: snapshot.ID, Text: "exact evidence", Start: 0, End: 14}
citation, err := epistemics.ResolveCitation(source, snapshot, excerpt, epistemics.EvidenceObserved, epistemics.CoveragePartial, epistemics.ConflictPresent, 2)
if err != nil || citation.ExcerptID != excerpt.ID || citation.Conflict != epistemics.ConflictPresent {
t.Fatalf("unexpected citation: %#v %v", citation, err)
}
wrong := snapshot
wrong.SourceID = "other-source"
if _, err := epistemics.ResolveCitation(source, wrong, excerpt, epistemics.EvidenceObserved, epistemics.CoverageComplete, epistemics.ConflictNone, 2); err == nil {
t.Fatal("expected source-chain mismatch")
}
}
+33
View File
@@ -0,0 +1,33 @@
package work_recovery_test
import (
"testing"
"time"
"git.cin.su/m/Assistant/contracts/work"
workdomain "git.cin.su/m/Assistant/modules/work/domain"
)
func TestAssessRunLivenessClassifiesDurableHeartbeat(t *testing.T) {
now := time.UnixMilli(10000)
run := work.AgentRun{ID: "run-1", DelegationID: "delegation-1", ImplementationID: "agent-1", ImplementationRevision: 1, Protocol: work.AgentExecutionProtocol("v1"), State: work.AgentRunRunning, StartedAtUnixMillis: 9000, UpdatedAtUnixMillis: 9000}
if got := workdomain.AssessRunLiveness(run, now.Add(100*time.Millisecond), time.Second); got.State != workdomain.RunLivenessStale || got.Age != 1100*time.Millisecond {
t.Fatalf("stale = %#v", got)
}
run.UpdatedAtUnixMillis = 9500
if got := workdomain.AssessRunLiveness(run, now, time.Second); got.State != workdomain.RunLivenessLive {
t.Fatalf("live = %#v", got)
}
}
func TestAssessRunLivenessDoesNotTreatCompletedAsStale(t *testing.T) {
run := work.AgentRun{State: work.AgentRunCompleted, UpdatedAtUnixMillis: 1}
got := workdomain.AssessRunLiveness(run, time.UnixMilli(10000), time.Second)
if got.State != workdomain.RunLivenessNotRun {
t.Fatalf("completed = %#v", got)
}
invalid := workdomain.AssessRunLiveness(run, time.Time{}, time.Second)
if invalid.State != workdomain.RunLivenessInvalid {
t.Fatalf("invalid = %#v", invalid)
}
}