Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9f338484a | ||
|
|
bb131147f8 | ||
|
|
22b84ead9c | ||
|
|
42c7df964b | ||
|
|
c542642bf0 | ||
|
|
fde14cf0b8 | ||
|
|
b52590c458 | ||
|
|
6854458e21 | ||
|
|
5658bed520 | ||
|
|
7252296a74 | ||
|
|
348856a9c2 | ||
|
|
7779dc37d1 | ||
|
|
c1db571a39 | ||
|
|
5f65f3d570 | ||
|
|
bb18cb4f1c | ||
|
|
3f7b1f6e9c | ||
|
|
413a432e8c |
@@ -14,9 +14,10 @@ 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")
|
||||
ErrDispatchIntentConflict = errors.New("dispatch intent idempotency conflict")
|
||||
ErrAgentLeaseUnavailable = errors.New("agent lease unavailable")
|
||||
ErrAgentCallbackRejected = errors.New("agent callback rejected")
|
||||
ErrDevelopmentGateConflict = errors.New("development gate revision conflict")
|
||||
)
|
||||
|
||||
func OpenWork(ctx context.Context, config ContinuityConfig) (*WorkRepository, error) {
|
||||
@@ -49,6 +50,56 @@ func (r *WorkRepository) Load(ctx context.Context, id work.TaskID) (work.TaskTar
|
||||
return target, nil
|
||||
}
|
||||
|
||||
func (r *WorkRepository) LoadDevelopmentGate(ctx context.Context, id string) (work.DevelopmentGate, error) {
|
||||
var gate work.DevelopmentGate
|
||||
var evidence, producer, verifier []string
|
||||
err := r.pool.QueryRow(ctx, `SELECT gate_id,revision,updated_at_unix_millis,stage,evidence_refs,proposal_ref,task_packet_ref,worktree_ref,producer_run_refs,verifier_run_refs,verification_evidence,canary_evidence,promotion_ref,rollback_ref FROM work.development_gates WHERE gate_id=$1`, id).Scan(
|
||||
&gate.ID, &gate.Revision, &gate.UpdatedAtUnixMillis, &gate.Stage, &evidence, &gate.ProposalRef, &gate.TaskPacketRef, &gate.WorktreeRef, &producer, &verifier, &gate.VerificationEvidence, &gate.CanaryEvidence, &gate.PromotionRef, &gate.RollbackRef)
|
||||
if err != nil {
|
||||
return work.DevelopmentGate{}, fmt.Errorf("load development gate: %w", err)
|
||||
}
|
||||
gate.EvidenceRefs = append([]string(nil), evidence...)
|
||||
for _, run := range producer {
|
||||
gate.ProducerRunRefs = append(gate.ProducerRunRefs, work.AgentRunID(run))
|
||||
}
|
||||
for _, run := range verifier {
|
||||
gate.VerifierRunRefs = append(gate.VerifierRunRefs, work.AgentRunID(run))
|
||||
}
|
||||
return gate, nil
|
||||
}
|
||||
|
||||
func (r *WorkRepository) SaveDevelopmentGate(ctx context.Context, gate work.DevelopmentGate, expectedRevision int64) error {
|
||||
if gate.ID == "" || expectedRevision < 0 || gate.Revision < 1 || gate.UpdatedAtUnixMillis < 1 {
|
||||
return ErrDevelopmentGateConflict
|
||||
}
|
||||
producer := make([]string, 0, len(gate.ProducerRunRefs))
|
||||
for _, run := range gate.ProducerRunRefs {
|
||||
producer = append(producer, string(run))
|
||||
}
|
||||
verifier := make([]string, 0, len(gate.VerifierRunRefs))
|
||||
for _, run := range gate.VerifierRunRefs {
|
||||
verifier = append(verifier, string(run))
|
||||
}
|
||||
if expectedRevision == 0 {
|
||||
command, err := r.pool.Exec(ctx, `INSERT INTO work.development_gates(gate_id,revision,updated_at_unix_millis,stage,evidence_refs,proposal_ref,task_packet_ref,worktree_ref,producer_run_refs,verifier_run_refs,verification_evidence,canary_evidence,promotion_ref,rollback_ref) VALUES($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14) ON CONFLICT(gate_id) DO NOTHING`, gate.ID, gate.Revision, gate.UpdatedAtUnixMillis, gate.Stage, gate.EvidenceRefs, gate.ProposalRef, gate.TaskPacketRef, gate.WorktreeRef, producer, verifier, gate.VerificationEvidence, gate.CanaryEvidence, gate.PromotionRef, gate.RollbackRef)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if command.RowsAffected() != 1 {
|
||||
return ErrDevelopmentGateConflict
|
||||
}
|
||||
return nil
|
||||
}
|
||||
command, err := r.pool.Exec(ctx, `UPDATE work.development_gates SET revision=$1,updated_at_unix_millis=$2,stage=$3,evidence_refs=$4,proposal_ref=$5,task_packet_ref=$6,worktree_ref=$7,producer_run_refs=$8,verifier_run_refs=$9,verification_evidence=$10,canary_evidence=$11,promotion_ref=$12,rollback_ref=$13 WHERE gate_id=$14 AND revision=$15`, gate.Revision, gate.UpdatedAtUnixMillis, gate.Stage, gate.EvidenceRefs, gate.ProposalRef, gate.TaskPacketRef, gate.WorktreeRef, producer, verifier, gate.VerificationEvidence, gate.CanaryEvidence, gate.PromotionRef, gate.RollbackRef, gate.ID, expectedRevision)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if command.RowsAffected() != 1 {
|
||||
return ErrDevelopmentGateConflict
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *WorkRepository) SaveDispatchIntent(ctx context.Context, intent work.AgentDispatchIntent) error {
|
||||
tx, err := r.pool.Begin(ctx)
|
||||
if err != nil {
|
||||
|
||||
@@ -6,12 +6,16 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.cin.su/m/Assistant/adapters/postgres"
|
||||
secretcontracts "git.cin.su/m/Assistant/contracts/secrets"
|
||||
"git.cin.su/m/Assistant/contracts/work"
|
||||
"git.cin.su/m/Assistant/modules/work/application"
|
||||
"git.cin.su/m/Assistant/modules/work/ports"
|
||||
platformsecrets "git.cin.su/m/Assistant/platform/secrets"
|
||||
)
|
||||
|
||||
type memoryStore struct{ writes int }
|
||||
@@ -30,14 +34,24 @@ func main() {
|
||||
enabled := flag.Bool("enable-canary", false, "explicitly enable the manual canary")
|
||||
implementation := flag.String("implementation", "fake-worker", "allowlisted deterministic implementation")
|
||||
postgresMode := flag.Bool("postgres", false, "use the Work PostgreSQL repository instead of in-memory storage")
|
||||
runID := flag.String("run-id", "", "optional unique canary run suffix; generated when omitted")
|
||||
flag.Parse()
|
||||
|
||||
now := time.UnixMilli(1_700_000_000_000)
|
||||
intent := work.AgentDispatchIntent{DispatchID: "manual-dispatch-1", IdempotencyKey: "manual-idempotency-1", DelegationID: "manual-delegation-1", RunID: "manual-run-1", ImplementationID: *implementation, ImplementationRevision: 1, SourceRevision: "manual-source-1", WorktreeID: "manual-worktree-1", VerificationPlanRef: "manual-verification-1", CreatedAtUnixMillis: now.UnixMilli()}
|
||||
suffix := *runID
|
||||
if suffix == "" {
|
||||
suffix = fmt.Sprintf("%d", time.Now().UnixNano())
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
intent := work.AgentDispatchIntent{DispatchID: "manual-dispatch-" + suffix, IdempotencyKey: "manual-idempotency-" + suffix, DelegationID: work.DelegationID("manual-delegation-" + suffix), RunID: work.AgentRunID("manual-run-" + suffix), ImplementationID: *implementation, ImplementationRevision: 1, SourceRevision: "manual-source-" + suffix, WorktreeID: "manual-worktree-" + suffix, VerificationPlanRef: "manual-verification-" + suffix, CreatedAtUnixMillis: now.UnixMilli()}
|
||||
var store ports.ExecutionStore = &memoryStore{}
|
||||
var closeStore func()
|
||||
if *postgresMode {
|
||||
repository, err := postgres.OpenWork(context.Background(), postgres.ContinuityConfig{Host: os.Getenv("POSTGRES_HOST"), Port: os.Getenv("POSTGRES_PORT"), Database: os.Getenv("POSTGRES_DATABASE"), User: os.Getenv("WORK_POSTGRES_USER"), Password: os.Getenv("WORK_POSTGRES_PASSWORD")})
|
||||
password, err := resolveDatabasePassword()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
repository, err := postgres.OpenWork(context.Background(), postgres.ContinuityConfig{Host: os.Getenv("POSTGRES_HOST"), Port: os.Getenv("POSTGRES_PORT"), Database: os.Getenv("POSTGRES_DATABASE"), User: os.Getenv("WORK_POSTGRES_USER"), Password: password})
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
@@ -67,3 +81,27 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func resolveDatabasePassword() (string, error) {
|
||||
if _, ok := os.LookupEnv("WORK_POSTGRES_PASSWORD"); ok {
|
||||
return "", fmt.Errorf("WORK_POSTGRES_PASSWORD direct environment secret is forbidden")
|
||||
}
|
||||
path, ref := os.Getenv("WORK_POSTGRES_PASSWORD_FILE"), os.Getenv("WORK_POSTGRES_CREDENTIAL_REF")
|
||||
if path == "" || ref == "" {
|
||||
return "", fmt.Errorf("WORK_POSTGRES_PASSWORD_FILE and WORK_POSTGRES_CREDENTIAL_REF are required")
|
||||
}
|
||||
root := os.Getenv("SECRET_BACKEND_ROOT")
|
||||
if root == "" {
|
||||
root = "/run/secrets"
|
||||
}
|
||||
cleanRoot := filepath.Clean(root)
|
||||
cleanPath := filepath.Clean(path)
|
||||
if cleanPath != cleanRoot && !strings.HasPrefix(cleanPath, cleanRoot+string(os.PathSeparator)) {
|
||||
return "", fmt.Errorf("database password file escapes protected secret root")
|
||||
}
|
||||
resolver, err := platformsecrets.NewFileResolver(cleanRoot)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return resolver.ResolveFile(secretcontracts.SecretFileRef{CredentialRef: secretcontracts.CredentialRef(ref), Purpose: secretcontracts.SecretPurposeDatabasePassword, Consumer: secretcontracts.RuntimeConsumerWorkWorker, Path: cleanPath})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestResolveDatabasePasswordRejectsDirectEnvironmentSecret(t *testing.T) {
|
||||
t.Setenv("WORK_POSTGRES_PASSWORD", "literal-secret")
|
||||
if _, err := resolveDatabasePassword(); err == nil {
|
||||
t.Fatal("direct database password environment variable was accepted")
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveDatabasePasswordRejectsPathOutsideProtectedRoot(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
t.Setenv("SECRET_BACKEND_ROOT", root)
|
||||
t.Setenv("WORK_POSTGRES_PASSWORD_FILE", filepath.Join(root, "..", "outside"))
|
||||
t.Setenv("WORK_POSTGRES_CREDENTIAL_REF", "credential://domovoy/work/db-password/v1")
|
||||
if _, err := resolveDatabasePassword(); err == nil {
|
||||
t.Fatal("database password path outside protected root was accepted")
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,8 @@ const (
|
||||
|
||||
type DevelopmentGate struct {
|
||||
ID string
|
||||
Revision int64
|
||||
UpdatedAtUnixMillis int64
|
||||
Stage DevelopmentStage
|
||||
EvidenceRefs []string
|
||||
ProposalRef string
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
CREATE TABLE IF NOT EXISTS work.development_gates (
|
||||
gate_id TEXT PRIMARY KEY,
|
||||
revision BIGINT NOT NULL CHECK (revision > 0),
|
||||
updated_at_unix_millis BIGINT NOT NULL CHECK (updated_at_unix_millis > 0),
|
||||
stage TEXT NOT NULL,
|
||||
evidence_refs TEXT[] NOT NULL,
|
||||
proposal_ref TEXT NOT NULL,
|
||||
task_packet_ref TEXT NOT NULL,
|
||||
worktree_ref TEXT NOT NULL,
|
||||
producer_run_refs TEXT[] NOT NULL,
|
||||
verifier_run_refs TEXT[] NOT NULL,
|
||||
verification_evidence TEXT NOT NULL,
|
||||
canary_evidence TEXT NOT NULL,
|
||||
promotion_ref TEXT NOT NULL,
|
||||
rollback_ref TEXT NOT NULL
|
||||
);
|
||||
@@ -0,0 +1,6 @@
|
||||
BEGIN;
|
||||
|
||||
ALTER TABLE work.development_gates OWNER TO work_role;
|
||||
GRANT SELECT, INSERT, UPDATE ON work.development_gates TO work_role;
|
||||
|
||||
COMMIT;
|
||||
@@ -99,8 +99,12 @@ services:
|
||||
POSTGRES_PORT: "5432"
|
||||
POSTGRES_DATABASE: ${POSTGRES_DB:-assistant}
|
||||
WORK_POSTGRES_USER: work_role
|
||||
WORK_POSTGRES_PASSWORD: ${WORK_DB_PASSWORD}
|
||||
WORK_POSTGRES_CREDENTIAL_REF: credential://domovoy/work/db-password/v1
|
||||
WORK_POSTGRES_PASSWORD_FILE: /run/secrets/work_db_password
|
||||
SECRET_BACKEND_ROOT: /run/secrets
|
||||
depends_on: { postgres: { condition: service_healthy } }
|
||||
volumes:
|
||||
- assistant_core_secrets:/run/secrets:ro
|
||||
|
||||
agent-worker:
|
||||
build: { context: .., dockerfile: deploy/images/runtime.Dockerfile, args: { TARGET: agent-worker, SOURCE_REVISION: "${ASSISTANT_SOURCE_REVISION:-unknown}" } }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"task_file": "dev/tasks/DEV-000191-cleanup-obsolete-audit-descriptions.json",
|
||||
"task_id": "DEV-000191",
|
||||
"task_file": "dev/tasks/DEV-000198-development-gate-live-evidence.json",
|
||||
"task_id": "DEV-000198",
|
||||
"task_revision": 1
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"task_id": "DEV-000192",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ADR-0018",
|
||||
"base_repo_revision": "413a432e8c661ff702779c5288049eaede948c1b",
|
||||
"risk_tier": "E",
|
||||
"intent": "Record live reconciliation evidence for the durable Learning reflection worker and update the audit direction without changing runtime behavior.",
|
||||
"requirement_ids": ["Learning reflection loop", "reflection backlog reconciliation", "live evidence provenance"],
|
||||
"allowed_change_scope": {"modules": [], "paths": ["docs/PROJECT_STATUS.md", "docs/evidence/reflection-runtime-reconciliation-2026-08-23-v1.json", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000192-reflection-live-reconciliation-evidence.json"]},
|
||||
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "modules/**", "cmd/**", "contracts/**", ".gitea/**"]},
|
||||
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
|
||||
"contracts": {"consume": ["reflection worker status contract", "epistemics reflection candidate state"], "may_change": ["audit status projection", "append-only live evidence record"]},
|
||||
"semantic_owner": "project-governance",
|
||||
"canonical_state": ["epistemics.reflection_candidates"],
|
||||
"state_transitions": ["open backlog assessment -> reconciled resolved state evidence"],
|
||||
"side_effects_and_privileges": ["documentation and evidence only; no database mutation"],
|
||||
"persistence_and_migrations": ["none"],
|
||||
"failure_retry_restart": ["worker no-op and durable state query are retained as evidence; no inferred candidate resolution"],
|
||||
"assumptions": ["the production worker's one-shot no-candidate result and the authoritative state query were captured from docker.cin.su"],
|
||||
"acceptance_scenarios": ["worker runs without a pending candidate", "database state reports 31 RESOLVED reflection candidates and no OPEN backlog", "status marks the reflection loop reconciled", "Agent Execution and UI gaps remain explicit"],
|
||||
"required_tests": ["go test ./...", "go vet ./...", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "go run ./cmd/devctl independent", "git diff --check", "CI", "production public health check"],
|
||||
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"task_id": "DEV-000193",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ADR-0018",
|
||||
"base_repo_revision": "bb18cb4f1c18aee7c22ddbe7259123f96f779b8c",
|
||||
"risk_tier": "E",
|
||||
"intent": "Record the current live model fleet degradation and truthful FAST-first fallback selection after the opportunistic HEAVY provider timeout.",
|
||||
"requirement_ids": ["model fleet/failover", "CPU-first current operation", "truthful degraded readiness"],
|
||||
"allowed_change_scope": {"modules": [], "paths": ["docs/PROJECT_STATUS.md", "docs/evidence/audit-runtime-status-2026-08-23-v1.json", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000193-fleet-live-degraded-evidence.json"]},
|
||||
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "modules/**", "cmd/**", "contracts/**", ".gitea/**"]},
|
||||
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
|
||||
"contracts": {"consume": ["live /v1/fleet/readiness response", "existing FleetReadiness and fallback policy"], "may_change": ["fleet evidence projection"]},
|
||||
"semantic_owner": "project-governance",
|
||||
"canonical_state": ["cognitive-worker fleet readiness projection"],
|
||||
"state_transitions": ["HEAVY ready -> timeout/unavailable", "preferred FAST remains selected", "LIFEBOAT remains fallback tier"],
|
||||
"side_effects_and_privileges": ["documentation and evidence only; no provider activation or deployment configuration mutation"],
|
||||
"persistence_and_migrations": ["none"],
|
||||
"failure_retry_restart": ["provider timeout remains degraded evidence; no unavailable model is claimed ready"],
|
||||
"assumptions": ["the live endpoint response was captured from docker.cin.su after the current production deployment"],
|
||||
"acceptance_scenarios": ["FAST/MEDIUM/LIFEBOAT are ready", "HEAVY timeout is explicit", "FAST is selected for CPU-first operation", "LIFEBOAT is the declared fallback", "Agent bridge and authenticated UI gaps remain unchanged"],
|
||||
"required_tests": ["go test ./...", "go vet ./...", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "go run ./cmd/devctl independent", "git diff --check", "CI", "production public health check"],
|
||||
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"task_id": "DEV-000194",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ADR-0018",
|
||||
"base_repo_revision": "c1db571a39514e65156123b7f9394645183cf2ab",
|
||||
"risk_tier": "E",
|
||||
"intent": "Move the explicit production Agent Execution dry-run canary from direct database secret environment input to protected file/CredentialRef storage and preserve durable lease/callback evidence.",
|
||||
"requirement_ids": ["Agent Execution runtime and recovery", "protected canary configuration", "durable lease/callback canary"],
|
||||
"allowed_change_scope": {"modules": ["agent-canary", "deployment"], "paths": ["cmd/agent-canary/main.go", "cmd/agent-canary/main_test.go", "deploy/docker-compose.prod.yml", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000194-agent-canary-protected-storage.json"]},
|
||||
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "modules/**", "contracts/**", ".gitea/**"]},
|
||||
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 1, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
|
||||
"contracts": {"consume": ["existing protected secret resolver", "existing ExecutionStore and dry-run canary contract"], "may_change": ["agent-canary runtime configuration"]},
|
||||
"semantic_owner": "work-agent-execution",
|
||||
"canonical_state": ["work.agent_dispatch_intents", "work.agent_leases", "work.agent_callback_heads"],
|
||||
"state_transitions": ["explicit canary -> durable dispatch intent -> lease -> heartbeat callback -> result callback"],
|
||||
"side_effects_and_privileges": ["manual opt-in dry-run only; protected database credential read; no external provider or source mutation"],
|
||||
"persistence_and_migrations": ["none"],
|
||||
"failure_retry_restart": ["missing/invalid protected secret fails before database open; direct secret env is rejected; canary remains explicit opt-in"],
|
||||
"assumptions": ["the production compose secret volume already contains the work database credential reference used by agent-worker"],
|
||||
"acceptance_scenarios": ["direct password environment is rejected", "path outside protected root is rejected", "compose renders canary with protected file/ref only", "production opt-in canary writes lease and callback evidence", "normal agent-worker remains recovery-only without an external bridge"],
|
||||
"required_tests": ["go test ./...", "go vet ./...", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "go run ./cmd/devctl independent", "git diff --check", "CI", "production canary and public health check"],
|
||||
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"task_id": "DEV-000195",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ADR-0018",
|
||||
"base_repo_revision": "7252296a74ab7ff236fae865d63692e0fc6a777d",
|
||||
"risk_tier": "E",
|
||||
"intent": "Make the explicit Agent Execution dry-run canary replayable by generating unique dispatch, lease and callback identities per invocation while retaining durable evidence.",
|
||||
"requirement_ids": ["Agent Execution runtime and recovery", "replayable durable canary", "lease/callback evidence"],
|
||||
"allowed_change_scope": {"modules": ["agent-canary"], "paths": ["cmd/agent-canary/main.go", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000195-replayable-agent-canary-run-ids.json"]},
|
||||
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "modules/**", "contracts/**", ".gitea/**"]},
|
||||
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
|
||||
"contracts": {"consume": ["existing canary dry-run contract", "existing durable Work execution records"], "may_change": ["canary identity generation"]},
|
||||
"semantic_owner": "work-agent-execution",
|
||||
"canonical_state": ["work.agent_dispatch_intents", "work.agent_leases", "work.agent_callback_heads"],
|
||||
"state_transitions": ["each explicit canary invocation creates a fresh dispatch identity and durable lease/callback chain"],
|
||||
"side_effects_and_privileges": ["manual dry-run only; no external execution or source mutation"],
|
||||
"persistence_and_migrations": ["none"],
|
||||
"failure_retry_restart": ["repeated invocation does not reuse prior canary identity; lease conflicts remain explicit errors"],
|
||||
"assumptions": ["the protected-storage canary configuration from DEV-000194 is already in main"],
|
||||
"acceptance_scenarios": ["default invocation generates a unique suffix", "operator-supplied run-id remains attributable", "two production canary invocations persist independent durable records"],
|
||||
"required_tests": ["go test ./...", "go vet ./...", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "go run ./cmd/devctl independent", "git diff --check", "CI", "production canary and public health check"],
|
||||
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"task_id": "DEV-000196",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ADR-0018",
|
||||
"base_repo_revision": "6854458e21ad7ec1c4b736dfabf916ae4f7fac12",
|
||||
"risk_tier": "E",
|
||||
"intent": "Record independently verified production evidence for the protected replayable Agent Execution dry-run canary.",
|
||||
"requirement_ids": ["Agent Execution runtime and recovery", "durable canary evidence"],
|
||||
"allowed_change_scope": {"modules": ["governance status", "evidence"], "paths": ["docs/PROJECT_STATUS.md", "docs/evidence/agent-execution-dry-run-canary-2026-08-23-v1.json", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000196-agent-canary-live-evidence.json"]},
|
||||
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "modules/**", "contracts/**", ".gitea/**"]},
|
||||
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
|
||||
"contracts": {"consume": ["existing protected canary contract", "existing durable Work execution records"], "may_change": ["status projection and evidence only"]},
|
||||
"semantic_owner": "governance",
|
||||
"canonical_state": ["work.agent_dispatch_intents", "work.agent_leases", "work.agent_callback_heads"],
|
||||
"state_transitions": ["none; evidence records already completed canary state"],
|
||||
"side_effects_and_privileges": ["read-only status/evidence change after an explicit manual dry-run canary"],
|
||||
"persistence_and_migrations": ["none"],
|
||||
"failure_retry_restart": ["no runtime retry; retain the negative external-bridge finding"],
|
||||
"assumptions": ["the production canary output and database rows were captured without secret values"],
|
||||
"acceptance_scenarios": ["evidence contains callback_count 2", "evidence contains durable dispatch intent, ACTIVE lease and callback sequence 2", "status explicitly retains the external bridge gap"],
|
||||
"required_tests": ["go test ./...", "go vet ./...", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "go run ./cmd/devctl independent", "git diff --check", "CI", "production public health check"],
|
||||
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"task_id": "DEV-000197",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ADR-0018",
|
||||
"base_repo_revision": "fde14cf0b81e2772c81ee7cb9b0335b56943f6e4",
|
||||
"risk_tier": "E",
|
||||
"intent": "Persist the governed self-development gate as a Work-owned revisioned state machine with optimistic concurrency, without executing code or granting promotion authority.",
|
||||
"requirement_ids": ["WP-701", "durable governed self-development provenance", "CAS stage transitions"],
|
||||
"allowed_change_scope": {"modules": ["work", "postgres persistence"], "paths": ["contracts/work/self_development.go", "modules/work/ports/development.go", "adapters/postgres/work_repository.go", "database/migrations/037_work_development_gates.sql", "database/migrations/038_work_development_gates_privileges.sql", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000197-durable-development-gate.json"]},
|
||||
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "deploy/**", "cmd/**", "modules/identity/**", "contracts/authority/**", ".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": ["existing DevelopmentGate state machine", "Work-owned persistence boundary"], "may_change": ["revision metadata on DevelopmentGate"]},
|
||||
"semantic_owner": "work-agent-execution",
|
||||
"canonical_state": ["work.development_gates"],
|
||||
"state_transitions": ["persist initial evidence gate", "advance only through caller-validated stage with expected revision", "reject stale revision"],
|
||||
"side_effects_and_privileges": ["database persistence only; no worktree, provider, source, deployment or promotion mutation"],
|
||||
"persistence_and_migrations": ["037_work_development_gates.sql", "038_work_development_gates_privileges.sql"],
|
||||
"failure_retry_restart": ["CAS conflict is explicit and retry requires reloading the gate; restart reloads durable provenance"],
|
||||
"assumptions": ["domain AdvanceDevelopmentGate remains the sole stage transition validator", "external bridge is a later independently operated capability"],
|
||||
"acceptance_scenarios": ["initial gate persists at revision 1", "update with expected revision succeeds", "stale revision is rejected", "all provenance fields round-trip without secrets", "work_role can read and write the table"],
|
||||
"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 -task dev/tasks/DEV-000197-durable-development-gate.json", "git diff --check", "CI", "production migration and health check"],
|
||||
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "REQUIRES_MIGRATION_DECISION"]
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"task_id": "DEV-000198",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ADR-0018",
|
||||
"base_repo_revision": "bb131147f80cb75c0ba3d80607d0f806bd378aa8",
|
||||
"risk_tier": "E",
|
||||
"intent": "Record live production evidence for durable governed self-development gate persistence and Work-owner access.",
|
||||
"requirement_ids": ["WP-701", "live durable gate evidence"],
|
||||
"allowed_change_scope": {"modules": ["governance status", "evidence"], "paths": ["docs/PROJECT_STATUS.md", "docs/evidence/governed-development-gate-persistence-2026-08-23-v1.json", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000198-development-gate-live-evidence.json"]},
|
||||
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "cmd/**", "modules/**", "contracts/**", ".gitea/**"]},
|
||||
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
|
||||
"contracts": {"consume": ["Work development gate persistence contract", "production health"], "may_change": ["status and evidence projection only"]},
|
||||
"semantic_owner": "governance",
|
||||
"canonical_state": ["work.development_gates"],
|
||||
"state_transitions": ["none; evidence records applied migration and owner read access"],
|
||||
"side_effects_and_privileges": ["read-only production verification"],
|
||||
"persistence_and_migrations": ["no migration"],
|
||||
"failure_retry_restart": ["retain explicit external bridge gap"],
|
||||
"assumptions": ["migration 037 and privilege forward migration 038 are present in production"],
|
||||
"acceptance_scenarios": ["work_role can query the relation", "public health is ok", "status distinguishes durable gate persistence from bridge-backed execution"],
|
||||
"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 -task dev/tasks/DEV-000198-development-gate-live-evidence.json", "git diff --check", "CI", "production health and schema check"],
|
||||
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY"]
|
||||
}
|
||||
@@ -6,17 +6,17 @@
|
||||
- `FAST_PROVIDER_STATUS = LIVE_SMOKE_SUPPORTED`: local CPU gateway authentication is configured through a protected file reference; four bounded local-only advisory cases completed through cognitive-worker. Evidence: `docs/evidence/wp-601-fast-gateway-live-smoke-2026-08-22-v1.json`.
|
||||
- `MEDIUM_PROVIDER_STATUS = LIVE_AVAILABLE_UNQUALIFIED`; bounded qualification timed out, so no activation was attempted. Evidence: `docs/evidence/wp-200-medium-qualification-2026-08-22-v3.json`.
|
||||
- `HEAVY_PROVIDER_STATUS = LIVE_QUALIFIED_UNACTIVATED`; the qualified GPU binding remains inactive. Evidence: `docs/evidence/wp-200-heavy-qualification-2026-08-22-v2.json`.
|
||||
- `AGENT_EXECUTION_STATUS = RECOVERY_ONLY`; no independently operated bridge endpoint is configured. Durable recovery and status endpoints remain active.
|
||||
- `FLEET_EXECUTION_EVIDENCE = docs/evidence/audit-runtime-status-2026-08-23-v1.json`; FAST, MEDIUM, HEAVY and LIFEBOAT are readiness-ready, preferred and selected tier is FAST, and LIFEBOAT is the fallback tier.
|
||||
- `AGENT_EXECUTION_STATUS = DURABLE_DRY_RUN_CANARY_VERIFIED`; the explicit production canary persisted a dispatch intent, an ACTIVE lease and callback head sequence 2. No independently operated bridge endpoint is configured, so bridge-backed execution remains unavailable. Evidence: `docs/evidence/agent-execution-dry-run-canary-2026-08-23-v1.json`.
|
||||
- `FLEET_EXECUTION_EVIDENCE = docs/evidence/audit-runtime-status-2026-08-23-v1.json`; FAST, MEDIUM and LIFEBOAT are ready, HEAVY is explicitly degraded by provider timeout, FAST remains preferred/selected, and LIFEBOAT is the fallback tier.
|
||||
- `TRUTH_RESEARCH_STATUS = LIVE_CANARY_PERSISTED`; the web research canary preserved an OBSERVED/COMPLETE/NO-CONFLICT source chain while leaving the claim UNVERIFIED and belief PROVISIONAL. Evidence: `docs/evidence/truth-runtime-canary-2026-08-23-v1.json`.
|
||||
- `LEARNING_REFLECTION_STATUS = RUNNING_FOUNDATION`; reflection worker is running, but historical candidate backlog closure is not claimed.
|
||||
- `LEARNING_REFLECTION_STATUS = LIVE_RECONCILED`; reflection worker returned no pending candidate and the durable state contains 31 RESOLVED candidates with no OPEN backlog. Evidence: `docs/evidence/reflection-runtime-reconciliation-2026-08-23-v1.json`.
|
||||
- `BACKUP_RESTORE_STATUS = VERIFIED`; latest checksum and isolated restore evidence is retained in `docs/evidence/wp-002-postgres-restore-2026-08-22-v1.json`.
|
||||
- `GOVERNED_SELF_DEVELOPMENT_STATUS = PROPOSAL_AND_SCAFFOLD_ONLY`; no bridge-backed source mutation, promotion or rollback is claimed.
|
||||
- `GOVERNED_SELF_DEVELOPMENT_STATUS = DURABLE_GATE_PERSISTED`; Work-owned revisioned gate storage and optimistic-concurrency persistence are live. No bridge-backed source mutation, promotion or rollback is claimed. Evidence: `docs/evidence/governed-development-gate-persistence-2026-08-23-v1.json`.
|
||||
- `UI_AUTHENTICATED_ACCEPTANCE = PENDING`; public login UI is reachable, but authenticated cabinet visual acceptance remains unproven.
|
||||
|
||||
## Current governance checkpoint
|
||||
|
||||
- `ACTIVE_TASK = dev/tasks/DEV-000190-live-audit-status-sync.json revision 1`
|
||||
- `ACTIVE_TASK = dev/tasks/DEV-000198-development-gate-live-evidence.json revision 1`
|
||||
- `LINEAGE_DECISION = docs/GOVERNANCE_LINEAGE_DECISION_2026-08-22.md`; historical packets remain provenance and are not current authority.
|
||||
- `REGISTRY_STATUS = YAML and JSON semantic mirrors agree`
|
||||
- `PRODUCTION_DOMAIN = docker.cin.su`; shared docker-test is not used for this project.
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"captured_at": "2026-08-23",
|
||||
"environment": "production",
|
||||
"endpoint": "internal agent-canary profile on docker.cin.su",
|
||||
"run_id": "manual-run-1787444046393734499",
|
||||
"dispatch_id": "manual-dispatch-1787444046393734499",
|
||||
"canary_output": {
|
||||
"callback_count": 2,
|
||||
"store_writes": 0
|
||||
},
|
||||
"durable_database_verification": {
|
||||
"dispatch_intent": "manual-dispatch-1787444046393734499",
|
||||
"lease": {
|
||||
"run_id": "manual-run-1787444046393734499",
|
||||
"worker_id": "manual-worker",
|
||||
"state": "ACTIVE"
|
||||
},
|
||||
"callback_head": {
|
||||
"run_id": "manual-run-1787444046393734499",
|
||||
"last_sequence": 2
|
||||
}
|
||||
},
|
||||
"interpretation": "The explicit protected dry-run canary is replayable and its dispatch, lease and callback chain are durable. This does not prove execution through an independently operated external bridge; the production agent worker remains recovery-only until that bridge is configured and qualified.",
|
||||
"secrets_exposed": false
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
"reflection_worker": "running",
|
||||
"agent_worker": {"ready": false, "mode": "recovery-only", "executor_configured": false, "reason": "protected agent execution bridge URL is not configured"}
|
||||
},
|
||||
"fleet": {"ready_tiers": ["FAST", "MEDIUM", "HEAVY", "LIFEBOAT"], "preferred_tier": "FAST", "selected_tier": "FAST", "fallback_tier": "LIFEBOAT"},
|
||||
"fleet": {"ready_tiers": ["FAST", "MEDIUM", "LIFEBOAT"], "preferred_tier": "FAST", "selected_tier": "FAST", "fallback_tier": "LIFEBOAT", "heavy": {"ready": false, "reason": "provider /v1/models request timed out"}},
|
||||
"work_graph_canary": {"endpoint": "internal work-graph-runtime /v1/work-graphs/synthesis", "result": "HTTP 200; candidates, conflict and VERIFIED synthesis persisted"},
|
||||
"interpretation": "Readiness and canary evidence prove runtime behavior only; they do not prove external Agent Execution bridge availability or authenticated UI acceptance."
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"captured_at": "2026-08-23",
|
||||
"production_revision": "bb131147f80cb75c0ba3d80607d0f806bd378aa8",
|
||||
"migration": "038_work_development_gates_privileges.sql",
|
||||
"database_verification": {
|
||||
"relation": "work.development_gates",
|
||||
"work_role_select_count": 0,
|
||||
"privilege_result": "SELECT succeeded as work_role"
|
||||
},
|
||||
"public_health": {"url": "https://assistant.cin.su/healthz", "status": "ok"},
|
||||
"interpretation": "The governed self-development gate is now durable and accessible to its Work owner with revisioned persistence. This evidence does not prove external bridge execution, source mutation, promotion or rollback.",
|
||||
"secrets_exposed": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"captured_at": "2026-08-23",
|
||||
"runtime": "reflection-worker",
|
||||
"one_shot_result": "no reflection candidate",
|
||||
"durable_state": {"reflection_candidates": {"RESOLVED": 31, "OPEN": 0, "other": 0}},
|
||||
"interpretation": "The durable reflection candidate backlog is reconciled. This proves the current worker/state loop, not autonomous source mutation or skill promotion."
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package ports
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.cin.su/m/Assistant/contracts/work"
|
||||
)
|
||||
|
||||
// DevelopmentGateStore persists only the governed state machine and its
|
||||
// provenance. It does not create worktrees, execute code or grant promotion
|
||||
// authority.
|
||||
type DevelopmentGateStore interface {
|
||||
LoadDevelopmentGate(context.Context, string) (work.DevelopmentGate, error)
|
||||
SaveDevelopmentGate(context.Context, work.DevelopmentGate, int64) error
|
||||
}
|
||||
Reference in New Issue
Block a user