Compare commits
150
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd00dabf32 | ||
|
|
33365ce7ee | ||
|
|
5017a854ee | ||
|
|
839b35f310 | ||
|
|
96687140ce | ||
|
|
b282038949 | ||
|
|
4de506a2af | ||
|
|
36272b9036 | ||
|
|
a779568622 | ||
|
|
243a11490b | ||
|
|
26207d93ef | ||
|
|
42871f2e47 | ||
|
|
6ea1d4b729 | ||
|
|
c6d1ebf36f | ||
|
|
25d8d6d219 | ||
|
|
6cbef9af41 | ||
|
|
2fe9802e5a | ||
|
|
0882df3ad2 | ||
|
|
0acbe53fad | ||
|
|
e634bb946a | ||
|
|
c7ad996cae | ||
|
|
a065382512 | ||
|
|
8a8e571905 | ||
|
|
301dbdcfb2 | ||
|
|
8037e66998 | ||
|
|
29815c49b8 | ||
|
|
c14ad7fd0b | ||
|
|
939a5152e1 | ||
|
|
4e54ace76b | ||
|
|
f329224d12 | ||
|
|
bb76171af4 | ||
|
|
213f06661d | ||
|
|
d350b9a565 | ||
|
|
f862c3ac13 | ||
|
|
5e18eb6082 | ||
|
|
ec86c983e3 | ||
|
|
ea854d5278 | ||
|
|
bbecadcec2 | ||
|
|
1c89b30512 | ||
|
|
75a8533939 | ||
|
|
515a98c470 | ||
|
|
d3ad1fd5bb | ||
|
|
e1802aef12 | ||
|
|
35fc2438dc | ||
|
|
70335a78e2 | ||
|
|
41067a0b16 | ||
|
|
fba05d5ed4 | ||
|
|
6e14ba1727 | ||
|
|
9c6e2097b1 | ||
|
|
96559f6ab8 | ||
|
|
46b89577eb | ||
|
|
a6ddbfaaf2 | ||
|
|
362a1bd993 | ||
|
|
735222de0d | ||
|
|
4e3d153557 | ||
|
|
f316f7ec3f | ||
|
|
da5d06895b | ||
|
|
4d1a579132 | ||
|
|
43836af511 | ||
|
|
32457f7142 | ||
|
|
204eee489c | ||
|
|
5cd8665aba | ||
|
|
04018cbd07 | ||
|
|
23a1eeae56 | ||
|
|
369588ada7 | ||
|
|
e323689fd4 | ||
|
|
af042dc69f | ||
|
|
60c1c31bd9 | ||
|
|
ff3f14fc77 | ||
|
|
36f991d8d0 | ||
|
|
54a3996c17 | ||
|
|
a769f33d16 | ||
|
|
056ff1b795 | ||
|
|
7b2debb60e | ||
|
|
6dfb3fc26c | ||
|
|
e0a093eaf3 | ||
|
|
24916aa8f7 | ||
|
|
d7afd5be34 | ||
|
|
e104ce790d | ||
|
|
d7484cf757 | ||
|
|
a57e8ea71b | ||
|
|
9dffde63b8 | ||
|
|
4e5bb9abc9 | ||
|
|
3435de8013 | ||
|
|
76c2824887 | ||
|
|
fc00a57942 | ||
|
|
1b21888e8b | ||
|
|
012f5efafe | ||
|
|
eb842e24ff | ||
|
|
13bbcdf747 | ||
|
|
c00b9b1549 | ||
|
|
2d88d488f4 | ||
|
|
72c9cfc893 | ||
|
|
0b619bdc08 | ||
|
|
db23e984da | ||
|
|
f0c8f39986 | ||
|
|
5d8ec1995a | ||
|
|
75d707e960 | ||
|
|
89b053b7bf | ||
|
|
e90aded6d3 | ||
|
|
8456ecd237 | ||
|
|
b89fdef1d9 | ||
|
|
6f3d576602 | ||
|
|
b026ec151e | ||
|
|
5d4c3877b1 | ||
|
|
113c5ab965 | ||
|
|
bce7993a6a | ||
|
|
3bbf416c20 | ||
|
|
b62b3b0dc3 | ||
|
|
7e72474b0a | ||
|
|
b05081328e | ||
|
|
0e7d1a7a50 | ||
|
|
2aaa73bb2a | ||
|
|
9000d90d72 | ||
|
|
4afcfd3846 | ||
|
|
6ba8265686 | ||
|
|
21583528c0 | ||
|
|
b1bb48f5b5 | ||
|
|
88f5f6e8f8 | ||
|
|
5439db2cc3 | ||
|
|
208cb18ee0 | ||
|
|
5d19becdaf | ||
|
|
4cd3ecdf15 | ||
|
|
8e26f529f7 | ||
|
|
3cc636d6b7 | ||
|
|
14dc439c82 | ||
|
|
21887988b2 | ||
|
|
232a57eed4 | ||
|
|
d1fd70e436 | ||
|
|
1d73162df1 | ||
|
|
7788cf4b44 | ||
|
|
d9dcddbdbb | ||
|
|
5fb616310d | ||
|
|
5a9cf7a22d | ||
|
|
d649f79ca4 | ||
|
|
ffce659332 | ||
|
|
24b30bf669 | ||
|
|
1bd9137761 | ||
|
|
f895c5a104 | ||
|
|
d0074709aa | ||
|
|
982eb6f49b | ||
|
|
b1ee085de4 | ||
|
|
2c66a44910 | ||
|
|
b95714a71e | ||
|
|
6183d811f1 | ||
|
|
6a2ed287ba | ||
|
|
d342728b46 | ||
|
|
f121445f24 | ||
|
|
124871da76 | ||
|
|
2062094228 |
@@ -72,3 +72,16 @@ State:
|
||||
- assumptions;
|
||||
- findings not changed;
|
||||
- gaps or limitations.
|
||||
|
||||
## Domovoy Gen2 execution governance
|
||||
|
||||
When the active task is a Gen2 packet, read the canonical documents before editing:
|
||||
|
||||
- `docs/DOMOVOY_CONSTITUTION.md`
|
||||
- `architecture/GEN2_TARGET_ARCHITECTURE.md`
|
||||
- `architecture/GEN2_CANONICAL_DATA_MODEL.md`
|
||||
- `development/GEN2_MIGRATION_MAP.md`
|
||||
- `development/GEN2_MASTER_PLAN.md`
|
||||
- `development/GEN2_EXECUTION_CONTRACT.md`
|
||||
|
||||
The active task packet and `development/GEN2_TASK_INDEX.md` define execution order. `PRE-GEN2-000` freezes the V1 baseline; `ARCH-GEN2-000` accepts Gen2 governance; later `GEN2-*` packets must not be started automatically. Gen2 architecture and ownership changes are permitted only within the active packet's declared scope and authority. Default validation is FAST; MILESTONE or FULL is required only when the selected packet explicitly says so.
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
@@ -275,6 +276,20 @@ func (c ComfyImageGenerationClient) Generate(ctx context.Context, model, prompt,
|
||||
if accepted.PromptID == "" {
|
||||
return GeneratedImage{}, ErrEmptyResult
|
||||
}
|
||||
// A caller timeout must remove only this prompt. ComfyUI's global
|
||||
// /interrupt endpoint can otherwise stop unrelated users' work and leave
|
||||
// queued requests in an ambiguous state.
|
||||
defer func() {
|
||||
if ctx.Err() != nil {
|
||||
cancelCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
if err := c.cancelPrompt(cancelCtx, accepted.PromptID); err != nil {
|
||||
// The original context error is more useful to the caller; the
|
||||
// provider cleanup failure remains observable in the server log.
|
||||
log.Printf("comfy prompt cleanup failed for %s: %v", accepted.PromptID, err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
interval := c.PollInterval
|
||||
if interval <= 0 {
|
||||
@@ -306,6 +321,30 @@ func (c ComfyImageGenerationClient) Generate(ctx context.Context, model, prompt,
|
||||
}
|
||||
}
|
||||
|
||||
func (c ComfyImageGenerationClient) cancelPrompt(ctx context.Context, promptID string) error {
|
||||
if strings.TrimSpace(promptID) == "" {
|
||||
return errors.New("comfy prompt id is empty")
|
||||
}
|
||||
payload, err := json.Marshal(map[string][]string{"delete": {promptID}})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodPost, strings.TrimRight(c.BaseURL, "/")+"/queue", bytes.NewReader(payload))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
response, err := c.client().Do(request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if response.StatusCode < 200 || response.StatusCode > 299 {
|
||||
return fmt.Errorf("comfy prompt cleanup status %d", response.StatusCode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c ComfyImageGenerationClient) historyImage(ctx context.Context, promptID string) (GeneratedImage, bool, error) {
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, strings.TrimRight(c.BaseURL, "/")+"/history/"+url.PathEscape(promptID), nil)
|
||||
if err != nil {
|
||||
|
||||
@@ -3,6 +3,7 @@ package multimodal
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -167,3 +168,42 @@ func TestComfyImageGenerationClientRunsWorkflowAndReturnsImage(t *testing.T) {
|
||||
t.Fatalf("image=%+v err=%v", image, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestComfyImageGenerationClientDeletesOnlyCancelledPrompt(t *testing.T) {
|
||||
var deleted []string
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case "/prompt":
|
||||
_ = json.NewEncoder(w).Encode(map[string]string{"prompt_id": "cancel-me"})
|
||||
case "/history/cancel-me":
|
||||
<-r.Context().Done()
|
||||
case "/queue":
|
||||
var payload struct {
|
||||
Delete []string `json:"delete"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
deleted = append(deleted, payload.Delete...)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
done := make(chan error, 1)
|
||||
go func() {
|
||||
_, err := (ComfyImageGenerationClient{BaseURL: server.URL, Client: server.Client(), PollInterval: time.Millisecond, PollTimeout: time.Minute}).Generate(ctx, "", "red bird", "256x256")
|
||||
done <- err
|
||||
}()
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
cancel()
|
||||
if err := <-done; !errors.Is(err, context.Canceled) {
|
||||
t.Fatalf("err=%v", err)
|
||||
}
|
||||
if len(deleted) != 1 || deleted[0] != "cancel-me" {
|
||||
t.Fatalf("deleted=%v", deleted)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package multimodal
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"git.cin.su/m/Assistant/contracts/artifact"
|
||||
"git.cin.su/m/Assistant/contracts/capability"
|
||||
"git.cin.su/m/Assistant/contracts/interaction"
|
||||
)
|
||||
|
||||
func NormalizeIngress(id string, modality interaction.IngressModality, sourceRef, deviceRef, locationRef, principalID, contentType string, data []byte, response capability.MultimodalResponse, at int64) (interaction.IngressEnvelope, artifact.MediaArtifact) {
|
||||
hash := sha256.Sum256(data)
|
||||
artifactValue := artifact.MediaArtifact{ID: artifact.ArtifactID("artifact:media:" + hex.EncodeToString(hash[:8])), ContentType: contentType, SizeBytes: int64(len(data)), ContentDigest: hex.EncodeToString(hash[:]), SourceRef: sourceRef, RecordedAtUnixMillis: at}
|
||||
if len(data) == 0 {
|
||||
return interaction.IngressEnvelope{ID: id, Modality: modality, SourceRef: sourceRef, DeviceRef: deviceRef, LocationRef: locationRef, PrincipalID: principalID, Degraded: true, RecordedAtUnixMillis: at}, artifact.MediaArtifact{}
|
||||
}
|
||||
return interaction.IngressEnvelope{ID: id, Modality: modality, SourceRef: sourceRef, DeviceRef: deviceRef, LocationRef: locationRef, PrincipalID: principalID, ArtifactRef: string(artifactValue.ID), ObservationRef: response.EvidenceRef, ProviderRef: response.Provider, ModelRef: response.Model, ModelVersion: response.ModelVersion, ConfidenceMillis: response.ConfidenceMillis, RecordedAtUnixMillis: at}, artifactValue
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package multimodal_test
|
||||
|
||||
import (
|
||||
"git.cin.su/m/Assistant/adapters/multimodal"
|
||||
"git.cin.su/m/Assistant/contracts/capability"
|
||||
"git.cin.su/m/Assistant/contracts/interaction"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIngressNormalizesMediaArtifactAndObservationProvenance(t *testing.T) {
|
||||
e, a := multimodal.NormalizeIngress("ingress-1", interaction.IngressImage, "camera:street", "device:cam", "world:street", "principal", "image/png", []byte("image"), capability.MultimodalResponse{Provider: "local", Model: "vision", ModelVersion: "1", EvidenceRef: "observation-1", ConfidenceMillis: 800}, 1)
|
||||
if !e.Valid() || !a.Valid() || e.ArtifactRef == "" || e.ObservationRef != "observation-1" || e.DeviceRef == "" || e.LocationRef == "" {
|
||||
t.Fatalf("envelope=%+v artifact=%+v", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnavailableIngressDegradesWithoutFabricatingObservation(t *testing.T) {
|
||||
e, a := multimodal.NormalizeIngress("ingress-1", interaction.IngressAudio, "mic", "device:mic", "", "principal", "audio/wav", nil, capability.MultimodalResponse{}, 1)
|
||||
if !e.Degraded || e.ObservationRef != "" || a.Valid() {
|
||||
t.Fatalf("envelope=%+v artifact=%+v", e, a)
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"git.cin.su/m/Assistant/contracts/identity"
|
||||
@@ -18,6 +19,38 @@ func OpenSituation(ctx context.Context, config ContinuityConfig) (*SituationRepo
|
||||
}
|
||||
return &SituationRepository{pool: pool}, nil
|
||||
}
|
||||
|
||||
func (r *SituationRepository) SaveContextScope(ctx context.Context, scope situation.ContextScope) error {
|
||||
if err := scope.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
bindings, err := json.Marshal(scope.Bindings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = r.pool.Exec(ctx, `INSERT INTO situation.context_scopes(context_scope_id,principal_id,revision,bindings,recorded_at_unix_millis) VALUES($1,$2,$3,$4,$5) ON CONFLICT(context_scope_id) DO UPDATE SET revision=EXCLUDED.revision, bindings=EXCLUDED.bindings, recorded_at_unix_millis=EXCLUDED.recorded_at_unix_millis`, scope.ID, scope.PrincipalID, scope.Revision, bindings, scope.RecordedAt)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *SituationRepository) SaveFocusFrame(ctx context.Context, frame situation.FocusFrame) error {
|
||||
if err := frame.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
ref, err := json.Marshal(frame.ContextScopeRef)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = r.pool.Exec(ctx, `INSERT INTO situation.focus_frames(focus_frame_id,principal_id,actor_id,revision,authority_scope_ref,context_scope_ref,goal_ref,task_ref,source_utterance_ref,provenance,confidence_millis,state,parent_frame_ref,activated_at_unix_millis,suspended_at_unix_millis,expired_at_unix_millis,recorded_at_unix_millis) VALUES($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17) ON CONFLICT(focus_frame_id) DO UPDATE SET revision=EXCLUDED.revision, context_scope_ref=EXCLUDED.context_scope_ref, state=EXCLUDED.state, suspended_at_unix_millis=EXCLUDED.suspended_at_unix_millis, expired_at_unix_millis=EXCLUDED.expired_at_unix_millis, recorded_at_unix_millis=EXCLUDED.recorded_at_unix_millis`, frame.ID, frame.PrincipalID, frame.ActorID, frame.Revision, frame.AuthorityScopeRef, ref, frame.GoalRef, frame.TaskRef, frame.SourceUtteranceRef, frame.Provenance, frame.ConfidenceMillis, frame.State, frame.ParentFrameRef, frame.ActivatedAt, frame.SuspendedAt, frame.ExpiredAt, frame.RecordedAt)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *SituationRepository) AppendFocusTransition(ctx context.Context, transition situation.FocusTransition) error {
|
||||
if err := transition.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := r.pool.Exec(ctx, `INSERT INTO situation.focus_transitions(transition_id,focus_frame_id,from_revision,to_revision,operation,cause_ref,recorded_at_unix_millis) VALUES($1,$2,$3,$4,$5,$6,$7)`, transition.TransitionID, transition.FrameID, transition.FromRevision, transition.ToRevision, transition.Operation, transition.CauseRef, transition.RecordedAt)
|
||||
return err
|
||||
}
|
||||
func (r *SituationRepository) Close() { r.pool.Close() }
|
||||
func (r *SituationRepository) Save(ctx context.Context, binding situation.FocusBinding) error {
|
||||
_, err := r.pool.Exec(ctx, `INSERT INTO situation.focus_bindings (focus_binding_id, actor_id, session_id, workspace_id, project_id, goal_id, scope_id, active) VALUES ($1,$2,$3,$4,$5,$6,$7,$8) ON CONFLICT (focus_binding_id) DO UPDATE SET active=EXCLUDED.active`, binding.ID, binding.ActorID, binding.SessionID, binding.Workspace, binding.ProjectID, binding.GoalID, binding.ScopeID, binding.Active)
|
||||
|
||||
@@ -25,6 +25,121 @@ var (
|
||||
ErrDevelopmentCandidateConflict = errors.New("development candidate conflict")
|
||||
)
|
||||
|
||||
func (r *WorkRepository) SaveInvestigation(ctx context.Context, value work.Investigation, expectedRevision int64) error {
|
||||
if !value.Valid() || expectedRevision < 0 {
|
||||
return errors.New("invalid investigation revision")
|
||||
}
|
||||
attempts := make([]string, 0, len(value.AttemptRefs))
|
||||
for _, item := range value.AttemptRefs {
|
||||
attempts = append(attempts, string(item))
|
||||
}
|
||||
hypotheses := make([]string, 0, len(value.HypothesisRefs))
|
||||
for _, item := range value.HypothesisRefs {
|
||||
hypotheses = append(hypotheses, string(item))
|
||||
}
|
||||
findings := make([]string, 0, len(value.FindingRefs))
|
||||
for _, item := range value.FindingRefs {
|
||||
findings = append(findings, string(item))
|
||||
}
|
||||
blockers := append([]string(nil), value.Blockers...)
|
||||
checkpoints := make([]string, 0, len(value.CheckpointRefs))
|
||||
for _, item := range value.CheckpointRefs {
|
||||
checkpoints = append(checkpoints, string(item))
|
||||
}
|
||||
if expectedRevision == 0 {
|
||||
result, err := r.pool.Exec(ctx, `INSERT INTO work.long_running_work(investigation_id,objective,hypothesis_refs,attempt_refs,finding_refs,blockers,checkpoint_refs,next_step,state,revision,time_budget_millis,cost_budget_micros,max_attempts,recorded_at_unix_millis) VALUES($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14) ON CONFLICT(investigation_id) DO NOTHING`, value.ID, value.Objective, hypotheses, attempts, findings, blockers, checkpoints, value.NextStep, value.State, value.Revision, value.TimeBudgetMillis, value.CostBudgetMicros, value.MaxAttempts, value.RecordedAtUnixMillis)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if result.RowsAffected() != 1 {
|
||||
return errors.New("investigation already exists")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
result, err := r.pool.Exec(ctx, `UPDATE work.long_running_work SET objective=$2,hypothesis_refs=$3,attempt_refs=$4,finding_refs=$5,blockers=$6,checkpoint_refs=$7,next_step=$8,state=$9,revision=$10,time_budget_millis=$11,cost_budget_micros=$12,max_attempts=$13,recorded_at_unix_millis=$14 WHERE investigation_id=$1 AND revision=$15`, value.ID, value.Objective, hypotheses, attempts, findings, blockers, checkpoints, value.NextStep, value.State, value.Revision, value.TimeBudgetMillis, value.CostBudgetMicros, value.MaxAttempts, value.RecordedAtUnixMillis, expectedRevision)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if result.RowsAffected() != 1 {
|
||||
return errors.New("investigation revision conflict")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *WorkRepository) LoadInvestigation(ctx context.Context, id work.LongRunningWorkID) (work.Investigation, error) {
|
||||
var value work.Investigation
|
||||
var hypotheses, attempts, findings, blockers, checkpoints []string
|
||||
err := r.pool.QueryRow(ctx, `SELECT investigation_id,objective,hypothesis_refs,attempt_refs,finding_refs,blockers,checkpoint_refs,next_step,state,revision,time_budget_millis,cost_budget_micros,max_attempts,recorded_at_unix_millis FROM work.long_running_work WHERE investigation_id=$1`, id).Scan(&value.ID, &value.Objective, &hypotheses, &attempts, &findings, &blockers, &checkpoints, &value.NextStep, &value.State, &value.Revision, &value.TimeBudgetMillis, &value.CostBudgetMicros, &value.MaxAttempts, &value.RecordedAtUnixMillis)
|
||||
if err != nil {
|
||||
return work.Investigation{}, fmt.Errorf("load investigation: %w", err)
|
||||
}
|
||||
for _, item := range hypotheses {
|
||||
value.HypothesisRefs = append(value.HypothesisRefs, work.EvidenceRef(item))
|
||||
}
|
||||
for _, item := range attempts {
|
||||
value.AttemptRefs = append(value.AttemptRefs, work.AgentRunID(item))
|
||||
}
|
||||
for _, item := range findings {
|
||||
value.FindingRefs = append(value.FindingRefs, work.EvidenceRef(item))
|
||||
}
|
||||
value.Blockers = append(value.Blockers, blockers...)
|
||||
for _, item := range checkpoints {
|
||||
value.CheckpointRefs = append(value.CheckpointRefs, work.AgentCheckpointID(item))
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// SaveInvestigationCheckpoint atomically records the checkpoint and marks its
|
||||
// attempts verified. The unique key makes retries idempotent.
|
||||
func (r *WorkRepository) SaveInvestigationCheckpoint(ctx context.Context, checkpoint work.InvestigationCheckpoint) error {
|
||||
if !checkpoint.Valid() {
|
||||
return errors.New("invalid investigation checkpoint")
|
||||
}
|
||||
tx, err := r.pool.Begin(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tx.Rollback(ctx)
|
||||
if _, err = tx.Exec(ctx, `INSERT INTO work.investigation_checkpoints(checkpoint_id,investigation_id,completed_attempt_refs,completed_finding_refs,next_step,recorded_at_unix_millis) VALUES($1,$2,$3,$4,$5,$6) ON CONFLICT(checkpoint_id) DO NOTHING`, checkpoint.ID, checkpoint.InvestigationID, stringIDs(checkpoint.CompletedAttemptRefs), stringEvidence(checkpoint.CompletedFindingRefs), checkpoint.NextStep, checkpoint.RecordedAtUnixMillis); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, attempt := range checkpoint.CompletedAttemptRefs {
|
||||
if _, err = tx.Exec(ctx, `INSERT INTO work.investigation_attempts(investigation_id,attempt_ref,verified_at_unix_millis) VALUES($1,$2,$3) ON CONFLICT(investigation_id,attempt_ref) DO NOTHING`, checkpoint.InvestigationID, attempt, checkpoint.RecordedAtUnixMillis); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return tx.Commit(ctx)
|
||||
}
|
||||
|
||||
func (r *WorkRepository) UnverifiedInvestigationAttempts(ctx context.Context, id work.LongRunningWorkID, candidates []work.AgentRunID) ([]work.AgentRunID, error) {
|
||||
result := make([]work.AgentRunID, 0, len(candidates))
|
||||
for _, candidate := range candidates {
|
||||
var exists bool
|
||||
if err := r.pool.QueryRow(ctx, `SELECT EXISTS(SELECT 1 FROM work.investigation_attempts WHERE investigation_id=$1 AND attempt_ref=$2)`, id, candidate).Scan(&exists); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !exists {
|
||||
result = append(result, candidate)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func stringIDs(values []work.AgentRunID) []string {
|
||||
result := make([]string, 0, len(values))
|
||||
for _, value := range values {
|
||||
result = append(result, string(value))
|
||||
}
|
||||
return result
|
||||
}
|
||||
func stringEvidence(values []work.EvidenceRef) []string {
|
||||
result := make([]string, 0, len(values))
|
||||
for _, value := range values {
|
||||
result = append(result, string(value))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func OpenWork(ctx context.Context, config ContinuityConfig) (*WorkRepository, error) {
|
||||
pool, err := openPool(ctx, config)
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"git.cin.su/m/Assistant/contracts/world"
|
||||
worlddomain "git.cin.su/m/Assistant/modules/world/domain"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type WorldRepository struct{ pool *pgxpool.Pool }
|
||||
|
||||
func OpenWorld(ctx context.Context, config ContinuityConfig) (*WorldRepository, error) {
|
||||
pool, err := openPool(ctx, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &WorldRepository{pool: pool}, nil
|
||||
}
|
||||
|
||||
func (r *WorldRepository) Close() { r.pool.Close() }
|
||||
|
||||
func (r *WorldRepository) SaveKindDefinition(ctx context.Context, value world.WorldObjectKindDefinition) error {
|
||||
if err := worlddomain.ValidateKindDefinition(value); err != nil {
|
||||
return err
|
||||
}
|
||||
var parentOwner, parentKind, parentID string
|
||||
var parentRevision *uint64
|
||||
if value.ParentKindRef != nil {
|
||||
parentOwner, parentKind, parentID = value.ParentKindRef.OwnerNamespace, value.ParentKindRef.SemanticKind, string(value.ParentKindRef.StableID)
|
||||
if value.ParentKindRef.Revision != nil {
|
||||
revision := uint64(*value.ParentKindRef.Revision)
|
||||
parentRevision = &revision
|
||||
}
|
||||
}
|
||||
_, err := r.pool.Exec(ctx, `INSERT INTO world.kind_definitions(kind_definition_id,kind_key,revision,display_name,parent_owner_namespace,parent_semantic_kind,parent_stable_id,parent_revision,lifecycle,definition_digest,recorded_at_unix_millis) VALUES($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11) ON CONFLICT(kind_definition_id) DO NOTHING`, value.ID, value.KindKey, value.Revision, value.DisplayName, parentOwner, parentKind, parentID, parentRevision, value.Lifecycle, value.DefinitionDigest, value.RecordedAt)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *WorldRepository) SaveObject(ctx context.Context, value world.WorldObject) error {
|
||||
if err := worlddomain.ValidateObject(value); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := r.pool.Exec(ctx, `INSERT INTO world.objects(world_object_id,principal_id,kind_owner_namespace,kind_semantic_kind,kind_stable_id,kind_revision,lifecycle,created_at_unix_millis) VALUES($1,$2,$3,$4,$5,$6,$7,$8) ON CONFLICT(world_object_id) DO NOTHING`, value.ID, value.PrincipalID, value.KindDefinitionRef.OwnerNamespace, value.KindDefinitionRef.SemanticKind, value.KindDefinitionRef.StableID, value.KindRevision, value.Lifecycle, value.CreatedAt)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *WorldRepository) SaveAlias(ctx context.Context, value world.WorldAlias) error {
|
||||
if err := worlddomain.ValidateAlias(value); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := r.pool.Exec(ctx, `INSERT INTO world.aliases(world_alias_id,principal_id,world_object_id,alias,recorded_at_unix_millis) VALUES($1,$2,$3,$4,$5) ON CONFLICT(world_alias_id) DO NOTHING`, value.ID, value.PrincipalID, value.ObjectID, value.Alias, value.RecordedAt)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *WorldRepository) SaveFacetDefinition(ctx context.Context, value world.FacetDefinition) error {
|
||||
if err := worlddomain.ValidateFacetDefinition(value); err != nil {
|
||||
return err
|
||||
}
|
||||
payload, err := json.Marshal(value.ValueType)
|
||||
if err != nil {
|
||||
return fmt.Errorf("encode facet value type: %w", err)
|
||||
}
|
||||
_, err = r.pool.Exec(ctx, `INSERT INTO world.facet_definitions(facet_definition_id,kind_key,revision,facet_key,value_type,definition_digest,recorded_at_unix_millis) VALUES($1,$2,$3,$4,$5,$6,$7) ON CONFLICT(facet_definition_id) DO NOTHING`, value.ID, value.KindKey, value.Revision, value.FacetKey, payload, value.DefinitionDigest, value.RecordedAt)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *WorldRepository) SaveRelationDefinition(ctx context.Context, value world.RelationDefinition) error {
|
||||
if err := worlddomain.ValidateRelationDefinition(value); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := r.pool.Exec(ctx, `INSERT INTO world.relation_definitions(relation_definition_id,kind_key,revision,relation_key,target_kind_key,definition_digest,recorded_at_unix_millis) VALUES($1,$2,$3,$4,$5,$6,$7) ON CONFLICT(relation_definition_id) DO NOTHING`, value.ID, value.KindKey, value.Revision, value.RelationKey, value.TargetKindKey, value.DefinitionDigest, value.RecordedAt)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *WorldRepository) SaveResolution(ctx context.Context, value world.IdentityResolutionDecisionRecord) error {
|
||||
if err := worlddomain.ValidateResolution(value); err != nil {
|
||||
return err
|
||||
}
|
||||
candidates, err := json.Marshal(value.Candidates)
|
||||
if err != nil {
|
||||
return fmt.Errorf("encode resolution candidates: %w", err)
|
||||
}
|
||||
evidence, err := json.Marshal(value.Evidence)
|
||||
if err != nil {
|
||||
return fmt.Errorf("encode resolution evidence: %w", err)
|
||||
}
|
||||
_, err = r.pool.Exec(ctx, `INSERT INTO world.identity_resolution_decisions(resolution_decision_id,principal_id,candidates,decision,evidence,confidence_millis,decided_at_unix_millis,revision) VALUES($1,$2,$3,$4,$5,$6,$7,$8) ON CONFLICT(resolution_decision_id) DO NOTHING`, value.ID, value.PrincipalID, candidates, value.Decision, evidence, value.ConfidenceMillis, value.DecidedAt, value.Revision)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *WorldRepository) SaveLineage(ctx context.Context, value world.WorldLineage) error {
|
||||
if err := worlddomain.ValidateLineage(value); err != nil {
|
||||
return err
|
||||
}
|
||||
sources, err := json.Marshal(value.SourceRefs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("encode lineage sources: %w", err)
|
||||
}
|
||||
successors, err := json.Marshal(value.SuccessorRefs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("encode lineage successors: %w", err)
|
||||
}
|
||||
_, err = r.pool.Exec(ctx, `INSERT INTO world.lineage(lineage_id,operation,source_refs,successor_refs,recorded_at_unix_millis) VALUES($1,$2,$3,$4,$5) ON CONFLICT(lineage_id) DO NOTHING`, value.ID, value.Operation, sources, successors, value.RecordedAt)
|
||||
return err
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,45 @@
|
||||
# ADR-0020 — Architecture Generation 2 governance adoption
|
||||
|
||||
**Status:** Accepted by `ARCH-GEN2-000`
|
||||
**Date:** 2026-08-29
|
||||
**Scope:** Architecture and governance only; no runtime or schema migration.
|
||||
|
||||
## Decision
|
||||
|
||||
Assistant adopts the documents under `docs/DOMOVOY_CONSTITUTION.md`,
|
||||
`architecture/GEN2_TARGET_ARCHITECTURE.md`,
|
||||
`architecture/GEN2_CANONICAL_DATA_MODEL.md`,
|
||||
`development/GEN2_MIGRATION_MAP.md`,
|
||||
`development/GEN2_MASTER_PLAN.md`, and the execution contract as the
|
||||
canonical Architecture Generation 2 governance basis.
|
||||
|
||||
The existing V1 implementation remains the rollback and compatibility
|
||||
baseline. This decision authorizes subsequent, separately accepted Gen2
|
||||
packets to introduce the World, Configuration, and SELF bounded owners and
|
||||
Gen2 migrations, subject to explicit ownership, schema, contract, and
|
||||
rollback decisions in those packets. It does not create those owners or
|
||||
change canonical runtime state in this task.
|
||||
|
||||
## Authority and execution order
|
||||
|
||||
The Gen2 constitution and accepted architecture decisions govern the Gen2
|
||||
program. The target architecture, canonical data model, migration map,
|
||||
master plan, execution contract, task index, and individual task packet are
|
||||
interpreted in that order. A lower-level packet cannot silently weaken a
|
||||
higher-level invariant or reorder the task index.
|
||||
|
||||
Every Gen2 task starts from `gen2-integration`, uses the active task pointer,
|
||||
and passes the declared FAST/MILESTONE/FULL gate before merge. `ARCH-GEN2-000`
|
||||
is the architecture adoption root accepted against the PRE baseline;
|
||||
`GEN2-001` and later tasks are not accepted by this decision and require
|
||||
separate execution.
|
||||
|
||||
## Invariants preserved
|
||||
|
||||
- No new canonical owner, cross-module dependency, privilege, database role,
|
||||
third-party dependency, runtime writer, event, or migration is introduced.
|
||||
- Existing Authority → Commit → Executor boundaries remain mandatory.
|
||||
- Memory remains a rebuildable retrieval fabric, World is not a generic truth
|
||||
owner, and SELF is not a monolithic orchestrator.
|
||||
- The `domovoy-pre-gen2` baseline tag remains immutable and is the rollback
|
||||
point until a later migration task explicitly establishes another one.
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"registry_version": 1,
|
||||
"semantic_generation": 1,
|
||||
"architecture_generation": 1,
|
||||
"architecture_generation": 2,
|
||||
"modules": {
|
||||
"identity": {
|
||||
"id": "mod.identity",
|
||||
@@ -179,6 +179,30 @@
|
||||
"database_schema": "interaction",
|
||||
"boundary_class": "remote_capable",
|
||||
"may_depend_on": ["contracts.identity", "contracts.situation", "contracts.epistemics", "contracts.cognitive", "platform.ids", "platform.events"]
|
||||
},
|
||||
"world": {
|
||||
"id": "mod.world",
|
||||
"owns": ["WorldObject", "WorldObjectKindDefinition", "WorldAlias", "FacetDefinition", "RelationDefinition", "IdentityResolutionCandidate", "IdentityResolutionDecision", "WorldLineage"],
|
||||
"database_schema": "world",
|
||||
"boundary_class": "remote_capable",
|
||||
"may_depend_on": ["contracts.identity", "platform.ids", "platform.events"],
|
||||
"forbidden": ["adapters.models", "adapters.temporal", "claims", "beliefs", "observations"]
|
||||
},
|
||||
"configuration": {
|
||||
"id": "mod.configuration",
|
||||
"owns": ["SettingDefinition", "SettingAssignment", "ConfigurationRevision", "ConfigurationChangeProvenance"],
|
||||
"database_schema": "configuration",
|
||||
"boundary_class": "remote_capable",
|
||||
"may_depend_on": ["contracts.identity", "contracts.authority", "platform.ids", "platform.events", "platform.secrets"],
|
||||
"forbidden": ["secret_values", "arbitrary_key_value_without_definition"]
|
||||
},
|
||||
"self": {
|
||||
"id": "mod.self",
|
||||
"owns": ["CompetenceAssessment", "WeaknessPattern", "DependencyAssessment", "LearningLedgerEntry", "ImprovementNeed", "SelfVersionView", "SelfEvaluationInterpretation"],
|
||||
"database_schema": "self",
|
||||
"boundary_class": "remote_capable",
|
||||
"may_depend_on": ["contracts.capability", "contracts.epistemics", "contracts.artifact", "platform.ids", "platform.events"],
|
||||
"forbidden": ["adapters.models", "tasks", "authority_grants", "code_release_switching"]
|
||||
}
|
||||
},
|
||||
"platform": {
|
||||
@@ -244,6 +268,26 @@
|
||||
"cross_owner_write": "forbidden",
|
||||
"cross_owner_application_join": "forbidden"
|
||||
},
|
||||
"runtime_topology_v2": {
|
||||
"core_runtime": {
|
||||
"modules": ["identity", "situation", "work", "epistemics", "capability", "artifact", "interaction", "world", "configuration", "self"]
|
||||
},
|
||||
"continuity_runtime": {"modules": ["continuity"]},
|
||||
"commit_runtime": {"modules": ["authority", "commit"]},
|
||||
"cognitive_workers": {"modules": [], "role": "replaceable reasoning/context workers; no canonical ownership"},
|
||||
"supervisor_runtime": {"modules": [], "role": "release health, switch, rollback and recovery; not a semantic owner"}
|
||||
},
|
||||
"storage_topology_v2": {
|
||||
"postgres_cluster": "assistant_primary",
|
||||
"schemas": {
|
||||
"identity": "identity_role", "authority": "authority_role", "situation": "situation_role", "work": "work_role",
|
||||
"epistemics": "epistemics_role", "continuity": "continuity_role", "capability": "capability_role", "commit": "commit_role",
|
||||
"artifact": "artifact_role", "interaction": "interaction_role", "world": "world_role", "configuration": "configuration_role", "self": "self_role"
|
||||
},
|
||||
"cross_owner_fk": "forbidden_by_default",
|
||||
"cross_owner_write": "forbidden",
|
||||
"cross_owner_application_join": "forbidden"
|
||||
},
|
||||
"invariants": [
|
||||
{
|
||||
"id": "INV-AUTH-001",
|
||||
@@ -309,6 +353,14 @@
|
||||
".gitea/workflows/ci.yml",
|
||||
"cmd/devctl/**",
|
||||
"dev/ACTIVE_TASK.json",
|
||||
"docs/DOMOVOY_CONSTITUTION.md",
|
||||
"docs/DOMOVOY_GEN2_GAP_MAP.md",
|
||||
"architecture/GEN2_TARGET_ARCHITECTURE.md",
|
||||
"architecture/GEN2_CANONICAL_DATA_MODEL.md",
|
||||
"development/GEN2_MIGRATION_MAP.md",
|
||||
"development/GEN2_MASTER_PLAN.md",
|
||||
"development/GEN2_EXECUTION_CONTRACT.md",
|
||||
"development/GEN2_TASK_INDEX.md",
|
||||
"security/**"
|
||||
],
|
||||
"default_feature_task_authority": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
registry_version: 1
|
||||
semantic_generation: 1
|
||||
architecture_generation: 1
|
||||
architecture_generation: 2
|
||||
|
||||
modules:
|
||||
identity:
|
||||
@@ -75,6 +75,30 @@ modules:
|
||||
boundary_class: remote_capable
|
||||
may_depend_on: [contracts.identity, contracts.situation, contracts.epistemics, contracts.cognitive, platform.ids, platform.events]
|
||||
|
||||
world:
|
||||
id: mod.world
|
||||
owns: [WorldObject, WorldObjectKindDefinition, WorldAlias, FacetDefinition, RelationDefinition, IdentityResolutionCandidate, IdentityResolutionDecision, WorldLineage]
|
||||
database_schema: world
|
||||
boundary_class: remote_capable
|
||||
may_depend_on: [contracts.identity, platform.ids, platform.events]
|
||||
forbidden: [adapters.models, adapters.temporal, claims, beliefs, observations]
|
||||
|
||||
configuration:
|
||||
id: mod.configuration
|
||||
owns: [SettingDefinition, SettingAssignment, ConfigurationRevision, ConfigurationChangeProvenance]
|
||||
database_schema: configuration
|
||||
boundary_class: remote_capable
|
||||
may_depend_on: [contracts.identity, contracts.authority, platform.ids, platform.events, platform.secrets]
|
||||
forbidden: [secret_values, arbitrary_key_value_without_definition]
|
||||
|
||||
self:
|
||||
id: mod.self
|
||||
owns: [CompetenceAssessment, WeaknessPattern, DependencyAssessment, LearningLedgerEntry, ImprovementNeed, SelfVersionView, SelfEvaluationInterpretation]
|
||||
database_schema: self
|
||||
boundary_class: remote_capable
|
||||
may_depend_on: [contracts.capability, contracts.epistemics, contracts.artifact, platform.ids, platform.events]
|
||||
forbidden: [adapters.models, tasks, authority_grants, code_release_switching]
|
||||
|
||||
platform:
|
||||
allowed_packages:
|
||||
- platform.ids
|
||||
@@ -98,6 +122,20 @@ runtime_topology_v1:
|
||||
modules: []
|
||||
role: "replaceable cognition/context compilation workers; no canonical ownership"
|
||||
|
||||
runtime_topology_v2:
|
||||
core_runtime:
|
||||
modules: [identity, situation, work, epistemics, capability, artifact, interaction, world, configuration, self]
|
||||
continuity_runtime:
|
||||
modules: [continuity]
|
||||
commit_runtime:
|
||||
modules: [authority, commit]
|
||||
cognitive_workers:
|
||||
modules: []
|
||||
role: "replaceable reasoning/context workers; no canonical ownership"
|
||||
supervisor_runtime:
|
||||
modules: []
|
||||
role: "release health, switch, rollback and recovery; not a semantic owner"
|
||||
|
||||
storage_topology_v1:
|
||||
postgres_cluster: assistant_primary
|
||||
schemas:
|
||||
@@ -115,6 +153,26 @@ storage_topology_v1:
|
||||
cross_owner_write: forbidden
|
||||
cross_owner_application_join: forbidden
|
||||
|
||||
storage_topology_v2:
|
||||
postgres_cluster: assistant_primary
|
||||
schemas:
|
||||
identity: identity_role
|
||||
authority: authority_role
|
||||
situation: situation_role
|
||||
work: work_role
|
||||
epistemics: epistemics_role
|
||||
continuity: continuity_role
|
||||
capability: capability_role
|
||||
commit: commit_role
|
||||
artifact: artifact_role
|
||||
interaction: interaction_role
|
||||
world: world_role
|
||||
configuration: configuration_role
|
||||
self: self_role
|
||||
cross_owner_fk: forbidden_by_default
|
||||
cross_owner_write: forbidden
|
||||
cross_owner_application_join: forbidden
|
||||
|
||||
invariants:
|
||||
- id: INV-AUTH-001
|
||||
property: "No external mutation without current valid authority and target/revision checks"
|
||||
@@ -149,6 +207,14 @@ protected_paths:
|
||||
- .gitea/workflows/ci.yml
|
||||
- cmd/devctl/**
|
||||
- dev/ACTIVE_TASK.json
|
||||
- docs/DOMOVOY_CONSTITUTION.md
|
||||
- docs/DOMOVOY_GEN2_GAP_MAP.md
|
||||
- architecture/GEN2_TARGET_ARCHITECTURE.md
|
||||
- architecture/GEN2_CANONICAL_DATA_MODEL.md
|
||||
- development/GEN2_MIGRATION_MAP.md
|
||||
- development/GEN2_MASTER_PLAN.md
|
||||
- development/GEN2_EXECUTION_CONTRACT.md
|
||||
- development/GEN2_TASK_INDEX.md
|
||||
- security/**
|
||||
|
||||
default_feature_task_authority:
|
||||
|
||||
@@ -67,12 +67,18 @@ func (g cognitiveGateway) interpretationHandler(w http.ResponseWriter, r *http.R
|
||||
}
|
||||
g.observe(r.Context(), selected, response, started)
|
||||
if response.FinishReason != cognitive.FinishCompleted {
|
||||
writeInterpretationResponse(w, cognitive.GatewayInterpretationResponse{Version: request.Version, RequestID: request.RequestID, FinishReason: response.FinishReason, Usage: response.Usage})
|
||||
// A selected provider can become unavailable after discovery. Semantic
|
||||
// routing must still retain the deterministic interpretation and use the
|
||||
// local lifeboat rather than turn an ordinary conversation into a
|
||||
// degraded reply solely because one provider failed.
|
||||
writeInterpretationResponse(w, g.interpretWithBuiltin(r.Context(), request, maxOutputBytes))
|
||||
return
|
||||
}
|
||||
candidate, ok := parseInterpretationCandidate(response.AdvisoryText)
|
||||
if !ok {
|
||||
writeInterpretationResponse(w, cognitive.GatewayInterpretationResponse{Version: request.Version, RequestID: request.RequestID, FinishReason: cognitive.FinishInvalidStructuredOutput, Usage: response.Usage})
|
||||
// Structured interpretation is a transport contract. A malformed model
|
||||
// answer is not a reason to discard the available local fallback.
|
||||
writeInterpretationResponse(w, g.interpretWithBuiltin(r.Context(), request, maxOutputBytes))
|
||||
return
|
||||
}
|
||||
writeInterpretationResponse(w, cognitive.GatewayInterpretationResponse{Version: request.Version, RequestID: request.RequestID, Candidate: candidate, FinishReason: cognitive.FinishCompleted, Usage: response.Usage})
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.cin.su/m/Assistant/contracts/capability"
|
||||
"git.cin.su/m/Assistant/contracts/cognitive"
|
||||
)
|
||||
|
||||
@@ -24,3 +30,32 @@ func TestBuiltinInterpretationTimeoutRetainsDeterministicCandidate(t *testing.T)
|
||||
t.Fatalf("candidate did not retain deterministic meaning: %+v", response.Candidate)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterpretationProviderFailureUsesLocalLifeboat(t *testing.T) {
|
||||
selected := gatewaySelection()
|
||||
selected.Binding.DataPolicy = capability.DataPolicyLocalOnly
|
||||
primary := &stubAdvisoryProvider{response: cognitive.AdvisoryResponse{FinishReason: cognitive.FinishProviderUnavailable}}
|
||||
lifeboat := &stubAdvisoryProvider{response: cognitive.AdvisoryResponse{AdvisoryText: `{"primary_speech_act":"QUESTION","secondary_speech_act":"","confidence_millis":900,"applicability":"APPLICABLE","requested_outcome":"","needs_cognition":true,"clarification":"NOT_REQUIRED","selection_reason":"local lifeboat","capability_hints":["COGNITIVE_REASONING"]}`, FinishReason: cognitive.FinishCompleted}}
|
||||
gateway := cognitiveGateway{
|
||||
resolver: ®istryStub{selected: selected},
|
||||
providers: map[capability.ProviderKind][]providerRegistration{
|
||||
selected.Provider.Kind: {{ProviderInstanceID: selected.Provider.ID, Kind: selected.Provider.Kind, Provider: primary}},
|
||||
},
|
||||
builtinLocal: &providerRegistration{Provider: lifeboat},
|
||||
now: time.Now,
|
||||
}
|
||||
request := cognitive.GatewayInterpretationRequest{Version: cognitive.CreatorAdvisoryV1, RequestID: "request-1", ActorID: "actor-1", PrincipalID: "principal-1", ScopeID: "scope-1", Message: "сколько фигур в шахматах?", Locale: "ru", MaxOutputBytes: 4096, DataPolicy: capability.DataPolicyLocalOnly, DeterministicSpeechAct: "QUESTION", DeterministicConfidence: 800, DeterministicApplicability: cognitive.InterpretationApplicable}
|
||||
payload, _ := json.Marshal(request)
|
||||
recorder := httptest.NewRecorder()
|
||||
gateway.interpretationHandler(recorder, httptest.NewRequest(http.MethodPost, "/v1/interpretations", bytes.NewReader(payload)))
|
||||
if recorder.Code != http.StatusOK || !primary.called || !lifeboat.called {
|
||||
t.Fatalf("status=%d primary=%t lifeboat=%t body=%s", recorder.Code, primary.called, lifeboat.called, recorder.Body.String())
|
||||
}
|
||||
var response cognitive.GatewayInterpretationResponse
|
||||
if err := json.Unmarshal(recorder.Body.Bytes(), &response); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if response.FinishReason != cognitive.FinishCompleted || response.Candidate.SelectionReason != "local lifeboat" {
|
||||
t.Fatalf("response=%+v", response)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
@@ -38,7 +39,8 @@ func (g multimodalGateway) handler(operation capability.MultimodalOperation) htt
|
||||
http.Error(w, "local image generation capability unavailable", http.StatusBadGateway)
|
||||
return
|
||||
}
|
||||
image, err := g.imageGenerator.Generate(r.Context(), g.imageModel, r.FormValue("prompt"), r.FormValue("size"))
|
||||
prompt := r.FormValue("prompt")
|
||||
image, err := g.imageGenerator.Generate(r.Context(), g.imageModel, prompt, imageRequestSize(prompt, r.FormValue("size")))
|
||||
if err != nil || strings.TrimSpace(image.DataBase64) == "" {
|
||||
logMultimodalFailure(operation, err)
|
||||
http.Error(w, "local image generation capability unavailable", http.StatusBadGateway)
|
||||
@@ -96,6 +98,40 @@ func (g multimodalGateway) handler(operation capability.MultimodalOperation) htt
|
||||
}
|
||||
}
|
||||
|
||||
// imageRequestSize is a transport-level normalization for the local image
|
||||
// capability. An explicit, valid size always wins. When the caller only
|
||||
// expresses a conventional output-quality target, the capability translates it
|
||||
// to a supported provider size without exposing provider workflow details to
|
||||
// the interaction core. This policy is deliberately local and replaceable
|
||||
// with the image capability implementation.
|
||||
func imageRequestSize(prompt, requested string) string {
|
||||
if isImageSize(requested) {
|
||||
return strings.TrimSpace(requested)
|
||||
}
|
||||
words := strings.Fields(strings.ToLower(prompt))
|
||||
for index, word := range words {
|
||||
switch word {
|
||||
case "4k", "uhd":
|
||||
return "2048x2048"
|
||||
case "hd":
|
||||
return "1024x1024"
|
||||
case "ultra":
|
||||
if index+1 < len(words) && words[index+1] == "hd" {
|
||||
return "2048x2048"
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func isImageSize(value string) bool {
|
||||
var width, height int
|
||||
if _, err := fmt.Sscanf(strings.TrimSpace(value), "%dx%d", &width, &height); err != nil {
|
||||
return false
|
||||
}
|
||||
return width >= 256 && height >= 256 && width <= 2048 && height <= 2048
|
||||
}
|
||||
|
||||
func logMultimodalFailure(operation capability.MultimodalOperation, err error) {
|
||||
// Keep provider details in server diagnostics; never return them to chat.
|
||||
log.Printf("multimodal operation %s failed: %v", operation, err)
|
||||
|
||||
@@ -47,6 +47,27 @@ func TestMultimodalImageGenerationFailsClosedWhenSkillIsUnbound(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageRequestSizeUsesExplicitSizeOrQualityTarget(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
prompt string
|
||||
requested string
|
||||
want string
|
||||
}{
|
||||
{name: "explicit", prompt: "нарисуй птицу", requested: "768x512", want: "768x512"},
|
||||
{name: "hd", prompt: "нарисуй птицу в HD", want: "1024x1024"},
|
||||
{name: "uhd", prompt: "draw a bird in UHD", want: "2048x2048"},
|
||||
{name: "ordinary", prompt: "нарисуй птицу", want: ""},
|
||||
{name: "invalid explicit", prompt: "нарисуй птицу", requested: "100x100", want: ""},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
if got := imageRequestSize(test.prompt, test.requested); got != test.want {
|
||||
t.Fatalf("imageRequestSize(%q, %q) = %q, want %q", test.prompt, test.requested, got, test.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultimodalVideoUnderstandingFailsClosedWhenSkillIsUnbound(t *testing.T) {
|
||||
server := multimodalGateway{now: time.Now}
|
||||
request := multipartRequest(t, "/v1/multimodal/video-understand", "clip.mp4", []byte("video"))
|
||||
|
||||
@@ -72,3 +72,11 @@ func hasChatRoute(routes []chatRoute, kind chatRouteKind) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// specializedRouteBypassesGeneralInterpretation keeps a concrete capability
|
||||
// route stable. General cognition may still be used later to assess evidence,
|
||||
// but it must not replace an executable research or media route with a vague
|
||||
// conversational clarification before the capability is attempted.
|
||||
func specializedRouteBypassesGeneralInterpretation(routes []chatRoute) bool {
|
||||
return hasChatRoute(routes, chatRouteMedia) || hasChatRoute(routes, chatRouteResearch)
|
||||
}
|
||||
|
||||
@@ -39,3 +39,15 @@ func TestBuildChatRoutePlanAlwaysKeepsLocalConversationRoute(t *testing.T) {
|
||||
t.Fatalf("unexpected local route: %+v", planned)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSpecializedRouteBypassesGeneralInterpretation(t *testing.T) {
|
||||
if !specializedRouteBypassesGeneralInterpretation([]chatRoute{{Kind: chatRouteResearch}}) {
|
||||
t.Fatal("research route must execute before general cognition")
|
||||
}
|
||||
if !specializedRouteBypassesGeneralInterpretation([]chatRoute{{Kind: chatRouteMedia}}) {
|
||||
t.Fatal("media route must execute before general cognition")
|
||||
}
|
||||
if specializedRouteBypassesGeneralInterpretation([]chatRoute{{Kind: chatRouteConversation}}) {
|
||||
t.Fatal("conversation-only route must retain general cognition")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,19 +31,23 @@ type cognitiveInterpretationClient struct {
|
||||
// ReviewResponse asks the cognitive gateway to turn a locally assembled draft
|
||||
// into a natural answer. The draft remains the safe fallback when cognition is
|
||||
// unavailable; the model does not mutate canonical state or execute actions.
|
||||
func (c cognitiveInterpretationClient) ReviewResponse(ctx context.Context, session identity.CreatorSession, userMessage, draft string, meaning cognitive.TaskMeaning) (string, error) {
|
||||
func (c cognitiveInterpretationClient) ReviewResponse(ctx context.Context, session identity.CreatorSession, userMessage, draft, personalMemory string, meaning cognitive.TaskMeaning) (string, error) {
|
||||
requestID, err := ids.New()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
prompt := "Ты редактор ответа Домового. Проверь черновик по смыслу и подготовь естественный ответ человеку. Учитывай весь запрос, включая недостающие параметры для конкретного результата. Если данных не хватает, задай один короткий полезный уточняющий вопрос, например откуда и куда ехать. Не выдавай поисковые сниппеты как выполненное действие и не показывай служебные URL поисковой выдачи. Сохрани проверяемые факты, названия источников, прямые ссылки и ограничения. Не выдумывай факты и не утверждай невыполненные действия. Верни только готовый ответ без разбора проверки, служебных слов и упоминания моделей. Если черновик уже адекватен, верни его без лишних добавлений.\n\nСообщение пользователя:\n" + userMessage + "\n\nЧерновик ответа:\n" + draft
|
||||
prompt := "Ты редактор финального ответа Домового. Проверь черновик по смыслу и подготовь естественный ответ человеку. Учитывай весь запрос, включая недостающие параметры для конкретного результата. Если данных не хватает, задай один короткий полезный уточняющий вопрос, например откуда и куда ехать. Не выдавай поисковые сниппеты как выполненное действие и не показывай служебные URL поисковой выдачи. Сохрани проверяемые факты, названия источников, прямые ссылки и ограничения. Не выдумывай факты и не утверждай невыполненные действия. Сначала внутренне проверь, что ответ начинается с содержания, решающего вопрос. Не повторяй вопрос пользователя в качестве заголовка или вступления, если это не нужно для смысла. Представление, имя помощника, приветствие или предложение дополнительной помощи допустимы только если пользователь сам спросил об этом; иначе убери их. Верни только готовый ответ без разбора проверки, служебных слов и упоминания моделей. Если черновик уже адекватен, верни его без лишних добавлений.\n\nСообщение пользователя:\n" + userMessage + "\n\nЧерновик ответа:\n" + draft
|
||||
context := []cognitive.ContextExcerpt{}
|
||||
if strings.TrimSpace(personalMemory) != "" {
|
||||
context = append(context, cognitive.ContextExcerpt{ID: "personal-memory", SourceKind: "derived-personal-memory", SourceID: "interaction:human-utterances", Revision: 1, Sensitivity: cognitive.SensitivityPrivateAllowed, Text: personalMemory})
|
||||
}
|
||||
target := *c.baseURL
|
||||
target.Path = "/v1/advisories"
|
||||
payload, err := json.Marshal(cognitive.GatewayAdvisoryRequest{
|
||||
Version: cognitive.CreatorAdvisoryV1, RequestID: cognitive.RequestID(requestID),
|
||||
ActorID: session.ActorID, PrincipalID: session.PrincipalID,
|
||||
ScopeID: authority.ScopeID("domovoy-global-scope"), Message: prompt,
|
||||
Locale: "ru", MaxOutputBytes: 2048, DataPolicy: capability.DataPolicyLocalOnly,
|
||||
Context: context, Locale: "ru", MaxOutputBytes: 2048, DataPolicy: capability.DataPolicyLocalOnly,
|
||||
Meaning: cognitive.TaskMeaning{
|
||||
SpeechAct: "RESPONSE_REVIEW",
|
||||
CapabilityHints: meaning.CapabilityHints,
|
||||
@@ -78,6 +82,54 @@ func (c cognitiveInterpretationClient) ReviewResponse(ctx context.Context, sessi
|
||||
return strings.TrimSpace(result.AdvisoryText), nil
|
||||
}
|
||||
|
||||
// AnswerStableKnowledge asks the local cognitive route for a short answer to a
|
||||
// non-current question only after evidence retrieval did not produce a usable
|
||||
// result. The model decides whether its knowledge is stable enough; it must
|
||||
// explicitly decline changing or source-dependent facts instead of guessing.
|
||||
func (c cognitiveInterpretationClient) AnswerStableKnowledge(ctx context.Context, session identity.CreatorSession, userMessage, personalMemory string, meaning cognitive.TaskMeaning) (string, error) {
|
||||
requestID, err := ids.New()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
prompt := "Ты отвечаешь как Домовой на обычное сообщение пользователя. Сначала пойми цель: на вопрос дай короткий естественный ответ по устойчивому общеизвестному знанию, а безопасную просьбу создать текстовый или структурный результат выполни прямо в ответе. Начинай сразу с содержания и не представляйся, если пользователь не спросил об имени. Не называй модель или внутреннюю систему. Если истинность ответа зависит от текущего наблюдения, внешнего источника или инструмента, верни ровно NEEDS_VERIFIED_RESEARCH. Не придумывай факты и не утверждай выполнение внешнего действия.\n\nСообщение пользователя:\n" + userMessage
|
||||
context := []cognitive.ContextExcerpt{}
|
||||
if strings.TrimSpace(personalMemory) != "" {
|
||||
context = append(context, cognitive.ContextExcerpt{ID: "personal-memory", SourceKind: "derived-personal-memory", SourceID: "interaction:human-utterances", Revision: 1, Sensitivity: cognitive.SensitivityPrivateAllowed, Text: personalMemory})
|
||||
}
|
||||
target := *c.baseURL
|
||||
target.Path = "/v1/advisories"
|
||||
payload, err := json.Marshal(cognitive.GatewayAdvisoryRequest{
|
||||
Version: cognitive.CreatorAdvisoryV1, RequestID: cognitive.RequestID(requestID),
|
||||
ActorID: session.ActorID, PrincipalID: session.PrincipalID, ScopeID: authority.ScopeID("domovoy-global-scope"),
|
||||
Message: prompt, Context: context, Locale: "ru", MaxOutputBytes: 1024, DataPolicy: capability.DataPolicyLocalOnly,
|
||||
Meaning: cognitive.TaskMeaning{SpeechAct: "STABLE_KNOWLEDGE_ANSWER", CapabilityHints: meaning.CapabilityHints, RequestedOutcome: meaning.RequestedOutcome, InformationRequestKind: meaning.InformationRequestKind, ConfidenceMillis: meaning.ConfidenceMillis},
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodPost, target.String(), bytes.NewReader(payload))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
response, err := c.client.Do(request)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return "", fmt.Errorf("cognitive stable knowledge status %d", response.StatusCode)
|
||||
}
|
||||
var result cognitive.GatewayAdvisoryResponse
|
||||
if err := json.NewDecoder(response.Body).Decode(&result); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if result.FinishReason != cognitive.FinishCompleted || strings.TrimSpace(result.AdvisoryText) == "" {
|
||||
return "", fmt.Errorf("cognitive stable knowledge incomplete: %s", result.FinishReason)
|
||||
}
|
||||
return strings.TrimSpace(result.AdvisoryText), nil
|
||||
}
|
||||
|
||||
func newCognitiveInterpretationClient(rawURL string, client *http.Client) (cognitiveInterpretationClient, error) {
|
||||
base, err := url.Parse(rawURL)
|
||||
if err != nil || base.Scheme == "" || base.Host == "" {
|
||||
|
||||
@@ -25,6 +25,12 @@ func TestReviewResponseUsesCognitiveGatewayAndReturnsOnlyDraftAnswer(t *testing.
|
||||
if request.Message == "" || request.Meaning.SpeechAct == "" {
|
||||
t.Fatalf("review request did not carry semantic context: %+v", request)
|
||||
}
|
||||
if !strings.Contains(request.Message, "ответ начинается с содержания") || !strings.Contains(request.Message, "Не повторяй вопрос пользователя") || !strings.Contains(request.Message, "только если пользователь сам спросил") {
|
||||
t.Fatalf("review request lacks the conversational quality contract: %q", request.Message)
|
||||
}
|
||||
if len(request.Context) != 1 || !strings.Contains(request.Context[0].Text, "красный цвет") {
|
||||
t.Fatalf("review request did not carry compact personal memory: %+v", request.Context)
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(cognitive.GatewayAdvisoryResponse{Version: cognitive.CreatorAdvisoryV1, RequestID: request.RequestID, AdvisoryText: "Меня зовут Дед. Я Домовой.", FinishReason: cognitive.FinishCompleted})
|
||||
}))
|
||||
@@ -33,7 +39,7 @@ func TestReviewResponseUsesCognitiveGatewayAndReturnsOnlyDraftAnswer(t *testing.
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
reviewed, err := client.ReviewResponse(context.Background(), identity.CreatorSession{ActorID: "actor", PrincipalID: "principal"}, "кто ты?", "Я — Домовой.", cognitive.TaskMeaning{SpeechAct: "QUESTION"})
|
||||
reviewed, err := client.ReviewResponse(context.Background(), identity.CreatorSession{ActorID: "actor", PrincipalID: "principal"}, "кто ты?", "Я — Домовой.", "Предпочтение пользователя (его сообщение): красный цвет", cognitive.TaskMeaning{SpeechAct: "QUESTION"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -42,6 +48,28 @@ func TestReviewResponseUsesCognitiveGatewayAndReturnsOnlyDraftAnswer(t *testing.
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnswerStableKnowledgeUsesLocalOnlyCognitiveRoute(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var request cognitive.GatewayAdvisoryRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
|
||||
t.Fatalf("decode stable knowledge request: %v", err)
|
||||
}
|
||||
if request.DataPolicy != capability.DataPolicyLocalOnly || request.Meaning.SpeechAct != "STABLE_KNOWLEDGE_ANSWER" {
|
||||
t.Fatalf("unexpected stable knowledge request: %+v", request)
|
||||
}
|
||||
_ = json.NewEncoder(w).Encode(cognitive.GatewayAdvisoryResponse{Version: cognitive.CreatorAdvisoryV1, RequestID: request.RequestID, AdvisoryText: "Санкт-Петербург — город в России.", FinishReason: cognitive.FinishCompleted})
|
||||
}))
|
||||
defer server.Close()
|
||||
client, err := newCognitiveInterpretationClient(server.URL, server.Client())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
answer, err := client.AnswerStableKnowledge(context.Background(), identity.CreatorSession{ActorID: "actor", PrincipalID: "principal"}, "Питер — это где?", "", cognitive.TaskMeaning{SpeechAct: "QUESTION"})
|
||||
if err != nil || answer != "Санкт-Петербург — город в России." {
|
||||
t.Fatalf("answer=%q err=%v", answer, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateImageUsesDedicatedCognitiveMediaRoute(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost || r.URL.Path != "/v1/multimodal/generate-image" {
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.cin.su/m/Assistant/contracts/interaction"
|
||||
)
|
||||
|
||||
func TestSpecializedInformationReplyIsNotOverwrittenByResearch(t *testing.T) {
|
||||
for _, kind := range []interaction.InformationRequestKind{
|
||||
interaction.InformationRequestMemoryInventory,
|
||||
interaction.InformationRequestCapabilityInventory,
|
||||
interaction.InformationRequestCognitiveModelInventory,
|
||||
interaction.InformationRequestCognitiveChannelStatus,
|
||||
interaction.InformationRequestCognitiveRecovery,
|
||||
} {
|
||||
if shouldRunGeneralResearch(kind) {
|
||||
t.Fatalf("specialized information request %q must bypass general research", kind)
|
||||
}
|
||||
}
|
||||
if !shouldRunGeneralResearch(interaction.InformationRequestTimeDifference) {
|
||||
t.Fatal("time difference must use the current-data research route")
|
||||
}
|
||||
if !shouldRunGeneralResearch("") {
|
||||
t.Fatal("ordinary research requests must still use general research")
|
||||
}
|
||||
}
|
||||
+386
-35
@@ -120,7 +120,7 @@ func main() {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
researchClient, err := newResearchRuntimeClient(envOr("RESEARCH_RUNTIME_URL", "http://research-runtime:8087"), &http.Client{Timeout: 12 * time.Second})
|
||||
researchClient, err := newResearchRuntimeClient(envOr("RESEARCH_RUNTIME_URL", "http://research-runtime:8087"), &http.Client{Timeout: 20 * time.Second})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -511,6 +511,7 @@ func main() {
|
||||
}
|
||||
var effectiveTaskMessage = interactiondomain.StripAssistantVocative(utterance.Content)
|
||||
creativeUserRequest := interactiondomain.IsCreativeRequest(utterance.Content)
|
||||
locationReply := interactiondomain.LocationContextReply(utterance.Content)
|
||||
continuationMessage := ""
|
||||
travelContinuation := false
|
||||
var previousTask *interaction.HumanUtterance
|
||||
@@ -532,7 +533,8 @@ func main() {
|
||||
log.Printf("interaction principal history loaded: %d utterances", len(recentUserHistory))
|
||||
}
|
||||
conversationHistory = append(conversationHistory, recentUserHistory...)
|
||||
if !creativeUserRequest {
|
||||
gen2Context := interactiondomain.BuildGen2Context(conversationHistory)
|
||||
if !creativeUserRequest && locationReply == "" {
|
||||
if continuation := interactiondomain.ContinueLocalSearch(effectiveTaskMessage, conversationHistory); continuation != "" {
|
||||
continuationMessage = continuation
|
||||
effectiveTaskMessage = continuation
|
||||
@@ -540,6 +542,10 @@ func main() {
|
||||
continuationMessage = continuation
|
||||
travelContinuation = true
|
||||
effectiveTaskMessage = continuation
|
||||
} else if continuation := interactiondomain.ContinueTravelDate(effectiveTaskMessage, conversationHistory); continuation != "" {
|
||||
continuationMessage = continuation
|
||||
travelContinuation = true
|
||||
effectiveTaskMessage = continuation
|
||||
}
|
||||
}
|
||||
if request.ConversationID != "" && interactiondomain.IsRetryRequest(utterance.Content) {
|
||||
@@ -566,7 +572,11 @@ func main() {
|
||||
continuedSemantic, continuedErr := interactiondomain.InterpretUtterance(continuedUtterance, projects, previousFocus, now)
|
||||
if continuedErr == nil {
|
||||
continuedSemantic.Interpretation.Source = "CONVERSATION_CONTINUATION"
|
||||
continuedSemantic.Interpretation.SelectionReason = "location follow-up completed the previous local-search task"
|
||||
if travelContinuation {
|
||||
continuedSemantic.Interpretation.SelectionReason = "travel follow-up completed the previous travel task"
|
||||
} else {
|
||||
continuedSemantic.Interpretation.SelectionReason = "location follow-up completed the previous local-search task"
|
||||
}
|
||||
semantic = continuedSemantic
|
||||
}
|
||||
}
|
||||
@@ -599,8 +609,11 @@ func main() {
|
||||
}
|
||||
}
|
||||
}
|
||||
locationReply := interactiondomain.LocationContextReply(utterance.Content)
|
||||
if locationReply != "" {
|
||||
continuationMessage = ""
|
||||
travelContinuation = false
|
||||
effectiveTaskMessage = interactiondomain.StripAssistantVocative(utterance.Content)
|
||||
previousTask = nil
|
||||
semantic.Reply = locationReply
|
||||
semantic.NeedsCognition = false
|
||||
semantic.Interpretation.NeedsCognition = false
|
||||
@@ -616,6 +629,7 @@ func main() {
|
||||
assistantName = latestAssistantName(conversationHistory)
|
||||
}
|
||||
identityReply := ""
|
||||
memoryInstructionReply := ""
|
||||
log.Printf("interaction identity context: name=%q history=%d", assistantName, len(conversationHistory))
|
||||
if interactiondomain.IsIdentityInquiry(utterance.Content) && assistantName != "" {
|
||||
identityReply = "Меня зовут " + assistantName + ". Я Домовой, ваш помощник."
|
||||
@@ -625,6 +639,14 @@ func main() {
|
||||
semantic.Interpretation.Applicability = interaction.SemanticApplicable
|
||||
semantic.Interpretation.Clarification = interaction.ClarificationNotRequired
|
||||
}
|
||||
if identityReply == "" && interactiondomain.IsExplicitMemoryInstruction(utterance.Content) {
|
||||
memoryInstructionReply = "Запомнил."
|
||||
semantic.Reply = memoryInstructionReply
|
||||
semantic.NeedsCognition = false
|
||||
semantic.CapabilityHints = nil
|
||||
semantic.Interpretation.Applicability = interaction.SemanticApplicable
|
||||
semantic.Interpretation.Clarification = interaction.ClarificationNotRequired
|
||||
}
|
||||
deterministicSpeechAct := semantic.Interpretation.SpeechAct
|
||||
deterministicReply := semantic.Reply
|
||||
routePlan := buildChatRoutePlan(semantic)
|
||||
@@ -632,7 +654,14 @@ func main() {
|
||||
cognitiveAvailable := true
|
||||
semanticInterpretationRequested := shouldRequestSemanticInterpretation(semantic)
|
||||
semanticReflexAudit := shouldAuditDeterministicReflex(semantic, string(utterance.ID), now)
|
||||
if identityReply != "" {
|
||||
// A selected specialized route already has an executable semantic outcome.
|
||||
// Do not block it on the general cognitive channel: research and image
|
||||
// generation must reach their own adapters even when cognition is degraded.
|
||||
if specializedRouteBypassesGeneralInterpretation(routePlan) {
|
||||
semanticInterpretationRequested = false
|
||||
semanticReflexAudit = false
|
||||
}
|
||||
if identityReply != "" || memoryInstructionReply != "" {
|
||||
semanticReflexAudit = false
|
||||
}
|
||||
if semanticInterpretationRequested || semanticReflexAudit {
|
||||
@@ -741,8 +770,8 @@ func main() {
|
||||
case interaction.InformationRequestMemoryInventory:
|
||||
planningReply = humanMemoryInventory(conversationHistory, assistantName)
|
||||
needsCognition = false
|
||||
case interaction.InformationRequestTimeDifference:
|
||||
planningReply = humanTimeDifferenceReply(effectiveTaskMessage)
|
||||
case interaction.InformationRequestResearchFailureExplanation:
|
||||
planningReply = "Я не выдаю предположение как факт, если не получил согласованных проверяемых данных. Могу повторить поиск, уточнить запрос или проверить другой источник."
|
||||
needsCognition = false
|
||||
case interaction.InformationRequestCapabilityInventory:
|
||||
kinds, err := capabilityRepository.AvailableCapabilityKinds(r.Context(), session.PrincipalID)
|
||||
@@ -752,17 +781,27 @@ func main() {
|
||||
planningReply = "Я умею общаться, сохранять контекст, искать проверяемую информацию и работать с подключёнными моделями. Дополнительные действия подключаются отдельно."
|
||||
}
|
||||
needsCognition = false
|
||||
case interaction.InformationRequestLocalCapabilityInventory:
|
||||
planningReply = "Без внешних моделей я могу поддерживать разговор, хранить контекст, вести цели и напоминания, а также честно сообщать состояние подключённых возможностей."
|
||||
needsCognition = false
|
||||
case interaction.InformationRequestCognitiveModelInventory:
|
||||
candidates, inventoryErr := capabilityRepository.LoadCognitiveCandidates(r.Context(), capability.CapabilityID("creator-advisory"), session.PrincipalID)
|
||||
planningReply = humanCognitiveModelInventory(candidates, inventoryErr)
|
||||
needsCognition = false
|
||||
case interaction.InformationRequestCognitiveCurrentModel:
|
||||
health, healthErr := capabilityRepository.LoadHealth(r.Context(), capability.CapabilityID("cognitive-proposal"))
|
||||
planningReply = humanCurrentCognitiveModelReply(health, healthErr)
|
||||
needsCognition = false
|
||||
case interaction.InformationRequestCognitiveChannelStatus, interaction.InformationRequestCognitiveRecovery:
|
||||
health, healthErr := capabilityRepository.LoadHealth(r.Context(), capability.CapabilityID("cognitive-proposal"))
|
||||
planningReply = humanCognitiveChannelReply(semantic.InformationRequestKind, semantic.Interpretation.SpeechAct, health, healthErr)
|
||||
needsCognition = false
|
||||
}
|
||||
researchBacked := false
|
||||
if hasChatRoute(routePlan, chatRouteResearch) && semantic.Interpretation.Clarification != interaction.ClarificationRequired {
|
||||
researchAttempted := false
|
||||
stableKnowledgeReply := false
|
||||
if hasChatRoute(routePlan, chatRouteResearch) && semantic.Interpretation.Clarification != interaction.ClarificationRequired && shouldRunGeneralResearch(semantic.InformationRequestKind) {
|
||||
researchAttempted = true
|
||||
if planID, idErr := ids.New(); idErr == nil {
|
||||
parts := interactiondomain.IndependentRequestParts(effectiveTaskMessage)
|
||||
replies := make([]string, 0, len(parts))
|
||||
@@ -771,13 +810,35 @@ func main() {
|
||||
replies = append(replies, "По запросу «"+part+"»: "+clarification)
|
||||
continue
|
||||
}
|
||||
researchCtx, cancelResearch := context.WithTimeout(r.Context(), 8*time.Second)
|
||||
learnedLexicon := interactiondomain.TaskLexiconFromHistory(conversationHistory)
|
||||
// Preserve the original utterance for semantic route compilation
|
||||
// before removing a search imperative. Distance and route skills
|
||||
// need the user's relation words (for example "до") to bind a
|
||||
// remembered current location to the requested destination.
|
||||
searchQuery := interactiondomain.TravelResearchQuery(part, conversationHistory, now)
|
||||
searchQuery = interactiondomain.SearchQueryWithLexicon(searchQuery, learnedLexicon)
|
||||
searchQuery = interactiondomain.TimeDifferenceResearchQuery(searchQuery)
|
||||
// The research capability may try its configured source and a
|
||||
// source-recovery fallback. Its caller must leave enough budget for
|
||||
// both attempts and evidence evaluation.
|
||||
researchCtx, cancelResearch := context.WithTimeout(r.Context(), 18*time.Second)
|
||||
result, researchErr := researchClient.SearchWeb(researchCtx, epistemics.WebSearchRequest{
|
||||
Plan: epistemics.ResearchPlan{ID: epistemics.ResearchPlanID(fmt.Sprintf("plan:%s:%d", planID, index)), Question: part, SourceClasses: []string{"web-search"}, FreshnessPolicy: "current"},
|
||||
PropositionKey: fmt.Sprintf("question:%s:%d", utterance.ID, index), Query: part,
|
||||
Plan: epistemics.ResearchPlan{ID: epistemics.ResearchPlanID(fmt.Sprintf("plan:%s:%d", planID, index)), Question: searchQuery, SourceClasses: []string{"web-search"}, FreshnessPolicy: "current"},
|
||||
PropositionKey: fmt.Sprintf("question:%s:%d", utterance.ID, index), Query: searchQuery,
|
||||
})
|
||||
reply, backed := renderResearchReply(searchQuery, result, researchErr)
|
||||
if !backed && cognitiveAvailable {
|
||||
answerCtx, cancelAnswer := context.WithTimeout(r.Context(), 12*time.Second)
|
||||
answer, answerErr := cognitiveClient.AnswerStableKnowledge(answerCtx, session, part, gen2Context.Text, cognitive.TaskMeaning{
|
||||
SpeechAct: string(semantic.Interpretation.SpeechAct), CapabilityHints: toCapabilityKinds(semantic.CapabilityHints), RequestedOutcome: semantic.Interpretation.RequestedOutcome, InformationRequestKind: semantic.InformationRequestKind, ConfidenceMillis: semantic.Interpretation.ConfidenceMillis,
|
||||
})
|
||||
cancelAnswer()
|
||||
if answerErr == nil && stableKnowledgeAnswer(answer) {
|
||||
reply, backed = answer, true
|
||||
stableKnowledgeReply = true
|
||||
}
|
||||
}
|
||||
cancelResearch()
|
||||
reply, backed := renderResearchReply(part, result, researchErr)
|
||||
if backed {
|
||||
researchBacked = true
|
||||
}
|
||||
@@ -805,6 +866,19 @@ func main() {
|
||||
needsCognition = false
|
||||
}
|
||||
}
|
||||
canGenerateTextReply := shouldGenerateNonMutatingTextDirective(semantic, routePlan, helpPath, creativeRequest) && planningReply == deterministicReply
|
||||
if (planningReply == "" && needsCognition && semantic.Interpretation.SpeechAct == interaction.SpeechActQuestion || canGenerateTextReply) && !hasChatRoute(routePlan, chatRouteResearch) && !creativeRequest {
|
||||
answerCtx, cancelAnswer := context.WithTimeout(r.Context(), 12*time.Second)
|
||||
answer, answerErr := cognitiveClient.AnswerStableKnowledge(answerCtx, session, effectiveTaskMessage, gen2Context.Text, cognitive.TaskMeaning{
|
||||
SpeechAct: string(semantic.Interpretation.SpeechAct), CapabilityHints: toCapabilityKinds(semantic.CapabilityHints), RequestedOutcome: semantic.Interpretation.RequestedOutcome, InformationRequestKind: semantic.InformationRequestKind, ConfidenceMillis: semantic.Interpretation.ConfidenceMillis,
|
||||
})
|
||||
cancelAnswer()
|
||||
if answerErr == nil && stableKnowledgeAnswer(answer) {
|
||||
planningReply = answer
|
||||
stableKnowledgeReply = true
|
||||
needsCognition = false
|
||||
}
|
||||
}
|
||||
if planningReply == "" && needsCognition && cognitiveAttempted && !cognitiveAvailable {
|
||||
planningReply = humanCognitiveDegradedReply(utterance.Content)
|
||||
needsCognition = false
|
||||
@@ -821,7 +895,9 @@ func main() {
|
||||
var reminder *continuity.ProspectiveItem
|
||||
var generatedImage capability.MultimodalResponse
|
||||
mediaHandled := false
|
||||
mediaAttempted := false
|
||||
if hasChatRoute(routePlan, chatRouteMedia) && semantic.Interpretation.Clarification != interaction.ClarificationRequired {
|
||||
mediaAttempted = true
|
||||
imageCtx, cancelImage := context.WithTimeout(r.Context(), 15*time.Minute)
|
||||
generatedImage, err = cognitiveClient.GenerateImage(imageCtx, effectiveTaskMessage, "")
|
||||
cancelImage()
|
||||
@@ -910,9 +986,13 @@ func main() {
|
||||
planningReply = identityReply
|
||||
needsCognition = false
|
||||
}
|
||||
if memoryInstructionReply != "" {
|
||||
planningReply = memoryInstructionReply
|
||||
needsCognition = false
|
||||
}
|
||||
if strings.TrimSpace(planningReply) != "" {
|
||||
reviewContext, cancelReview := context.WithTimeout(r.Context(), 12*time.Second)
|
||||
reviewedReply, reviewErr := cognitiveClient.ReviewResponse(reviewContext, session, utterance.Content, planningReply, cognitive.TaskMeaning{
|
||||
reviewedReply, reviewErr := cognitiveClient.ReviewResponse(reviewContext, session, utterance.Content, planningReply, gen2Context.Text, cognitive.TaskMeaning{
|
||||
SpeechAct: string(semantic.Interpretation.SpeechAct),
|
||||
CapabilityHints: toCapabilityKinds(semantic.CapabilityHints),
|
||||
RequestedOutcome: semantic.Interpretation.RequestedOutcome,
|
||||
@@ -920,16 +1000,22 @@ func main() {
|
||||
ConfidenceMillis: semantic.Interpretation.ConfidenceMillis,
|
||||
})
|
||||
cancelReview()
|
||||
stableSemanticReply := mediaHandled || identityReply != "" || creativeReply != "" || (creativeFallbackReply != "" && !cognitiveAvailable) || semantic.InformationRequestKind != "" || locationReply != "" || deterministicSpeechAct == interaction.SpeechActConversational || deterministicSpeechAct == interaction.SpeechActConfirmation || deterministicSpeechAct == interaction.SpeechActRejection
|
||||
// A source-backed research result is owned by the research capability,
|
||||
// but its wording is not canonical state. Let the cognitive editor turn
|
||||
// it into a useful human answer while reviewPreservesResearchEvidence
|
||||
// still requires the source evidence to remain present. Information
|
||||
// requests without research (identity, channel state and so on) remain
|
||||
// stable semantic replies and cannot be rewritten here.
|
||||
stableSemanticReply := mediaAttempted || identityReply != "" || memoryInstructionReply != "" || creativeReply != "" || (creativeFallbackReply != "" && !cognitiveAvailable) || (semantic.InformationRequestKind != "" && !(researchAttempted && researchBacked)) || locationReply != "" || deterministicSpeechAct == interaction.SpeechActConversational || deterministicSpeechAct == interaction.SpeechActConfirmation || deterministicSpeechAct == interaction.SpeechActRejection
|
||||
if reviewErr != nil {
|
||||
log.Printf("cognitive response review unavailable: %v", reviewErr)
|
||||
} else if !stableSemanticReply && (!researchBacked || reviewPreservesResearchEvidence(reviewedReply, planningReply)) {
|
||||
} else if reviewMayReplacePlanningReply(stableSemanticReply, stableKnowledgeReply, researchAttempted, researchBacked, reviewedReply, planningReply) {
|
||||
planningReply = reviewedReply
|
||||
} else if reviewedReply != planningReply {
|
||||
log.Printf("cognitive response review proposed stable semantic rewrite; preserving semantic reply")
|
||||
}
|
||||
}
|
||||
if !mediaHandled && identityReply == "" && creativeReply == "" && creativeFallbackReply == "" {
|
||||
if !mediaAttempted && !mediaHandled && identityReply == "" && creativeReply == "" && creativeFallbackReply == "" {
|
||||
stableAct := interactiondomain.ClassifySpeechAct(utterance.Content, utterance.CorrectsUtteranceID)
|
||||
travelClarification := ""
|
||||
if !creativeRequest {
|
||||
@@ -945,6 +1031,11 @@ func main() {
|
||||
needsCognition = false
|
||||
planningReply = identityReply
|
||||
}
|
||||
if memoryInstructionReply != "" {
|
||||
semantic.Reply = memoryInstructionReply
|
||||
needsCognition = false
|
||||
planningReply = memoryInstructionReply
|
||||
}
|
||||
if creativeReply != "" {
|
||||
semantic.Reply = creativeReply
|
||||
needsCognition = false
|
||||
@@ -1579,6 +1670,10 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
func shouldRunGeneralResearch(kind interaction.InformationRequestKind) bool {
|
||||
return kind == "" || kind == interaction.InformationRequestTimeDifference
|
||||
}
|
||||
|
||||
func creatorSessionHandler(bootstrapToken, creatorUsername, creatorPassword string, sessionKey []byte, now func() time.Time) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
authenticated := identitydomain.BootstrapMatches(bootstrapToken, bearerToken(r))
|
||||
@@ -1699,32 +1794,227 @@ func renderResearchReply(query string, result epistemics.ResearchResult, researc
|
||||
return "Сейчас не удалось получить данные из подключённого источника. Попробую снова через доступный маршрут.", false
|
||||
}
|
||||
lines := make([]string, 0, 2)
|
||||
for index, finding := range result.Findings {
|
||||
for _, finding := range result.Findings {
|
||||
if finding.State != epistemics.FindingSupported || strings.TrimSpace(finding.Excerpt.Text) == "" || strings.TrimSpace(finding.Source.Locator) == "" {
|
||||
continue
|
||||
}
|
||||
if !researchFindingMatchesIntent(query, finding) {
|
||||
continue
|
||||
}
|
||||
excerpt := humanResearchExcerpt(query, finding.Excerpt.Text)
|
||||
limit := 180
|
||||
if index > 0 {
|
||||
if len(lines) > 0 {
|
||||
limit = 100
|
||||
}
|
||||
if len([]rune(excerpt)) > limit {
|
||||
excerpt = string([]rune(excerpt)[:limit]) + "…"
|
||||
}
|
||||
lines = append(lines, excerpt+"\nИсточник: "+researchSourceLabel(finding))
|
||||
// A nearest-flight request needs one route-relevant schedule result.
|
||||
// Appending weaker matches makes unrelated category pages look like
|
||||
// alternatives even after the relevance check has accepted the route.
|
||||
if nearestFlightQuery(query) {
|
||||
break
|
||||
}
|
||||
if len(lines) == 2 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(lines) == 0 {
|
||||
if identifiers := requestedResearchIdentifiers(query); len(identifiers) > 0 {
|
||||
return "Не нашёл проверяемых данных о «" + strings.Join(identifiers, "», «") + "». Возможно, название написано иначе; пришлите ссылку или уточните написание, и я проверю.", false
|
||||
}
|
||||
return "Проверяемых данных пока не найдено. Могу повторить поиск или уточнить, что именно проверить.", false
|
||||
}
|
||||
reply := "Нашёл проверяемые данные:\n\n" + strings.Join(lines, "\n\n")
|
||||
lowerQuery := strings.ToLower(query)
|
||||
if (strings.Contains(lowerQuery, "цен") || strings.Contains(lowerQuery, "стоим")) && !containsPriceEvidence(reply) {
|
||||
reply = "Актуальная цена в найденных фрагментах не указана, поэтому не буду придумывать сумму. Вот страницы, где её можно проверить:\n" + reply
|
||||
if isInternetStatusQuery(query) {
|
||||
return "Интернет работает: Домовой получил ответ от внешнего источника.", true
|
||||
}
|
||||
return reply, true
|
||||
if nearestFlightQuery(query) && !containsFlightTimeEvidence(strings.Join(lines, " ")) {
|
||||
return "Нашёл расписание по маршруту, но точное время ближайшего вылета в доступных фрагментах не указано. Откройте источник и выберите ближайший рейс:\n\n" + strings.Join(lines, "\n\n"), true
|
||||
}
|
||||
if trainScheduleQuery(query) && !containsScheduleTimeEvidence(strings.Join(lines, " ")) {
|
||||
return "Нашёл страницу расписания по маршруту, но в доступных фрагментах нет точного времени отправления:\n\n" + strings.Join(lines, "\n\n"), true
|
||||
}
|
||||
return "Нашёл проверяемые данные:\n\n" + strings.Join(lines, "\n\n"), true
|
||||
}
|
||||
|
||||
func isInternetStatusQuery(query string) bool {
|
||||
lower := strings.ToLower(query)
|
||||
return (strings.Contains(lower, "интернет") || strings.Contains(lower, "сет")) &&
|
||||
(strings.Contains(lower, "работает") || strings.Contains(lower, "доступ") || strings.Contains(lower, "проверь") || strings.Contains(lower, "связ"))
|
||||
}
|
||||
|
||||
func nearestFlightQuery(query string) bool {
|
||||
lower := strings.ToLower(query)
|
||||
return strings.Contains(lower, "ближ") && (strings.Contains(lower, "рейс") || strings.Contains(lower, "самолет") || strings.Contains(lower, "самолёт") || strings.Contains(lower, "вылет"))
|
||||
}
|
||||
|
||||
// researchFindingMatchesIntent is a final relevance check over evidence
|
||||
// already obtained by the read-only research route. It prevents unrelated
|
||||
// search-engine snippets from becoming a human-facing answer when a search
|
||||
// term has a common non-travel meaning.
|
||||
func researchFindingMatchesIntent(query string, finding epistemics.ResearchFinding) bool {
|
||||
if !findingContainsRequestedIdentifier(query, finding) {
|
||||
return false
|
||||
}
|
||||
if ticketSearchQuery(query) && !ticketFindingMatchesDestination(query, finding) {
|
||||
return false
|
||||
}
|
||||
if distanceResearchQuery(query) && !containsDistanceEvidence(finding.Excerpt.Text) {
|
||||
return false
|
||||
}
|
||||
if localPlaceSearchQuery(query) && !containsLocalPlaceEvidence(finding.Excerpt.Text) {
|
||||
return false
|
||||
}
|
||||
if !nearestFlightQuery(query) {
|
||||
return true
|
||||
}
|
||||
displayedExcerpt := strings.ToLower(humanResearchExcerpt(query, finding.Excerpt.Text))
|
||||
travelEvidence := strings.Contains(displayedExcerpt, "рейс") ||
|
||||
strings.Contains(displayedExcerpt, "вылет") ||
|
||||
strings.Contains(displayedExcerpt, "авиабил") ||
|
||||
strings.Contains(displayedExcerpt, "аэропорт") ||
|
||||
strings.Contains(displayedExcerpt, "самолет") ||
|
||||
strings.Contains(displayedExcerpt, "самолёт") ||
|
||||
strings.Contains(displayedExcerpt, "расписан")
|
||||
if !travelEvidence {
|
||||
return false
|
||||
}
|
||||
routeTerms := nearestFlightRouteTerms(query)
|
||||
if len(routeTerms) < 2 {
|
||||
return true
|
||||
}
|
||||
// A route-looking URL or page title alone is not enough for a human-facing
|
||||
// answer: the displayed excerpt itself must support both endpoints.
|
||||
excerptEvidence := strings.ToLower(finding.Excerpt.Text)
|
||||
for _, term := range routeTerms {
|
||||
if !strings.Contains(excerptEvidence, term) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func localPlaceSearchQuery(query string) bool {
|
||||
lower := strings.ToLower(query)
|
||||
return (strings.Contains(lower, "ресторан") || strings.Contains(lower, "кафе")) &&
|
||||
(strings.Contains(lower, "адрес") || strings.Contains(lower, "отзыв") || strings.Contains(lower, "меню"))
|
||||
}
|
||||
|
||||
func containsLocalPlaceEvidence(value string) bool {
|
||||
lower := strings.ToLower(value)
|
||||
for _, term := range []string{"ресторан", "кафе", "адрес", "меню", "отзыв", "кухн"} {
|
||||
if strings.Contains(lower, term) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func distanceResearchQuery(query string) bool {
|
||||
lower := strings.ToLower(query)
|
||||
return strings.Contains(lower, "расстоян") || strings.Contains(lower, "километр")
|
||||
}
|
||||
|
||||
func containsDistanceEvidence(value string) bool {
|
||||
return regexp.MustCompile(`(?i)\b\d[\d\s]*\s*(?:км|километр)`).MatchString(value)
|
||||
}
|
||||
|
||||
func ticketSearchQuery(query string) bool {
|
||||
return interactiondomain.TicketSearchDestination(query) != ""
|
||||
}
|
||||
|
||||
func ticketFindingMatchesDestination(query string, finding epistemics.ResearchFinding) bool {
|
||||
destination := strings.ToLower(interactiondomain.TicketSearchDestination(query))
|
||||
if destination == "" {
|
||||
return true
|
||||
}
|
||||
// The source title or URL may describe a category; human-facing proof must
|
||||
// mention the requested destination in the actual displayed excerpt.
|
||||
return strings.Contains(strings.ToLower(finding.Excerpt.Text), destination)
|
||||
}
|
||||
|
||||
// findingContainsRequestedIdentifier prevents a generic landing page from
|
||||
// being presented as a result for a concrete model, article or brand request.
|
||||
// Identifiers are detected by their syntax, not by a catalogue of providers or
|
||||
// entities, so the rule stays valid as user vocabulary changes.
|
||||
func findingContainsRequestedIdentifier(query string, finding epistemics.ResearchFinding) bool {
|
||||
identifiers := requestedResearchIdentifiers(query)
|
||||
if len(identifiers) == 0 {
|
||||
return true
|
||||
}
|
||||
evidence := strings.ToLower(strings.Join([]string{finding.Source.Title, finding.Source.Locator, finding.Excerpt.Text}, " "))
|
||||
for _, identifier := range identifiers {
|
||||
if !strings.Contains(evidence, strings.ToLower(identifier)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func requestedResearchIdentifiers(query string) []string {
|
||||
rawIdentifiers := regexp.MustCompile(`(?i)[a-z0-9][a-z0-9._-]{3,}`).FindAllString(query, -1)
|
||||
identifiers := make([]string, 0, len(rawIdentifiers))
|
||||
exact := make([]string, 0, len(rawIdentifiers))
|
||||
for _, identifier := range rawIdentifiers {
|
||||
if researchDateIdentifier(identifier) {
|
||||
continue
|
||||
}
|
||||
identifiers = append(identifiers, identifier)
|
||||
if strings.ContainsAny(identifier, "._-") || strings.IndexFunc(identifier, func(r rune) bool { return r >= '0' && r <= '9' }) >= 0 {
|
||||
exact = append(exact, identifier)
|
||||
}
|
||||
}
|
||||
if len(exact) > 0 {
|
||||
return exact
|
||||
}
|
||||
return identifiers
|
||||
}
|
||||
|
||||
func researchDateIdentifier(value string) bool {
|
||||
return regexp.MustCompile(`^\d{1,2}[.-]\d{1,2}[.-]\d{2,4}$`).MatchString(value) ||
|
||||
regexp.MustCompile(`^\d{4}[.-]\d{1,2}[.-]\d{1,2}$`).MatchString(value)
|
||||
}
|
||||
|
||||
// nearestFlightRouteTerms retains only the two endpoint-shaped terms from a
|
||||
// structured flight search. It has no city or provider vocabulary: endpoint
|
||||
// relevance is determined from the request itself.
|
||||
func nearestFlightRouteTerms(query string) []string {
|
||||
fields := regexp.MustCompile(`[\p{L}]{4,}`).FindAllString(strings.ToLower(query), -1)
|
||||
ignored := []string{"ближай", "авиарейс", "сегодня", "самолет", "самолёт", "вылет", "рейс", "пункт", "отправлен"}
|
||||
terms := make([]string, 0, 2)
|
||||
for _, field := range fields {
|
||||
skip := false
|
||||
for _, prefix := range ignored {
|
||||
if strings.HasPrefix(field, prefix) {
|
||||
skip = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if skip {
|
||||
continue
|
||||
}
|
||||
prefixLength := 4
|
||||
if len([]rune(field)) < prefixLength {
|
||||
prefixLength = len([]rune(field))
|
||||
}
|
||||
term := string([]rune(field)[:prefixLength])
|
||||
already := false
|
||||
for _, current := range terms {
|
||||
if current == term {
|
||||
already = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !already {
|
||||
terms = append(terms, term)
|
||||
}
|
||||
}
|
||||
return terms
|
||||
}
|
||||
|
||||
func containsFlightTimeEvidence(value string) bool {
|
||||
return regexp.MustCompile(`(?m)\b(?:[01]?\d|2[0-3]):[0-5]\d\b`).MatchString(value)
|
||||
}
|
||||
|
||||
func humanResearchExcerpt(query, raw string) string {
|
||||
@@ -1747,9 +2037,32 @@ func humanResearchExcerpt(query, raw string) string {
|
||||
return text
|
||||
}
|
||||
|
||||
func containsPriceEvidence(value string) bool {
|
||||
pricePattern := regexp.MustCompile(`(?i)(\d[\d\s.,]{2,})(?:\s*(?:₽|руб\.?|р\.?|usd|\$|€))`)
|
||||
return pricePattern.MatchString(value)
|
||||
func trainScheduleQuery(query string) bool {
|
||||
lower := strings.ToLower(query)
|
||||
return strings.Contains(lower, "поезд") && strings.Contains(lower, "время отправления")
|
||||
}
|
||||
|
||||
func containsScheduleTimeEvidence(value string) bool {
|
||||
return regexp.MustCompile(`\b(?:[01]?\d|2[0-3]):[0-5]\d\b`).MatchString(value)
|
||||
}
|
||||
|
||||
func stableKnowledgeAnswer(answer string) bool {
|
||||
return strings.TrimSpace(answer) != "" && strings.TrimSpace(answer) != "NEEDS_VERIFIED_RESEARCH"
|
||||
}
|
||||
|
||||
// shouldGenerateNonMutatingTextDirective identifies a request that can be
|
||||
// completed entirely in the reply. It excludes specialized routes and durable
|
||||
// work, so cognition can produce content but cannot stand in for an executor.
|
||||
func shouldGenerateNonMutatingTextDirective(semantic interactiondomain.SemanticResult, routes []chatRoute, helpPath work.HelpPath, creativeRequest bool) bool {
|
||||
if semantic.Interpretation.SpeechAct != interaction.SpeechActDirective || creativeRequest || helpPath.NeedsDurableWork {
|
||||
return false
|
||||
}
|
||||
// ANSWER and COGNITION are reply-only help paths. They do not execute an
|
||||
// external action, unlike direct capabilities, gaps or durable work.
|
||||
if helpPath.Kind != work.HelpPathAnswer && helpPath.Kind != work.HelpPathCognition && helpPath.Kind != "" {
|
||||
return false
|
||||
}
|
||||
return !hasChatRoute(routes, chatRouteResearch) && !hasChatRoute(routes, chatRouteMedia) && !hasChatRoute(routes, chatRouteAgent)
|
||||
}
|
||||
|
||||
func reviewPreservesResearchEvidence(reviewed, draft string) bool {
|
||||
@@ -1762,6 +2075,26 @@ func reviewPreservesResearchEvidence(reviewed, draft string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// reviewMayReplacePlanningReply keeps ownership of a concrete capability
|
||||
// result with that capability. A review may improve source-backed research,
|
||||
// but must not turn an honest no-result into an invented clarification.
|
||||
func reviewMayReplacePlanningReply(stableSemanticReply, stableKnowledgeReply, researchAttempted, researchBacked bool, reviewed, draft string) bool {
|
||||
// A stable-knowledge answer is a model-produced draft rather than an owned
|
||||
// capability result. It therefore goes through a second cognitive pass for
|
||||
// conversational quality, while identity, memory, media and action outcomes
|
||||
// remain immutable at this boundary.
|
||||
if stableKnowledgeReply {
|
||||
return true
|
||||
}
|
||||
if stableSemanticReply {
|
||||
return false
|
||||
}
|
||||
if !researchAttempted {
|
||||
return true
|
||||
}
|
||||
return researchBacked && reviewPreservesResearchEvidence(reviewed, draft)
|
||||
}
|
||||
|
||||
func humanMemoryInventory(history []interaction.HumanUtterance, assistantName string) string {
|
||||
rows := make([]string, 0, 4)
|
||||
if assistantName != "" {
|
||||
@@ -1774,7 +2107,13 @@ func humanMemoryInventory(history []interaction.HumanUtterance, assistantName st
|
||||
lower := strings.ToLower(message)
|
||||
if (strings.Contains(lower, "нравится") || strings.Contains(lower, "люблю")) && utterance.ReceivedAtUnixMillis >= latestPreferenceAt {
|
||||
latestPreferenceAt = utterance.ReceivedAtUnixMillis
|
||||
latestPreference = message
|
||||
latestPreference = strings.TrimSpace(message)
|
||||
for _, prefix := range []string{"запомни:", "запомни", "помни:"} {
|
||||
if strings.HasPrefix(strings.ToLower(latestPreference), prefix) {
|
||||
latestPreference = strings.TrimSpace(latestPreference[len(prefix):])
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if latestPreference != "" {
|
||||
@@ -1798,14 +2137,6 @@ func latestAssistantName(history []interaction.HumanUtterance) string {
|
||||
return latestName
|
||||
}
|
||||
|
||||
func humanTimeDifferenceReply(message string) string {
|
||||
lower := strings.ToLower(message)
|
||||
if strings.Contains(lower, "моск") && strings.Contains(lower, "шанх") {
|
||||
return "Шанхай опережает Москву на 5 часов. Если в Москве 12:00, в Шанхае 17:00."
|
||||
}
|
||||
return "Уточните два города — я сравню их часовые пояса."
|
||||
}
|
||||
|
||||
func researchSourceLabel(finding epistemics.ResearchFinding) string {
|
||||
title := strings.TrimSpace(finding.Source.Title)
|
||||
locator := strings.TrimSpace(finding.Source.Locator)
|
||||
@@ -1975,6 +2306,26 @@ func humanCognitiveChannelReply(kind interaction.InformationRequestKind, act int
|
||||
}
|
||||
}
|
||||
|
||||
// humanCurrentCognitiveModelReply answers about the route that produced this
|
||||
// reply. It intentionally does not infer a provider/model name from health:
|
||||
// that would claim a model was used when this direct status path did not call it.
|
||||
func humanCurrentCognitiveModelReply(health capability.Health, healthErr error) string {
|
||||
base := "Этот ответ сформирован встроенным режимом Домового; внешняя модель для него не использовалась."
|
||||
if healthErr != nil {
|
||||
return base + " Состояние канала мышления сейчас не удалось проверить."
|
||||
}
|
||||
switch health.State {
|
||||
case capability.HealthAvailable:
|
||||
return base + " Канал мышления доступен для следующих задач."
|
||||
case capability.HealthDegraded:
|
||||
return base + " Канал мышления работает с ограничениями."
|
||||
case capability.HealthUnavailable:
|
||||
return base + " Канал мышления сейчас недоступен."
|
||||
default:
|
||||
return base + " Состояние канала мышления пока не подтверждено."
|
||||
}
|
||||
}
|
||||
|
||||
func humanCognitiveDegradedReply(message string) string {
|
||||
if strings.TrimSpace(message) == "" {
|
||||
return "Сейчас не удалось обработать запрос."
|
||||
|
||||
@@ -11,7 +11,9 @@ import (
|
||||
"time"
|
||||
|
||||
"git.cin.su/m/Assistant/contracts/capability"
|
||||
"git.cin.su/m/Assistant/contracts/epistemics"
|
||||
"git.cin.su/m/Assistant/contracts/interaction"
|
||||
"git.cin.su/m/Assistant/contracts/work"
|
||||
interactiondomain "git.cin.su/m/Assistant/modules/interaction/domain"
|
||||
)
|
||||
|
||||
@@ -22,6 +24,170 @@ func TestHumanResearchExcerptRemovesSearchResultHeading(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNearestFlightEvidenceDoesNotClaimMissingDepartureTime(t *testing.T) {
|
||||
if !nearestFlightQuery("ближайший рейс Москва Казань сегодня") {
|
||||
t.Fatal("nearest flight query was not recognized")
|
||||
}
|
||||
if containsFlightTimeEvidence("Казань составляет 1 ч 25 мин. График вылетов доступен онлайн") {
|
||||
t.Fatal("duration was mistaken for a departure time")
|
||||
}
|
||||
if !containsFlightTimeEvidence("Ближайший вылет в 18:40") {
|
||||
t.Fatal("departure time was not recognized")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNearestFlightReplyFiltersUnrelatedSearchSnippets(t *testing.T) {
|
||||
result := epistemics.ResearchResult{Findings: []epistemics.ResearchFinding{
|
||||
{State: epistemics.FindingSupported, Source: epistemics.Source{Locator: "https://naidy.example", Title: "Мебельная фурнитура"}, Excerpt: epistemics.SourceExcerpt{Text: "Комплектующие для мебели оптом и в розницу."}},
|
||||
{State: epistemics.FindingSupported, Source: epistemics.Source{Locator: "https://rasp.example", Title: "Рейсы Москва — Казань"}, Excerpt: epistemics.SourceExcerpt{Text: "Расписание самолётов Москва — Казань, время вылета и прилёта."}},
|
||||
}}
|
||||
reply, backed := renderResearchReply("ближайшие авиарейсы Москва Казань сегодня", result, nil)
|
||||
if !backed || !strings.Contains(reply, "Рейсы Москва") || strings.Contains(reply, "Мебельная") {
|
||||
t.Fatalf("unrelated source leaked into flight reply: %q", reply)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNearestFlightReplyDoesNotAppendWeakerSecondMatch(t *testing.T) {
|
||||
result := epistemics.ResearchResult{Findings: []epistemics.ResearchFinding{
|
||||
{State: epistemics.FindingSupported, Source: epistemics.Source{Locator: "https://schedule.example", Title: "Рейсы Москва — Казань"}, Excerpt: epistemics.SourceExcerpt{Text: "Расписание авиарейсов Москва — Казань, время вылета и прилёта."}},
|
||||
{State: epistemics.FindingSupported, Source: epistemics.Source{Locator: "https://category.example", Title: "Категория направлений"}, Excerpt: epistemics.SourceExcerpt{Text: "Список близлежащих городов и ориентировочные цены."}},
|
||||
}}
|
||||
reply, backed := renderResearchReply("ближайшие авиарейсы Москва Казань сегодня", result, nil)
|
||||
if !backed || !strings.Contains(reply, "Рейсы Москва") || strings.Contains(reply, "Категория направлений") {
|
||||
t.Fatalf("weaker second finding leaked into nearest-flight reply: %q", reply)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNearestFlightEvidenceRequiresBothRouteEndpointsWhenKnown(t *testing.T) {
|
||||
query := "ближайшие авиарейсы Москва Казань сегодня"
|
||||
matched := epistemics.ResearchFinding{Source: epistemics.Source{Title: "Рейсы Москва — Казань"}, Excerpt: epistemics.SourceExcerpt{Text: "Расписание авиарейсов из Москвы в Казань."}}
|
||||
if !researchFindingMatchesIntent(query, matched) {
|
||||
t.Fatal("route evidence with both endpoints was rejected")
|
||||
}
|
||||
unrelated := epistemics.ResearchFinding{Source: epistemics.Source{Title: "Рейсы Москва — Казань", Locator: "https://example.test/moscow-kazan"}, Excerpt: epistemics.SourceExcerpt{Text: "Рейсы Москва — Казань — Ближайшие к Казани города; цена указана ориентировочно."}}
|
||||
if researchFindingMatchesIntent(query, unrelated) {
|
||||
t.Fatal("one-ended travel evidence was accepted")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTicketReplyRejectsGenericCategoryPageWithoutDestination(t *testing.T) {
|
||||
query := "найди билеты до Ялты сегодня"
|
||||
generic := epistemics.ResearchFinding{Source: epistemics.Source{Title: "Купить билеты", Locator: "https://tickets.example"}, Excerpt: epistemics.SourceExcerpt{Text: "Поиск авиабилетов среди множества направлений."}}
|
||||
if researchFindingMatchesIntent(query, generic) {
|
||||
t.Fatal("generic ticket category was presented as destination result")
|
||||
}
|
||||
relevant := epistemics.ResearchFinding{Source: epistemics.Source{Title: "Билеты"}, Excerpt: epistemics.SourceExcerpt{Text: "Билеты до Ялты: варианты на сегодня."}}
|
||||
if !researchFindingMatchesIntent(query, relevant) {
|
||||
t.Fatal("destination-specific ticket result was rejected")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDistanceReplyRejectsCalculatorPageWithoutDistance(t *testing.T) {
|
||||
query := "расстояние между Москве и Сочи в километрах по прямой и по дороге"
|
||||
calculator := epistemics.ResearchFinding{Source: epistemics.Source{Title: "Расчет расстояний"}, Excerpt: epistemics.SourceExcerpt{Text: "Калькулятор рассчитывает точное расстояние между городами."}}
|
||||
if researchFindingMatchesIntent(query, calculator) {
|
||||
t.Fatal("distance calculator landing page was presented without a measured distance")
|
||||
}
|
||||
measured := epistemics.ResearchFinding{Source: epistemics.Source{Title: "Маршрут"}, Excerpt: epistemics.SourceExcerpt{Text: "Расстояние по дороге составляет 1 620 км."}}
|
||||
if !researchFindingMatchesIntent(query, measured) {
|
||||
t.Fatal("measured distance evidence was rejected")
|
||||
}
|
||||
}
|
||||
|
||||
func TestResearchReplyRejectsGenericLandingPageForConcreteIdentifier(t *testing.T) {
|
||||
result := epistemics.ResearchResult{Findings: []epistemics.ResearchFinding{{
|
||||
State: epistemics.FindingSupported,
|
||||
Source: epistemics.Source{Locator: "https://market.example", Title: "Интернет-магазин"},
|
||||
Excerpt: epistemics.SourceExcerpt{Text: "Широкий ассортимент товаров и услуг."},
|
||||
}}}
|
||||
reply, backed := renderResearchReply("на WB бренд marka-wear", result, nil)
|
||||
if backed || !strings.Contains(reply, "marka-wear") || strings.Contains(reply, "wildberries") || !strings.Contains(reply, "уточните написание") {
|
||||
t.Fatalf("generic page was presented as a brand result: %q", reply)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLocalPlaceReplyRejectsDictionaryFinding(t *testing.T) {
|
||||
result := epistemics.ResearchResult{Findings: []epistemics.ResearchFinding{{
|
||||
State: epistemics.FindingSupported,
|
||||
Source: epistemics.Source{Locator: "https://dictionary.example", Title: "Ближайший — словарь"},
|
||||
Excerpt: epistemics.SourceExcerpt{Text: "Ближайший — значение слова и пример употребления."},
|
||||
}}}
|
||||
reply, backed := renderResearchReply("места ресторан в Сочи адрес отзывы меню", result, nil)
|
||||
if backed || !strings.Contains(reply, "Проверяемых данных") {
|
||||
t.Fatalf("dictionary finding was presented as local place result: %q", reply)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResearchReplyKeepsFindingContainingConcreteIdentifier(t *testing.T) {
|
||||
result := epistemics.ResearchResult{Findings: []epistemics.ResearchFinding{{
|
||||
State: epistemics.FindingSupported,
|
||||
Source: epistemics.Source{Locator: "https://market.example/marka-wear", Title: "Marka-wear"},
|
||||
Excerpt: epistemics.SourceExcerpt{Text: "Бренд marka-wear: товары и карточки."},
|
||||
}}}
|
||||
reply, backed := renderResearchReply("на WB бренд marka-wear", result, nil)
|
||||
if !backed || !strings.Contains(strings.ToLower(reply), "marka-wear") {
|
||||
t.Fatalf("concrete identifier finding was rejected: %q", reply)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResearchIdentifiersExcludeSearchDate(t *testing.T) {
|
||||
identifiers := requestedResearchIdentifiers("ближайший поезд в Петербург сегодня 26.08.2026")
|
||||
if len(identifiers) != 0 {
|
||||
t.Fatalf("search date was treated as a concrete identifier: %#v", identifiers)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResearchQueryKeepsDistanceRelationBeforeSearchNormalization(t *testing.T) {
|
||||
history := []interaction.HumanUtterance{{Content: "я сейчас в Москве"}}
|
||||
query := interactiondomain.TravelResearchQuery("сколько километров до Сочи?", history, time.Time{})
|
||||
query = interactiondomain.SearchQueryWithLexicon(query, interactiondomain.TaskLexicon{})
|
||||
if query != "расстояние между Москве и Сочи в километрах по прямой и по дороге" {
|
||||
t.Fatalf("distance query lost remembered origin: %q", query)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResponseReviewPreservesAttemptedResearchOutcome(t *testing.T) {
|
||||
if reviewMayReplacePlanningReply(false, false, true, false, "Уточните аэропорт.", "Пока не смог получить проверяемый ответ.") {
|
||||
t.Fatal("review replaced an honest unbacked research outcome")
|
||||
}
|
||||
if !reviewMayReplacePlanningReply(false, false, true, true, "Расписание. Источник: https://example.test", "Расписание. Источник: https://example.test") {
|
||||
t.Fatal("review should be able to improve a source-backed research outcome")
|
||||
}
|
||||
if reviewMayReplacePlanningReply(false, false, true, true, "Расписание без ссылки", "Расписание. Источник: https://example.test") {
|
||||
t.Fatal("review dropped the research evidence")
|
||||
}
|
||||
if !reviewMayReplacePlanningReply(false, false, false, false, "Нормальный ответ", "Черновик") {
|
||||
t.Fatal("ordinary conversation review was unexpectedly blocked")
|
||||
}
|
||||
if !reviewMayReplacePlanningReply(true, true, false, false, "Санкт-Петербург — город в России.", "Меня зовут Домовой. Санкт-Петербург — город в России.") {
|
||||
t.Fatal("stable-knowledge draft must be eligible for semantic quality review")
|
||||
}
|
||||
// The caller must not mark a source-backed research result as immutable
|
||||
// semantic state: it may be rewritten, but only with its evidence retained.
|
||||
if !reviewMayReplacePlanningReply(false, false, true, true, "Из Москвы в Сочи можно добраться самолётом или поездом. Источник: https://example.test", "Сырые результаты. Источник: https://example.test") {
|
||||
t.Fatal("source-backed research draft must be eligible for human-facing review")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNonMutatingTextDirectiveCanUseCognitiveReply(t *testing.T) {
|
||||
semantic := interactiondomain.SemanticResult{Interpretation: interaction.UtteranceInterpretation{SpeechAct: interaction.SpeechActDirective}}
|
||||
if !shouldGenerateNonMutatingTextDirective(semantic, nil, work.HelpPath{}, false) {
|
||||
t.Fatal("ordinary formatting directive was not eligible for a cognitive reply")
|
||||
}
|
||||
if !shouldGenerateNonMutatingTextDirective(semantic, nil, work.HelpPath{Kind: work.HelpPathCognition}, false) {
|
||||
t.Fatal("reply-only cognition path was incorrectly rejected")
|
||||
}
|
||||
if shouldGenerateNonMutatingTextDirective(semantic, nil, work.HelpPath{Kind: work.HelpPathDirectCapability}, false) {
|
||||
t.Fatal("direct capability was incorrectly replaced by a textual reply")
|
||||
}
|
||||
if shouldGenerateNonMutatingTextDirective(semantic, []chatRoute{{Kind: chatRouteMedia}}, work.HelpPath{}, false) {
|
||||
t.Fatal("media route was incorrectly replaced by a textual reply")
|
||||
}
|
||||
if shouldGenerateNonMutatingTextDirective(semantic, nil, work.HelpPath{NeedsDurableWork: true}, false) {
|
||||
t.Fatal("durable work was incorrectly replaced by a textual reply")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBearerTokenRequiresBearerScheme(t *testing.T) {
|
||||
request := httptest.NewRequest("GET", "/", nil)
|
||||
request.Header.Set("Authorization", "Basic value")
|
||||
@@ -55,12 +221,35 @@ func TestHumanMemoryInventoryUsesNewestPreference(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainsPriceEvidenceDistinguishesLinksFromPrices(t *testing.T) {
|
||||
if containsPriceEvidence("RTX 4090 — market.yandex.ru") {
|
||||
t.Fatal("a product link is not price evidence")
|
||||
func TestContainsScheduleTimeEvidence(t *testing.T) {
|
||||
if containsScheduleTimeEvidence("расписание поездов и цена билета") {
|
||||
t.Fatal("a price or schedule label is not a departure time")
|
||||
}
|
||||
if !containsPriceEvidence("Цена: 189 990 ₽") {
|
||||
t.Fatal("expected a currency amount to count as price evidence")
|
||||
if !containsScheduleTimeEvidence("Отправление в 07:45") {
|
||||
t.Fatal("expected an explicit departure time to be recognized")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInternetStatusQueryRecognizesConnectionChecks(t *testing.T) {
|
||||
for _, query := range []string{"проверь, работает ли интернет", "есть доступ к сети?"} {
|
||||
if !isInternetStatusQuery(query) {
|
||||
t.Fatalf("connection check was not recognized: %q", query)
|
||||
}
|
||||
}
|
||||
if isInternetStatusQuery("найди интернет-магазин") {
|
||||
t.Fatal("ordinary search must not be treated as a connectivity check")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInternetStatusReplyDoesNotLeakIncidentalSearchResults(t *testing.T) {
|
||||
result := epistemics.ResearchResult{Findings: []epistemics.ResearchFinding{{
|
||||
State: epistemics.FindingSupported,
|
||||
Source: epistemics.Source{Locator: "https://example.test", Title: "Incidental page"},
|
||||
Excerpt: epistemics.SourceExcerpt{Text: "A response was received."},
|
||||
}}}
|
||||
reply, backed := renderResearchReply("проверь, работает ли интернет", result, nil)
|
||||
if !backed || reply != "Интернет работает: Домовой получил ответ от внешнего источника." {
|
||||
t.Fatalf("unexpected internet status reply: %q", reply)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +260,13 @@ func TestCognitiveChannelQuestionUsesHumanSafeHealthReply(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCurrentModelReplyDoesNotInventProvider(t *testing.T) {
|
||||
reply := humanCurrentCognitiveModelReply(capability.Health{State: capability.HealthDegraded, CapabilityID: "provider-secret-model"}, nil)
|
||||
if !strings.Contains(reply, "встроенным режимом") || !strings.Contains(reply, "ограничениями") || strings.Contains(reply, "provider-secret-model") {
|
||||
t.Fatalf("unexpected current-model reply: %q", reply)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCognitiveConfigurationRequestDoesNotPretendToMutate(t *testing.T) {
|
||||
reply := humanCognitiveChannelReply(interaction.InformationRequestCognitiveRecovery, interaction.SpeechActDirective, capability.Health{State: capability.HealthUnavailable}, nil)
|
||||
if reply != "Канал мышления недоступен. Домовой остаётся на связи." {
|
||||
|
||||
@@ -251,6 +251,38 @@ func TestChatMessageUsesLocalAdvisoryForOrdinaryConversationalQuestion(t *testin
|
||||
}
|
||||
}
|
||||
|
||||
func TestChatMessagePreservesCompletedCoreAnswerInsteadOfSecondAdvisory(t *testing.T) {
|
||||
core := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch {
|
||||
case r.Method == http.MethodGet && r.URL.Path == "/v1/creator/sessions/current":
|
||||
_ = json.NewEncoder(w).Encode(identity.CreatorSession{ActorID: "actor-1", PrincipalID: "principal-1"})
|
||||
case r.Method == http.MethodPost && r.URL.Path == "/v1/creator/chat/messages":
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
_ = json.NewEncoder(w).Encode(interaction.ChatMessageResponse{ConversationID: "conversation-1", UtteranceID: "utterance-1", SpeechAct: interaction.SpeechActQuestion, NeedsCognition: true, FallbackText: "fallback", PlanningReply: "Готовый ответ ядра."})
|
||||
default:
|
||||
t.Fatalf("unexpected core route %s %s", r.Method, r.URL.Path)
|
||||
}
|
||||
}))
|
||||
defer core.Close()
|
||||
cognitiveRuntime := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatal("completed core answer must not be replaced by a second advisory")
|
||||
}))
|
||||
defer cognitiveRuntime.Close()
|
||||
handler, err := newHandler(core.URL, "http://continuity", "http://commit", core.Client(), cognitiveRuntime.URL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
request := httptest.NewRequest(http.MethodPost, "/api/chat/messages", strings.NewReader(`{"message":"Обычный вопрос"}`))
|
||||
request.Header.Set("Authorization", "Bearer session-token")
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
recorder := httptest.NewRecorder()
|
||||
handler.ServeHTTP(recorder, request)
|
||||
var response browserChatResponse
|
||||
if recorder.Code != http.StatusOK || json.NewDecoder(recorder.Body).Decode(&response) != nil || response.ReplyText != "Готовый ответ ядра." {
|
||||
t.Fatalf("unexpected browser response: status=%d body=%s", recorder.Code, recorder.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestChatMessageReturnsHumanCapabilityMetadata(t *testing.T) {
|
||||
core := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch {
|
||||
|
||||
@@ -28,7 +28,11 @@ import (
|
||||
const (
|
||||
maxRequestBytes = 8 << 10
|
||||
maxAdvisoryRequestBytes = 20 << 10
|
||||
maxResponseBytes = 1 << 20
|
||||
// Chat responses may contain a locally generated image encoded as base64.
|
||||
// Keep the gateway limit aligned with the bounded media transport (32 MiB),
|
||||
// otherwise a successfully generated image is incorrectly surfaced as an
|
||||
// unavailable runtime to the human.
|
||||
maxResponseBytes = 32 << 20
|
||||
)
|
||||
|
||||
//go:embed static
|
||||
@@ -476,7 +480,10 @@ func (g *gateway) handleChatMessage(w http.ResponseWriter, r *http.Request) {
|
||||
saved.NeedsCognition = false
|
||||
}
|
||||
}
|
||||
if saved.NeedsCognition && !saved.WorkPlanned && saved.Clarification != interaction.ClarificationRequired {
|
||||
// Core owns the completed planning answer. The browser may ask for a local
|
||||
// advisory only when core explicitly left the turn unanswered; otherwise a
|
||||
// second model pass can replace a grounded result with a weaker one.
|
||||
if saved.NeedsCognition && saved.PlanningReply == "" && !saved.WorkPlanned && saved.Clarification != interaction.ClarificationRequired {
|
||||
cognition = "unavailable"
|
||||
requestID, idErr := ids.New()
|
||||
if idErr == nil {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -13,6 +14,25 @@ import (
|
||||
"git.cin.su/m/Assistant/contracts/interaction"
|
||||
)
|
||||
|
||||
func TestMaxResponseBytesAcceptsGeneratedImagePayload(t *testing.T) {
|
||||
// A 1024px PNG can exceed the former 1 MiB gateway limit once base64
|
||||
// encoded. The browser gateway must forward that successful core response.
|
||||
payload, err := json.Marshal(interaction.ChatMessageResponse{
|
||||
GeneratedImageContentType: "image/png",
|
||||
GeneratedImageBase64: strings.Repeat("A", 2<<20),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var decoded interaction.ChatMessageResponse
|
||||
if err := decodeStrictJSON(bytes.NewReader(payload), maxResponseBytes, &decoded); err != nil {
|
||||
t.Fatalf("generated image response must fit gateway limit: %v", err)
|
||||
}
|
||||
if len(decoded.GeneratedImageBase64) != 2<<20 {
|
||||
t.Fatalf("generated image payload was truncated: %d", len(decoded.GeneratedImageBase64))
|
||||
}
|
||||
}
|
||||
|
||||
func TestLocalChatContextIncludesDomovoyIdentityAndOptionalActiveProject(t *testing.T) {
|
||||
context := localChatContext(interaction.ChatMessageResponse{AssistantName: "Фея", ActiveProjectName: "Домовой"})
|
||||
if len(context) != 2 || context[0].SourceID != "domovoy" || !strings.Contains(context[0].Text, "Ты — Фея") || !strings.Contains(context[0].Text, "Меня зовут Фея") || context[1].Text != "Текущий проект: Домовой" {
|
||||
|
||||
@@ -49,6 +49,9 @@ func validateTaskPacket(root string, task TaskPacket, taskPath string) error {
|
||||
if err := validateArchitectureRevision(root, task.ArchitectureRevision); err != nil {
|
||||
return fmt.Errorf("task %s architecture_revision: %w", task.TaskID, err)
|
||||
}
|
||||
if err := validateGenerationTask(task); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := resolveBaseCommit(root, task.BaseRepoRevision); err != nil {
|
||||
return fmt.Errorf("task %s base_repo_revision: %w", task.TaskID, err)
|
||||
}
|
||||
@@ -58,6 +61,24 @@ func validateTaskPacket(root string, task TaskPacket, taskPath string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateGenerationTask(task TaskPacket) error {
|
||||
switch {
|
||||
case task.TaskID == "PRE-GEN2-000":
|
||||
if task.BaseRepoRevision != "origin/main" || task.ArchitectureRevision != "ADR-0019" {
|
||||
return fmt.Errorf("task %s must use origin/main and ADR-0019", task.TaskID)
|
||||
}
|
||||
case task.TaskID == "ARCH-GEN2-000":
|
||||
if task.BaseRepoRevision != "gen2-integration" || task.ArchitectureRevision != "ADR-0019" {
|
||||
return fmt.Errorf("task %s must use gen2-integration and ADR-0019", task.TaskID)
|
||||
}
|
||||
case strings.HasPrefix(task.TaskID, "GEN2-"):
|
||||
if task.BaseRepoRevision != "gen2-integration" || task.ArchitectureRevision != "ARCH-GEN2-000" {
|
||||
return fmt.Errorf("task %s must use gen2-integration and ARCH-GEN2-000", task.TaskID)
|
||||
}
|
||||
}
|
||||
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"} {
|
||||
|
||||
@@ -25,3 +25,42 @@ func TestValidateDeltaRejectsUnknownOrMissingFields(t *testing.T) {
|
||||
t.Fatalf("delta error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateGenerationTaskRequiresGen2BaseAndArchitecture(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
task TaskPacket
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "architecture task requires integration base",
|
||||
task: TaskPacket{TaskID: "ARCH-GEN2-000", BaseRepoRevision: "origin/main", ArchitectureRevision: "ADR-0019"},
|
||||
want: "gen2-integration",
|
||||
},
|
||||
{
|
||||
name: "implementation task requires accepted architecture root",
|
||||
task: TaskPacket{TaskID: "GEN2-001", BaseRepoRevision: "gen2-integration", ArchitectureRevision: "ADR-0019"},
|
||||
want: "ARCH-GEN2-000",
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := validateGenerationTask(tc.task)
|
||||
if err == nil || !strings.Contains(err.Error(), tc.want) {
|
||||
t.Fatalf("generation validation error = %v, want %q", err, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateGenerationTaskAcceptsExecutionRoots(t *testing.T) {
|
||||
for _, task := range []TaskPacket{
|
||||
{TaskID: "PRE-GEN2-000", BaseRepoRevision: "origin/main", ArchitectureRevision: "ADR-0019"},
|
||||
{TaskID: "ARCH-GEN2-000", BaseRepoRevision: "gen2-integration", ArchitectureRevision: "ADR-0019"},
|
||||
{TaskID: "GEN2-001", BaseRepoRevision: "gen2-integration", ArchitectureRevision: "ARCH-GEN2-000"},
|
||||
} {
|
||||
if err := validateGenerationTask(task); err != nil {
|
||||
t.Fatalf("validateGenerationTask(%+v) = %v", task, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
import "net/http"
|
||||
|
||||
func main() {
|
||||
_ = http.ListenAndServe(":8088", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("lifeboat-ready\n"))
|
||||
}))
|
||||
}
|
||||
@@ -5,6 +5,18 @@ import "git.cin.su/m/Assistant/platform/ids"
|
||||
import "errors"
|
||||
|
||||
type ArtifactID ids.ID
|
||||
type MediaArtifact struct {
|
||||
ID ArtifactID
|
||||
ContentType string
|
||||
SizeBytes int64
|
||||
ContentDigest string
|
||||
SourceRef string
|
||||
RecordedAtUnixMillis int64
|
||||
}
|
||||
|
||||
func (a MediaArtifact) Valid() bool {
|
||||
return a.ID != "" && a.ContentType != "" && a.SizeBytes > 0 && a.ContentDigest != "" && a.SourceRef != "" && a.RecordedAtUnixMillis > 0
|
||||
}
|
||||
|
||||
type AgentArtifactAttestation struct {
|
||||
ArtifactID ArtifactID
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package capability
|
||||
|
||||
type IntelligencePath string
|
||||
|
||||
const (
|
||||
PathRule IntelligencePath = "RULE"
|
||||
PathReflex IntelligencePath = "REFLEX"
|
||||
PathSkill IntelligencePath = "SKILL"
|
||||
PathSmallLocal IntelligencePath = "SMALL_LOCAL"
|
||||
PathStrongLocal IntelligencePath = "STRONG_LOCAL"
|
||||
PathExternal IntelligencePath = "EXTERNAL"
|
||||
PathResearch IntelligencePath = "RESEARCH"
|
||||
PathTeam IntelligencePath = "TEAM"
|
||||
)
|
||||
|
||||
type StrategyRequest struct {
|
||||
RequiredQuality int64
|
||||
PreferLowLatency bool
|
||||
CostBudgetMicros int64
|
||||
RiskClass string
|
||||
FreshnessRequired bool
|
||||
CostOfError int64
|
||||
}
|
||||
type StrategyDecision struct {
|
||||
Path IntelligencePath
|
||||
Reason string
|
||||
Escalate bool
|
||||
}
|
||||
|
||||
func (r StrategyRequest) Select() StrategyDecision {
|
||||
if r.RiskClass == "HIGH" || r.CostOfError >= 800 {
|
||||
return StrategyDecision{Path: PathStrongLocal, Reason: "high consequence requires deeper reasoning", Escalate: true}
|
||||
}
|
||||
if r.RequiredQuality <= 500 && r.CostBudgetMicros > 0 {
|
||||
return StrategyDecision{Path: PathRule, Reason: "bounded low-risk path"}
|
||||
}
|
||||
if r.PreferLowLatency {
|
||||
return StrategyDecision{Path: PathReflex, Reason: "latency preference with later escalation"}
|
||||
}
|
||||
return StrategyDecision{Path: PathSkill, Reason: "validated reusable strategy"}
|
||||
}
|
||||
@@ -15,11 +15,15 @@ const (
|
||||
)
|
||||
|
||||
type MultimodalResponse struct {
|
||||
Operation MultimodalOperation `json:"operation"`
|
||||
Text string `json:"text"`
|
||||
Provider string `json:"provider,omitempty"`
|
||||
ObservedAt int64 `json:"observed_at_unix_millis"`
|
||||
ConfidenceNote string `json:"confidence_note,omitempty"`
|
||||
ContentType string `json:"content_type,omitempty"`
|
||||
DataBase64 string `json:"data_base64,omitempty"`
|
||||
Operation MultimodalOperation `json:"operation"`
|
||||
Text string `json:"text"`
|
||||
Provider string `json:"provider,omitempty"`
|
||||
ObservedAt int64 `json:"observed_at_unix_millis"`
|
||||
ConfidenceNote string `json:"confidence_note,omitempty"`
|
||||
ContentType string `json:"content_type,omitempty"`
|
||||
DataBase64 string `json:"data_base64,omitempty"`
|
||||
Model string `json:"model,omitempty"`
|
||||
ModelVersion string `json:"model_version,omitempty"`
|
||||
ConfidenceMillis int64 `json:"confidence_millis,omitempty"`
|
||||
EvidenceRef string `json:"evidence_ref,omitempty"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package capability
|
||||
|
||||
type ReflexID string
|
||||
type ReflexLifecycle string
|
||||
type ReflexKind string
|
||||
|
||||
const (
|
||||
ReflexCandidate ReflexLifecycle = "CANDIDATE"
|
||||
ReflexValidating ReflexLifecycle = "VALIDATING"
|
||||
ReflexActive ReflexLifecycle = "ACTIVE"
|
||||
ReflexDegraded ReflexLifecycle = "DEGRADED"
|
||||
ReflexStale ReflexLifecycle = "STALE"
|
||||
ReflexSuspended ReflexLifecycle = "SUSPENDED"
|
||||
ReflexRetired ReflexLifecycle = "RETIRED"
|
||||
ReflexDeterministicCode ReflexKind = "DETERMINISTIC_CODE"
|
||||
ReflexRule ReflexKind = "RULE"
|
||||
ReflexWorkflow ReflexKind = "WORKFLOW"
|
||||
ReflexQueryTemplate ReflexKind = "QUERY_TEMPLATE"
|
||||
ReflexSmallModelChain ReflexKind = "SMALL_MODEL_CHAIN"
|
||||
ReflexBoundedAdapter ReflexKind = "BOUNDED_ADAPTER_OPERATION"
|
||||
)
|
||||
|
||||
type ShadowPolicy struct {
|
||||
Enabled bool
|
||||
SampleRateMillis int64
|
||||
ComparatorRef string
|
||||
MaxShadowCostMicros *int64
|
||||
EligibleErrorCostClasses []string
|
||||
}
|
||||
type RevalidationPolicy struct {
|
||||
MaxAgeMillis *int64
|
||||
RevalidateOnDependencyRevisionChange bool
|
||||
RevalidateOnWorldVersionChange bool
|
||||
RevalidateAfterFailureCount *int64
|
||||
RevalidateAfterCorrectionCount *int64
|
||||
SampleAuditRateMillis *int64
|
||||
}
|
||||
type Reflex struct {
|
||||
ID ReflexID
|
||||
PrincipalID string
|
||||
SourceSkillRef SkillID
|
||||
CreatedAt int64
|
||||
}
|
||||
type ReflexRevision struct {
|
||||
ReflexID ReflexID
|
||||
Revision int64
|
||||
SourceSkillRevision int64
|
||||
Kind ReflexKind
|
||||
Applicability ApplicabilitySpec
|
||||
ImplementationRef string
|
||||
ImplementationVersion string
|
||||
ArtifactDigest string
|
||||
DependencyRefs []string
|
||||
ValidationEvidenceRefs []SkillEvidenceRef
|
||||
KnownFailureExperienceRefs []SkillEvidenceRef
|
||||
Shadow ShadowPolicy
|
||||
Revalidation RevalidationPolicy
|
||||
LastValidatedAt int64
|
||||
Lifecycle ReflexLifecycle
|
||||
RecordedAt int64
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package capability
|
||||
|
||||
type SkillID string
|
||||
type SkillRevision struct {
|
||||
SkillID SkillID
|
||||
Revision int64
|
||||
Description string
|
||||
Applicability ApplicabilitySpec
|
||||
ImplementationRef string
|
||||
ImplementationVersion string
|
||||
DependencyRefs []string
|
||||
SupportingExperienceRefs []SkillEvidenceRef
|
||||
KnownFailureExperienceRefs []SkillEvidenceRef
|
||||
ValidationEvidenceRefs []SkillEvidenceRef
|
||||
QualityAssessmentRefs []string
|
||||
EnvironmentVersionConstraints []string
|
||||
LastValidatedAt int64
|
||||
Lifecycle SkillLifecycleStatus
|
||||
SupersedesRevision int64
|
||||
RecordedAt int64
|
||||
}
|
||||
type ApplicabilitySpec struct {
|
||||
RequiredContextBindings []string
|
||||
PreferredContextBindings []string
|
||||
ExcludedContextBindings []string
|
||||
RequiredWorldKinds []string
|
||||
VersionConstraints []string
|
||||
InputConditions []string
|
||||
MinimumEvidenceFreshness string
|
||||
}
|
||||
type Skill struct {
|
||||
ID SkillID
|
||||
PrincipalID string
|
||||
Name string
|
||||
CreatedAt int64
|
||||
}
|
||||
@@ -11,14 +11,24 @@ import (
|
||||
var ErrInvalidContextBudget = errors.New("context budget must be positive")
|
||||
|
||||
type ContextCandidate struct {
|
||||
Excerpt ContextExcerpt
|
||||
Relevance int
|
||||
Excerpt ContextExcerpt
|
||||
Relevance int
|
||||
Lane string
|
||||
ContextScopeRef string
|
||||
FreshnessState string
|
||||
Negative bool
|
||||
}
|
||||
|
||||
type ContextCompileInput struct {
|
||||
Candidates []ContextCandidate
|
||||
DataPolicy capability.DataPolicy
|
||||
MaxBytes int
|
||||
Candidates []ContextCandidate
|
||||
DataPolicy capability.DataPolicy
|
||||
MaxBytes int
|
||||
TaskRef string
|
||||
FocusFrameRef string
|
||||
ContextScopeRef string
|
||||
FreshnessRequirement string
|
||||
RiskClass string
|
||||
TokenBudget int
|
||||
}
|
||||
|
||||
type ContextOmission struct {
|
||||
@@ -30,6 +40,24 @@ type ContextBundle struct {
|
||||
Excerpts []ContextExcerpt
|
||||
Omissions []ContextOmission
|
||||
Bytes int
|
||||
Trace ContextAssemblyTrace
|
||||
}
|
||||
|
||||
type ContextAssemblyTrace struct {
|
||||
Included []ContextExcerptID
|
||||
Excluded []ContextOmission
|
||||
Coverage string
|
||||
CompilerVersion string
|
||||
}
|
||||
|
||||
type IterativeRetrievalRequest struct {
|
||||
Query string
|
||||
RemainingBytes int
|
||||
Pass int
|
||||
}
|
||||
type IterativeRetrievalResponse struct {
|
||||
Candidates []ContextCandidate
|
||||
Exhausted bool
|
||||
}
|
||||
|
||||
// CompileContext deterministically selects permitted, relevant excerpts for a
|
||||
@@ -54,6 +82,10 @@ func CompileContext(input ContextCompileInput) (ContextBundle, error) {
|
||||
continue
|
||||
}
|
||||
seen[excerpt.ID] = struct{}{}
|
||||
if input.ContextScopeRef != "" && candidate.ContextScopeRef != input.ContextScopeRef {
|
||||
bundle.Omissions = append(bundle.Omissions, ContextOmission{ID: excerpt.ID, Reason: "context_scope"})
|
||||
continue
|
||||
}
|
||||
reason := permittedContextReason(excerpt, input.DataPolicy)
|
||||
if reason != "" {
|
||||
bundle.Omissions = append(bundle.Omissions, ContextOmission{ID: excerpt.ID, Reason: reason})
|
||||
@@ -70,7 +102,11 @@ func CompileContext(input ContextCompileInput) (ContextBundle, error) {
|
||||
}
|
||||
bundle.Excerpts = append(bundle.Excerpts, excerpt)
|
||||
bundle.Bytes += bytes
|
||||
bundle.Trace.Included = append(bundle.Trace.Included, excerpt.ID)
|
||||
}
|
||||
bundle.Trace.Excluded = append([]ContextOmission(nil), bundle.Omissions...)
|
||||
bundle.Trace.CompilerVersion = "context-compiler-v2-additive"
|
||||
bundle.Trace.Coverage = "BOUNDED"
|
||||
return bundle, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package cognitive_test
|
||||
|
||||
import (
|
||||
"git.cin.su/m/Assistant/contracts/capability"
|
||||
"git.cin.su/m/Assistant/contracts/cognitive"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestContextCompilerV2TraceAndBudgetRemainDeterministic(t *testing.T) {
|
||||
b, err := cognitive.CompileContext(cognitive.ContextCompileInput{MaxBytes: 5, DataPolicy: capability.DataPolicyLocalOnly, TaskRef: "task", FocusFrameRef: "frame", ContextScopeRef: "company-a", Candidates: []cognitive.ContextCandidate{{Excerpt: cognitive.ContextExcerpt{ID: "a", Text: "123", Sensitivity: cognitive.SensitivityPrivateAllowed}, ContextScopeRef: "company-a", Relevance: 10}, {Excerpt: cognitive.ContextExcerpt{ID: "b", Text: "123456", Sensitivity: cognitive.SensitivityPrivateAllowed}, ContextScopeRef: "company-b", Relevance: 100}}})
|
||||
if err != nil || len(b.Excerpts) != 1 || len(b.Trace.Excluded) != 1 {
|
||||
t.Fatalf("bundle=%#v err=%v", b, err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package commit
|
||||
|
||||
type ExecutorResult string
|
||||
type AmbiguityState string
|
||||
|
||||
const (
|
||||
ExecutorAccepted ExecutorResult = "ACCEPTED"
|
||||
ExecutorReportedSuccess ExecutorResult = "REPORTED_SUCCESS"
|
||||
ExecutorReportedFailure ExecutorResult = "REPORTED_FAILURE"
|
||||
ExecutorNoResponse ExecutorResult = "NO_RESPONSE"
|
||||
ExecutorAmbiguous ExecutorResult = "AMBIGUOUS"
|
||||
)
|
||||
|
||||
type ExecutionReceipt struct {
|
||||
ID string
|
||||
ActionAttemptRef ActionAttemptID
|
||||
ExecutorImplementationRef string
|
||||
ExternalOperationRef string
|
||||
ExternalRequestID string
|
||||
RequestDigest string
|
||||
ResponseDigest string
|
||||
DispatchAtUnixMillis int64
|
||||
CompletedAtUnixMillis *int64
|
||||
ExecutorResult ExecutorResult
|
||||
Ambiguity AmbiguityState
|
||||
ReconciliationKey string
|
||||
RawArtifactRef string
|
||||
}
|
||||
|
||||
func (r ExecutionReceipt) Valid() bool {
|
||||
return r.ID != "" && r.ActionAttemptRef != "" && r.ExecutorImplementationRef != "" && r.RequestDigest != "" && r.ExecutorResult != "" && r.ReconciliationKey != "" && r.DispatchAtUnixMillis > 0
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# Configuration contract boundary
|
||||
|
||||
Configuration owns typed and versioned `SettingDefinition` and
|
||||
`SettingAssignment` records plus scoped resolution and change provenance.
|
||||
Effective configuration is a derived result. Secret values never enter a
|
||||
setting assignment; only a `SecretRef`/`CredentialRef` crosses the secrets
|
||||
boundary into an authorized adapter or runtime.
|
||||
@@ -0,0 +1,7 @@
|
||||
package configuration
|
||||
|
||||
import "git.cin.su/m/Assistant/platform/ids"
|
||||
|
||||
// SemanticReference is a typed cross-owner reference; settings remain
|
||||
// Configuration-owned records.
|
||||
type SemanticReference = ids.SemanticRef
|
||||
@@ -0,0 +1,101 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.cin.su/m/Assistant/contracts/secrets"
|
||||
"git.cin.su/m/Assistant/platform/ids"
|
||||
)
|
||||
|
||||
type SettingDefinitionID ids.StableID
|
||||
type SettingAssignmentID ids.StableID
|
||||
type ConfigurationRevision ids.Revision
|
||||
type ValueType string
|
||||
type ResolutionMode string
|
||||
type SettingLifecycle string
|
||||
type SettingSource string
|
||||
|
||||
const (
|
||||
ValueString ValueType = "STRING"
|
||||
ValueInteger ValueType = "INTEGER"
|
||||
ValueBoolean ValueType = "BOOLEAN"
|
||||
ValueSecretRef ValueType = "SECRET_REF"
|
||||
ResolutionMostSpecific ResolutionMode = "MOST_SPECIFIC_WINS"
|
||||
ResolutionFirstHuman ResolutionMode = "FIRST_EXPLICIT_HUMAN_WINS"
|
||||
ResolutionMergeList ResolutionMode = "MERGE_LIST"
|
||||
SettingActive SettingLifecycle = "ACTIVE"
|
||||
SettingDeprecated SettingLifecycle = "DEPRECATED"
|
||||
SettingRetired SettingLifecycle = "RETIRED"
|
||||
SourceHumanDirective SettingSource = "HUMAN_DIRECTIVE"
|
||||
SourceMigrated SettingSource = "MIGRATED_CONFIG"
|
||||
SourceSkillDefault SettingSource = "SKILL_DEFAULT"
|
||||
SourceSystemDefault SettingSource = "SYSTEM_DEFAULT"
|
||||
SourceGovernedChange SettingSource = "GOVERNED_CHANGE"
|
||||
)
|
||||
|
||||
type SettingDefinition struct {
|
||||
ID SettingDefinitionID
|
||||
OwnerNamespace string
|
||||
StableKey string
|
||||
Revision ConfigurationRevision
|
||||
ValueType ValueType
|
||||
DefaultValue string
|
||||
Sensitivity string
|
||||
Lifecycle SettingLifecycle
|
||||
RecordedAtUnixMillis int64
|
||||
}
|
||||
type ScopeSelector struct {
|
||||
PrincipalRef string
|
||||
RequiredContextBindings []string
|
||||
ActivityRef string
|
||||
SkillRef string
|
||||
TaskRef string
|
||||
SessionRef string
|
||||
TemporaryOverride bool
|
||||
}
|
||||
type SettingAssignment struct {
|
||||
ID SettingAssignmentID
|
||||
PrincipalID string
|
||||
DefinitionRef string
|
||||
DefinitionRevision ConfigurationRevision
|
||||
Revision ConfigurationRevision
|
||||
Selector ScopeSelector
|
||||
TypedValue string
|
||||
SecretRef *secrets.CredentialRef
|
||||
EffectiveFromUnixMillis int64
|
||||
EffectiveUntilUnixMillis *int64
|
||||
Source SettingSource
|
||||
SourceRef string
|
||||
Lifecycle SettingLifecycle
|
||||
RecordedAtUnixMillis int64
|
||||
}
|
||||
type EffectiveSetting struct {
|
||||
DefinitionRef string
|
||||
Value string
|
||||
WinningAssignmentRef string
|
||||
ResolutionTrace []string
|
||||
}
|
||||
type BootstrapConfig struct {
|
||||
NodeIdentity string
|
||||
ControlDatabaseLocator string
|
||||
RootSecretResolverLocator string
|
||||
RecoveryLocator string
|
||||
}
|
||||
|
||||
// ConversationalConfigurationCommand is an intent, not an assignment. Only
|
||||
// Configuration may turn a SET command into durable state.
|
||||
|
||||
func (d SettingDefinition) Validate() error {
|
||||
if d.ID == "" || d.OwnerNamespace == "" || d.StableKey == "" || !ids.Revision(d.Revision).Valid() || d.ValueType == "" || d.Lifecycle == "" || d.RecordedAtUnixMillis < 1 {
|
||||
return fmt.Errorf("invalid setting definition")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (a SettingAssignment) Validate() error {
|
||||
if a.ID == "" || a.PrincipalID == "" || a.DefinitionRef == "" || !ids.Revision(a.Revision).Valid() || a.Source == "" || a.Lifecycle == "" || a.RecordedAtUnixMillis < 1 {
|
||||
return fmt.Errorf("invalid setting assignment")
|
||||
}
|
||||
if a.SecretRef != nil && a.TypedValue != "" {
|
||||
return fmt.Errorf("secret assignment must not contain typed value")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package epistemics
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.cin.su/m/Assistant/contracts/world"
|
||||
)
|
||||
|
||||
type VolatilityMode string
|
||||
|
||||
const (
|
||||
VolatilityImmutable VolatilityMode = "IMMUTABLE"
|
||||
VolatilityVersionBound VolatilityMode = "VERSION_BOUND"
|
||||
VolatilityVerySlow VolatilityMode = "VERY_SLOW"
|
||||
VolatilitySlow VolatilityMode = "SLOW"
|
||||
VolatilityMedium VolatilityMode = "MEDIUM"
|
||||
VolatilityFast VolatilityMode = "FAST"
|
||||
VolatilityEphemeral VolatilityMode = "EPHEMERAL"
|
||||
VolatilityCustom VolatilityMode = "CUSTOM"
|
||||
)
|
||||
|
||||
type TemporalApplicability struct {
|
||||
ObservedAtUnixMillis int64
|
||||
ValidFromUnixMillis *int64
|
||||
ValidUntilUnixMillis *int64
|
||||
LastVerifiedAtUnixMillis *int64
|
||||
Volatility VolatilitySpec
|
||||
VersionBindings []world.VersionBinding
|
||||
}
|
||||
type VolatilitySpec struct {
|
||||
Mode VolatilityMode
|
||||
ExpectedChangeIntervalMillis *int64
|
||||
RevalidateAfterMillis *int64
|
||||
HardStaleAfterMillis *int64
|
||||
InvalidateOnVersionChange bool
|
||||
RevalidationPolicyRef string
|
||||
}
|
||||
type FreshnessAssessmentState string
|
||||
|
||||
const (
|
||||
FreshnessAssessmentCurrent FreshnessAssessmentState = "CURRENT"
|
||||
FreshnessAssessmentAcceptable FreshnessAssessmentState = "ACCEPTABLE"
|
||||
FreshnessAssessmentAging FreshnessAssessmentState = "AGING"
|
||||
FreshnessAssessmentStale FreshnessAssessmentState = "STALE"
|
||||
FreshnessAssessmentVersionMismatch FreshnessAssessmentState = "VERSION_MISMATCH"
|
||||
FreshnessAssessmentHistoricalOnly FreshnessAssessmentState = "HISTORICAL_ONLY"
|
||||
FreshnessAssessmentUnknown FreshnessAssessmentState = "UNKNOWN"
|
||||
)
|
||||
|
||||
type FreshnessAssessment struct {
|
||||
SourceKnowledgeRef string
|
||||
TaskRef string
|
||||
State FreshnessAssessmentState
|
||||
Reason string
|
||||
AgeMillis int64
|
||||
VersionMatchState string
|
||||
RevalidationAttemptRef string
|
||||
AssessedAtUnixMillis int64
|
||||
}
|
||||
type RevalidationRequest struct {
|
||||
ID string
|
||||
SourceKnowledgeRef string
|
||||
TaskRef string
|
||||
RequiredVersionRefs []world.SemanticReference
|
||||
RequestedAtUnixMillis int64
|
||||
}
|
||||
type RevalidationResponse struct {
|
||||
RequestID string
|
||||
Assessment FreshnessAssessment
|
||||
NewEvidenceRefs []EvidenceRef
|
||||
CompletedAtUnixMillis int64
|
||||
}
|
||||
|
||||
func (v VolatilitySpec) Validate() error {
|
||||
if v.Mode == "" {
|
||||
return fmt.Errorf("volatility mode is required")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (t TemporalApplicability) Validate() error {
|
||||
if t.ObservedAtUnixMillis < 1 {
|
||||
return fmt.Errorf("observed time is required")
|
||||
}
|
||||
return t.Volatility.Validate()
|
||||
}
|
||||
func (a FreshnessAssessment) Validate() error {
|
||||
if a.SourceKnowledgeRef == "" || a.TaskRef == "" || a.State == "" || a.AgeMillis < 0 || a.AssessedAtUnixMillis < 1 {
|
||||
return fmt.Errorf("freshness assessment is incomplete")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package epistemics
|
||||
|
||||
type WorldOutcomeState string
|
||||
|
||||
const (
|
||||
OutcomeVerifiedSuccess WorldOutcomeState = "VERIFIED_SUCCESS"
|
||||
OutcomeVerifiedPartial WorldOutcomeState = "VERIFIED_PARTIAL"
|
||||
OutcomeVerifiedFailure WorldOutcomeState = "VERIFIED_FAILURE"
|
||||
OutcomeUnverified WorldOutcomeState = "UNVERIFIED"
|
||||
OutcomeUnknownState WorldOutcomeState = "UNKNOWN"
|
||||
OutcomeConflicted WorldOutcomeState = "CONFLICTED"
|
||||
)
|
||||
|
||||
type WorldOutcomeAssessment struct {
|
||||
ID string
|
||||
PrincipalID string
|
||||
ActionAttemptRef string
|
||||
ExecutionReceiptRefs []string
|
||||
ExpectedEffectRef string
|
||||
ObservationRefs []ObservationID
|
||||
ClaimRefs []ClaimID
|
||||
State WorldOutcomeState
|
||||
ConfidenceMillis int64
|
||||
EvidenceRefs []EvidenceRef
|
||||
AssessedAt int64
|
||||
}
|
||||
|
||||
func (a WorldOutcomeAssessment) Valid() bool {
|
||||
return a.ID != "" && a.PrincipalID != "" && a.ActionAttemptRef != "" && a.State != "" && a.ConfidenceMillis >= 0 && a.ConfidenceMillis <= 1000 && a.AssessedAt > 0
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package epistemics
|
||||
|
||||
import "git.cin.su/m/Assistant/platform/ids"
|
||||
|
||||
// SemanticReference identifies evidence-related records without transferring
|
||||
// canonical ownership to Epistemics.
|
||||
type SemanticReference = ids.SemanticRef
|
||||
@@ -17,23 +17,27 @@ type CoverageState string
|
||||
type ConflictState string
|
||||
type EvidenceKind string
|
||||
type ResearchFindingState string
|
||||
type ReliabilityState 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"
|
||||
FindingSupported ResearchFindingState = "SUPPORTED"
|
||||
FindingStale ResearchFindingState = "STALE"
|
||||
FindingConflicted ResearchFindingState = "CONFLICTED"
|
||||
FindingUnknown ResearchFindingState = "UNKNOWN"
|
||||
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"
|
||||
FindingSupported ResearchFindingState = "SUPPORTED"
|
||||
FindingStale ResearchFindingState = "STALE"
|
||||
FindingConflicted ResearchFindingState = "CONFLICTED"
|
||||
FindingUnknown ResearchFindingState = "UNKNOWN"
|
||||
ReliabilityCurrent ReliabilityState = "CURRENT"
|
||||
ReliabilityAging ReliabilityState = "AGING"
|
||||
ReliabilityUnknown ReliabilityState = "UNKNOWN"
|
||||
)
|
||||
|
||||
type ResearchPlan struct {
|
||||
@@ -52,6 +56,21 @@ type Source struct {
|
||||
Publisher string
|
||||
}
|
||||
|
||||
type SourceReliabilityAssessment struct {
|
||||
SourceRef string
|
||||
DomainRef string
|
||||
Freshness FreshnessState
|
||||
HistoryScoreMillis int64
|
||||
ReliabilityScoreMillis int64
|
||||
State ReliabilityState
|
||||
EvidenceRefs []string
|
||||
AssessedAtUnixMillis int64
|
||||
}
|
||||
|
||||
func (a SourceReliabilityAssessment) Valid() bool {
|
||||
return a.SourceRef != "" && a.DomainRef != "" && a.Freshness != "" && a.HistoryScoreMillis >= 0 && a.HistoryScoreMillis <= 1000 && a.ReliabilityScoreMillis >= 0 && a.ReliabilityScoreMillis <= 1000 && a.State != "" && a.AssessedAtUnixMillis > 0
|
||||
}
|
||||
|
||||
type SourceSnapshot struct {
|
||||
ID SourceSnapshotID
|
||||
SourceID SourceID
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package epistemics
|
||||
|
||||
import "git.cin.su/m/Assistant/platform/ids"
|
||||
|
||||
type BeliefState string
|
||||
|
||||
const (
|
||||
@@ -9,19 +11,27 @@ const (
|
||||
)
|
||||
|
||||
type Observation struct {
|
||||
ID ObservationID
|
||||
SourceSnapshot SourceSnapshotID
|
||||
Excerpt SourceExcerptID
|
||||
Text string
|
||||
ObservedAt int64
|
||||
ID ObservationID
|
||||
SourceSnapshot SourceSnapshotID
|
||||
Excerpt SourceExcerptID
|
||||
Text string
|
||||
ObservedAt int64
|
||||
PrincipalID string
|
||||
ContextScopeRef string
|
||||
ObservationKind string
|
||||
Payload string
|
||||
ExtractorRef string
|
||||
}
|
||||
|
||||
type Claim struct {
|
||||
ID ClaimID
|
||||
Statement string
|
||||
ObservationID ObservationID
|
||||
CitationID CitationID
|
||||
Verification VerificationState
|
||||
ID ClaimID
|
||||
Statement string
|
||||
ObservationID ObservationID
|
||||
CitationID CitationID
|
||||
Verification VerificationState
|
||||
PropositionID ids.ID
|
||||
RevisedFrom *ClaimID
|
||||
RecordedAtUnixMillis int64
|
||||
}
|
||||
|
||||
type Belief struct {
|
||||
|
||||
@@ -24,6 +24,8 @@ type HelpMethodKind string
|
||||
type OutcomeClass string
|
||||
type ReflectionTriggerKind string
|
||||
type ReflectionCandidateState string
|
||||
type PropositionKind string
|
||||
type BeliefAssessmentState string
|
||||
|
||||
const (
|
||||
VerificationUnverified VerificationState = "UNVERIFIED"
|
||||
@@ -73,6 +75,12 @@ const (
|
||||
ReflectionCandidateEvaluating ReflectionCandidateState = "EVALUATING"
|
||||
ReflectionCandidateSuperseded ReflectionCandidateState = "SUPERSEDED"
|
||||
ReflectionCandidateResolved ReflectionCandidateState = "RESOLVED"
|
||||
PropositionWorldFacet PropositionKind = "WORLD_FACET"
|
||||
PropositionWorldRelation PropositionKind = "WORLD_RELATION"
|
||||
PropositionOwnerDefined PropositionKind = "OWNER_DEFINED"
|
||||
PropositionTextual PropositionKind = "TEXTUAL"
|
||||
BeliefAssessmentCurrent BeliefAssessmentState = "CURRENT"
|
||||
BeliefAssessmentSuperseded BeliefAssessmentState = "SUPERSEDED"
|
||||
)
|
||||
|
||||
// SystemExpectation records what Domovoy expected a meaningful help path to
|
||||
@@ -114,6 +122,15 @@ type ExperienceRecord struct {
|
||||
VerificationEvidence []EvidenceRef
|
||||
ConfidenceMillis int64
|
||||
RecordedAtUnixMillis int64
|
||||
ProblemStatement string
|
||||
AttemptRef string
|
||||
WhyAttempted string
|
||||
ApplicabilityConditions []string
|
||||
ContextScopeRef string
|
||||
EpisodeRef string
|
||||
GoalRef string
|
||||
TaskRef string
|
||||
WhySucceededOrFailed string
|
||||
}
|
||||
|
||||
// ReflectionCandidate records that broader reflection may be warranted.
|
||||
@@ -134,3 +151,34 @@ type ReflectionCandidate struct {
|
||||
ClaimedBy string
|
||||
ClaimExpiresAtUnixMillis int64
|
||||
}
|
||||
|
||||
type Proposition struct {
|
||||
ID ids.ID
|
||||
Kind PropositionKind
|
||||
SubjectRef string
|
||||
PredicateRef string
|
||||
ObjectValue string
|
||||
ObjectRef string
|
||||
Qualifiers []string
|
||||
TextFallback string
|
||||
}
|
||||
|
||||
type BeliefAssessment struct {
|
||||
ID ids.ID
|
||||
ClaimID ClaimID
|
||||
Revision ids.Revision
|
||||
State BeliefAssessmentState
|
||||
ConfidenceMillis int64
|
||||
EvidenceRefs []EvidenceRef
|
||||
AssessedAtUnixMillis int64
|
||||
}
|
||||
|
||||
type EpisodeID ids.ID
|
||||
type Episode struct {
|
||||
ID EpisodeID
|
||||
PrincipalID string
|
||||
ExperienceRefs []ExperienceRecordID
|
||||
Summary string
|
||||
Outcome OutcomeClass
|
||||
RecordedAtUnixMillis int64
|
||||
}
|
||||
|
||||
@@ -6,6 +6,30 @@ type ActorID ids.ID
|
||||
type PrincipalID ids.ID
|
||||
type IdentityID ids.ID
|
||||
|
||||
type DataRealmKind string
|
||||
|
||||
const (
|
||||
DataRealmPersonal DataRealmKind = "PERSONAL"
|
||||
DataRealmShared DataRealmKind = "SHARED"
|
||||
DataRealmPublic DataRealmKind = "PUBLIC"
|
||||
)
|
||||
|
||||
// PrincipalDataRealm is an identity-owned access boundary. It does not make
|
||||
// a speaker or World person referent an authenticated principal.
|
||||
type PrincipalDataRealm struct {
|
||||
ID string
|
||||
OwnerPrincipalID PrincipalID
|
||||
Kind DataRealmKind
|
||||
MemberPrincipalIDs []PrincipalID
|
||||
Shared bool
|
||||
Revision int64
|
||||
RecordedAtUnixMillis int64
|
||||
}
|
||||
|
||||
func (r PrincipalDataRealm) Valid() bool {
|
||||
return r.ID != "" && r.OwnerPrincipalID != "" && (r.Kind == DataRealmPersonal || r.Kind == DataRealmShared || r.Kind == DataRealmPublic) && r.Revision > 0 && r.RecordedAtUnixMillis > 0 && (r.Kind != DataRealmShared || r.Shared)
|
||||
}
|
||||
|
||||
// PrincipalBinding keeps the operational actor separate from the interest on
|
||||
// whose behalf it acts. A Creator is not exempt from this distinction.
|
||||
type PrincipalBinding struct {
|
||||
|
||||
+100
-45
@@ -25,53 +25,96 @@ type SemanticApplicability string
|
||||
type SemanticCandidateSource string
|
||||
type SemanticCandidateSelectionState string
|
||||
type InformationRequestKind string
|
||||
type ConfigurationCommandKind string
|
||||
type SpeakerAttribution struct {
|
||||
SourceRef string
|
||||
PrincipalCandidate string
|
||||
ConfidenceMillis int64
|
||||
Authenticated bool
|
||||
ObservationRef string
|
||||
}
|
||||
type IngressModality string
|
||||
|
||||
const (
|
||||
ChannelWebChat Channel = "WEB_CHAT"
|
||||
ConversationOpen ConversationState = "OPEN"
|
||||
ConversationArchived ConversationState = "ARCHIVED"
|
||||
SpeechActDirective SpeechAct = "DIRECTIVE"
|
||||
SpeechActAssertion SpeechAct = "ASSERTION"
|
||||
SpeechActDecision SpeechAct = "DECISION"
|
||||
SpeechActPreference SpeechAct = "PREFERENCE"
|
||||
SpeechActQuestion SpeechAct = "QUESTION"
|
||||
SpeechActHypothesis SpeechAct = "HYPOTHESIS"
|
||||
SpeechActCorrection SpeechAct = "CORRECTION"
|
||||
SpeechActRequestToInvestigate SpeechAct = "REQUEST_TO_INVESTIGATE"
|
||||
SpeechActConfirmation SpeechAct = "CONFIRMATION"
|
||||
SpeechActRejection SpeechAct = "REJECTION"
|
||||
SpeechActCancellation SpeechAct = "CANCELLATION"
|
||||
SpeechActConversational SpeechAct = "CONVERSATIONAL"
|
||||
SpeechActUnknown SpeechAct = "UNKNOWN"
|
||||
InterpretationRecorded InterpretationState = "RECORDED"
|
||||
InterpretationInterpreted InterpretationState = "INTERPRETED"
|
||||
RelationCorrects InteractionRelationKind = "CORRECTS"
|
||||
RelationRespondsTo InteractionRelationKind = "RESPONDS_TO"
|
||||
RelationRequestsCognition InteractionRelationKind = "REQUESTS_COGNITION"
|
||||
RelationCognitionFallback InteractionRelationKind = "COGNITION_FALLBACK"
|
||||
RelationSupersedes InteractionRelationKind = "SUPERSEDES"
|
||||
RelationClarifies InteractionRelationKind = "CLARIFIES"
|
||||
ProjectRolePrimary ProjectReferenceRole = "PRIMARY"
|
||||
ProjectRoleRelated ProjectReferenceRole = "RELATED"
|
||||
ProjectRoleReferenceSource ProjectReferenceRole = "REFERENCE_SOURCE"
|
||||
ClarificationNotRequired ClarificationState = "NOT_REQUIRED"
|
||||
ClarificationRequired ClarificationState = "REQUIRED"
|
||||
SemanticApplicable SemanticApplicability = "APPLICABLE"
|
||||
SemanticLowConfidence SemanticApplicability = "LOW_CONFIDENCE"
|
||||
SemanticMixedIntent SemanticApplicability = "MIXED_INTENT"
|
||||
SemanticNeedsBroaderInterpret SemanticApplicability = "NEEDS_BROADER_INTERPRETATION"
|
||||
SemanticCandidateClarify SemanticApplicability = "CLARIFICATION_REQUIRED"
|
||||
CandidateSourceDeterministic SemanticCandidateSource = "DETERMINISTIC_V2"
|
||||
CandidateSourceCognitive SemanticCandidateSource = "COGNITIVE_V1"
|
||||
CandidateSelected SemanticCandidateSelectionState = "SELECTED"
|
||||
CandidateObserved SemanticCandidateSelectionState = "OBSERVED"
|
||||
CandidateRejected SemanticCandidateSelectionState = "REJECTED"
|
||||
InformationRequestCapabilityInventory InformationRequestKind = "CAPABILITY_INVENTORY"
|
||||
InformationRequestCognitiveModelInventory InformationRequestKind = "COGNITIVE_MODEL_INVENTORY"
|
||||
InformationRequestCognitiveChannelStatus InformationRequestKind = "COGNITIVE_CHANNEL_STATUS"
|
||||
InformationRequestCognitiveRecovery InformationRequestKind = "COGNITIVE_RECOVERY"
|
||||
InformationRequestMemoryInventory InformationRequestKind = "MEMORY_INVENTORY"
|
||||
InformationRequestTimeDifference InformationRequestKind = "TIME_DIFFERENCE"
|
||||
IngressText IngressModality = "TEXT"
|
||||
IngressAudio IngressModality = "AUDIO"
|
||||
IngressImage IngressModality = "IMAGE"
|
||||
IngressVideo IngressModality = "VIDEO"
|
||||
IngressDevice IngressModality = "DEVICE"
|
||||
)
|
||||
|
||||
type IngressEnvelope struct {
|
||||
ID string
|
||||
Modality IngressModality
|
||||
SourceRef string
|
||||
DeviceRef string
|
||||
LocationRef string
|
||||
PrincipalID string
|
||||
ArtifactRef string
|
||||
ObservationRef string
|
||||
ProviderRef string
|
||||
ModelRef string
|
||||
ModelVersion string
|
||||
ConfidenceMillis int64
|
||||
Degraded bool
|
||||
RecordedAtUnixMillis int64
|
||||
}
|
||||
|
||||
func (e IngressEnvelope) Valid() bool {
|
||||
return e.ID != "" && e.Modality != "" && e.SourceRef != "" && e.ConfidenceMillis >= 0 && e.ConfidenceMillis <= 1000 && e.RecordedAtUnixMillis > 0 && (!e.Degraded || e.ObservationRef == "")
|
||||
}
|
||||
|
||||
const (
|
||||
ChannelWebChat Channel = "WEB_CHAT"
|
||||
ConversationOpen ConversationState = "OPEN"
|
||||
ConversationArchived ConversationState = "ARCHIVED"
|
||||
SpeechActDirective SpeechAct = "DIRECTIVE"
|
||||
SpeechActAssertion SpeechAct = "ASSERTION"
|
||||
SpeechActDecision SpeechAct = "DECISION"
|
||||
SpeechActPreference SpeechAct = "PREFERENCE"
|
||||
SpeechActQuestion SpeechAct = "QUESTION"
|
||||
SpeechActHypothesis SpeechAct = "HYPOTHESIS"
|
||||
SpeechActCorrection SpeechAct = "CORRECTION"
|
||||
SpeechActRequestToInvestigate SpeechAct = "REQUEST_TO_INVESTIGATE"
|
||||
SpeechActConfirmation SpeechAct = "CONFIRMATION"
|
||||
SpeechActRejection SpeechAct = "REJECTION"
|
||||
SpeechActCancellation SpeechAct = "CANCELLATION"
|
||||
SpeechActConversational SpeechAct = "CONVERSATIONAL"
|
||||
SpeechActUnknown SpeechAct = "UNKNOWN"
|
||||
InterpretationRecorded InterpretationState = "RECORDED"
|
||||
InterpretationInterpreted InterpretationState = "INTERPRETED"
|
||||
RelationCorrects InteractionRelationKind = "CORRECTS"
|
||||
RelationRespondsTo InteractionRelationKind = "RESPONDS_TO"
|
||||
RelationRequestsCognition InteractionRelationKind = "REQUESTS_COGNITION"
|
||||
RelationCognitionFallback InteractionRelationKind = "COGNITION_FALLBACK"
|
||||
RelationSupersedes InteractionRelationKind = "SUPERSEDES"
|
||||
RelationClarifies InteractionRelationKind = "CLARIFIES"
|
||||
ProjectRolePrimary ProjectReferenceRole = "PRIMARY"
|
||||
ProjectRoleRelated ProjectReferenceRole = "RELATED"
|
||||
ProjectRoleReferenceSource ProjectReferenceRole = "REFERENCE_SOURCE"
|
||||
ClarificationNotRequired ClarificationState = "NOT_REQUIRED"
|
||||
ClarificationRequired ClarificationState = "REQUIRED"
|
||||
SemanticApplicable SemanticApplicability = "APPLICABLE"
|
||||
SemanticLowConfidence SemanticApplicability = "LOW_CONFIDENCE"
|
||||
SemanticMixedIntent SemanticApplicability = "MIXED_INTENT"
|
||||
SemanticNeedsBroaderInterpret SemanticApplicability = "NEEDS_BROADER_INTERPRETATION"
|
||||
SemanticCandidateClarify SemanticApplicability = "CLARIFICATION_REQUIRED"
|
||||
CandidateSourceDeterministic SemanticCandidateSource = "DETERMINISTIC_V2"
|
||||
CandidateSourceCognitive SemanticCandidateSource = "COGNITIVE_V1"
|
||||
CandidateSelected SemanticCandidateSelectionState = "SELECTED"
|
||||
CandidateObserved SemanticCandidateSelectionState = "OBSERVED"
|
||||
CandidateRejected SemanticCandidateSelectionState = "REJECTED"
|
||||
InformationRequestCapabilityInventory InformationRequestKind = "CAPABILITY_INVENTORY"
|
||||
InformationRequestLocalCapabilityInventory InformationRequestKind = "LOCAL_CAPABILITY_INVENTORY"
|
||||
InformationRequestCognitiveModelInventory InformationRequestKind = "COGNITIVE_MODEL_INVENTORY"
|
||||
InformationRequestCognitiveCurrentModel InformationRequestKind = "COGNITIVE_CURRENT_MODEL"
|
||||
InformationRequestCognitiveChannelStatus InformationRequestKind = "COGNITIVE_CHANNEL_STATUS"
|
||||
InformationRequestCognitiveRecovery InformationRequestKind = "COGNITIVE_RECOVERY"
|
||||
InformationRequestMemoryInventory InformationRequestKind = "MEMORY_INVENTORY"
|
||||
InformationRequestTimeDifference InformationRequestKind = "TIME_DIFFERENCE"
|
||||
InformationRequestResearchFailureExplanation InformationRequestKind = "RESEARCH_FAILURE_EXPLANATION"
|
||||
ConfigurationCommandSet ConfigurationCommandKind = "SET"
|
||||
ConfigurationCommandQuery ConfigurationCommandKind = "QUERY"
|
||||
)
|
||||
|
||||
type Conversation struct {
|
||||
@@ -103,6 +146,18 @@ type OriginalHumanIntent struct {
|
||||
RecordedAtUnixMillis int64
|
||||
Text string
|
||||
}
|
||||
|
||||
// ConfigurationCommand is Interaction-owned intent. Configuration remains
|
||||
// the only owner allowed to materialise a SettingAssignment.
|
||||
type ConfigurationCommand struct {
|
||||
Kind ConfigurationCommandKind
|
||||
PrincipalID string
|
||||
DefinitionRef string
|
||||
TypedValue string
|
||||
TemporaryOverride bool
|
||||
SessionRef string
|
||||
SourceRef string
|
||||
}
|
||||
type InteractionRelation struct {
|
||||
ID InteractionRelationID
|
||||
FromUtteranceID HumanUtteranceID
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# SELF contract boundary
|
||||
|
||||
SELF owns evidence-backed competence, weakness, dependency, learning, and
|
||||
self-evaluation records. Raw evidence remains Epistemics-owned; capability
|
||||
health remains Capability-owned; tasks and development remain Work-owned;
|
||||
release switching remains Supervisor/runtime-owned.
|
||||
|
||||
SELF is observational/evaluative and is not a mandatory request-path
|
||||
orchestrator or an authority source.
|
||||
@@ -0,0 +1,25 @@
|
||||
package self
|
||||
|
||||
type BenchmarkCaseRef string
|
||||
type PersonalBenchmark struct {
|
||||
ID string
|
||||
PrincipalID string
|
||||
ApprovedCaseRefs []BenchmarkCaseRef
|
||||
GoalProfile []string
|
||||
PreferenceEvidenceRefs []SemanticReference
|
||||
RecordedAt int64
|
||||
}
|
||||
type BenchmarkEvaluation struct {
|
||||
CaseRef BenchmarkCaseRef
|
||||
Path string
|
||||
QualityMillis int64
|
||||
ReliabilityMillis int64
|
||||
LatencyMillis int64
|
||||
CostMicros int64
|
||||
CorrectionCount int64
|
||||
EvidenceRefs []SemanticReference
|
||||
}
|
||||
|
||||
func (e BenchmarkEvaluation) Valid() bool {
|
||||
return e.CaseRef != "" && len(e.EvidenceRefs) > 0 && e.QualityMillis >= 0 && e.ReliabilityMillis >= 0 && e.CorrectionCount >= 0
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package self
|
||||
|
||||
import "git.cin.su/m/Assistant/platform/ids"
|
||||
|
||||
// SemanticReference lets SELF cite evidence and capabilities without owning
|
||||
// their canonical state.
|
||||
type SemanticReference = ids.SemanticRef
|
||||
@@ -0,0 +1,92 @@
|
||||
package self
|
||||
|
||||
import "fmt"
|
||||
|
||||
type AssessmentID string
|
||||
type LearningLedgerEntryID string
|
||||
type AssessmentState string
|
||||
|
||||
const (
|
||||
AssessmentCandidate AssessmentState = "CANDIDATE"
|
||||
AssessmentConfirmed AssessmentState = "CONFIRMED"
|
||||
AssessmentMonitoring AssessmentState = "MONITORING"
|
||||
AssessmentResolved AssessmentState = "RESOLVED"
|
||||
)
|
||||
|
||||
type CompetenceAssessment struct {
|
||||
ID AssessmentID
|
||||
PrincipalID string
|
||||
SubjectRef SemanticReference
|
||||
Revision uint64
|
||||
ContextMatcher []string
|
||||
QualityMillis int64
|
||||
ReliabilityMillis int64
|
||||
ConfidenceMillis int64
|
||||
EvidenceRefs []SemanticReference
|
||||
LastValidatedAt int64
|
||||
AssessedAt int64
|
||||
SupersedesRef *SemanticReference
|
||||
}
|
||||
type WeaknessPattern struct {
|
||||
ID AssessmentID
|
||||
PrincipalID string
|
||||
Revision uint64
|
||||
PatternKey string
|
||||
ContextMatcher []string
|
||||
FailureMode string
|
||||
DetectionEvidenceRefs []SemanticReference
|
||||
State AssessmentState
|
||||
RecordedAt int64
|
||||
}
|
||||
type DependencyAssessment struct {
|
||||
ID AssessmentID
|
||||
SubjectRef SemanticReference
|
||||
DependencyRef SemanticReference
|
||||
ImpactClass string
|
||||
CurrentEffect string
|
||||
EvidenceRefs []SemanticReference
|
||||
AssessedAt int64
|
||||
}
|
||||
type ImprovementNeed struct {
|
||||
ID AssessmentID
|
||||
PrincipalID string
|
||||
SubjectRef SemanticReference
|
||||
ProblemSummary string
|
||||
EvidenceRefs []SemanticReference
|
||||
ExpectedBenefit string
|
||||
Priority int
|
||||
State string
|
||||
CreatedAt int64
|
||||
}
|
||||
type LearningLedgerEntry struct {
|
||||
ID LearningLedgerEntryID
|
||||
PrincipalID string
|
||||
ImprovementNeedRef *SemanticReference
|
||||
ObservedProblem string
|
||||
EvidenceRefs []SemanticReference
|
||||
Diagnosis string
|
||||
Hypothesis string
|
||||
ProposedChangeRef SemanticReference
|
||||
ExpectedImprovement string
|
||||
ActualEvaluationRefs []SemanticReference
|
||||
DevelopmentGateRef *SemanticReference
|
||||
BeforeVersionRefs []SemanticReference
|
||||
AfterVersionRefs []SemanticReference
|
||||
Decision string
|
||||
SideEffects []string
|
||||
RollbackRef *SemanticReference
|
||||
RecordedAt int64
|
||||
}
|
||||
|
||||
func (a CompetenceAssessment) Validate() error {
|
||||
if a.ID == "" || a.PrincipalID == "" || !a.SubjectRef.Valid() || a.Revision < 1 || a.ConfidenceMillis < 0 || a.ConfidenceMillis > 1000 || len(a.EvidenceRefs) == 0 || a.AssessedAt < 1 {
|
||||
return fmt.Errorf("competence requires evidence")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (l LearningLedgerEntry) Validate() error {
|
||||
if l.ID == "" || l.PrincipalID == "" || l.ObservedProblem == "" || len(l.EvidenceRefs) == 0 || l.Decision == "" || l.RecordedAt < 1 {
|
||||
return fmt.Errorf("learning ledger entry is incomplete")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package situation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.cin.su/m/Assistant/contracts/authority"
|
||||
"git.cin.su/m/Assistant/contracts/identity"
|
||||
"git.cin.su/m/Assistant/contracts/work"
|
||||
"git.cin.su/m/Assistant/platform/clock"
|
||||
"git.cin.su/m/Assistant/platform/ids"
|
||||
)
|
||||
|
||||
type ContextScopeID ids.StableID
|
||||
type FocusFrameID ids.StableID
|
||||
type FocusTransitionID ids.StableID
|
||||
type FocusRevision ids.Revision
|
||||
|
||||
type RoleKey string
|
||||
type MatchPolicy string
|
||||
type FocusFrameState string
|
||||
type FocusProvenance string
|
||||
type FocusTransitionOperation string
|
||||
|
||||
const (
|
||||
MatchRequired MatchPolicy = "REQUIRED"
|
||||
MatchPreferred MatchPolicy = "PREFERRED"
|
||||
MatchRelated MatchPolicy = "RELATED"
|
||||
MatchExcluded MatchPolicy = "EXCLUDED"
|
||||
|
||||
RolePerson RoleKey = "PERSON"
|
||||
RoleOrganization RoleKey = "ORGANIZATION"
|
||||
RoleFamily RoleKey = "FAMILY"
|
||||
RoleEnvironment RoleKey = "ENVIRONMENT"
|
||||
RoleLocation RoleKey = "LOCATION"
|
||||
RoleSystem RoleKey = "SYSTEM"
|
||||
RoleDatabase RoleKey = "DATABASE"
|
||||
RoleRepository RoleKey = "REPOSITORY"
|
||||
RoleBranch RoleKey = "BRANCH"
|
||||
RoleVersion RoleKey = "VERSION"
|
||||
RoleActivity RoleKey = "ACTIVITY"
|
||||
RoleProject RoleKey = "PROJECT"
|
||||
RoleActiveEntity RoleKey = "ACTIVE_ENTITY"
|
||||
|
||||
FocusCandidate FocusFrameState = "CANDIDATE"
|
||||
FocusActive FocusFrameState = "ACTIVE"
|
||||
FocusSuspended FocusFrameState = "SUSPENDED"
|
||||
FocusExpired FocusFrameState = "EXPIRED"
|
||||
FocusSuperseded FocusFrameState = "SUPERSEDED"
|
||||
|
||||
FocusExplicitHuman FocusProvenance = "EXPLICIT_HUMAN"
|
||||
FocusUserConfirmed FocusProvenance = "USER_CONFIRMED"
|
||||
FocusInherited FocusProvenance = "INHERITED"
|
||||
FocusEntityReference FocusProvenance = "ENTITY_REFERENCE"
|
||||
FocusTaskReference FocusProvenance = "TASK_REFERENCE"
|
||||
FocusRepositoryReference FocusProvenance = "REPOSITORY_REFERENCE"
|
||||
FocusEnvironmentObservation FocusProvenance = "ENVIRONMENT_OBSERVATION"
|
||||
FocusInferred FocusProvenance = "INFERRED"
|
||||
|
||||
TransitionActivate FocusTransitionOperation = "ACTIVATE"
|
||||
TransitionSuspend FocusTransitionOperation = "SUSPEND"
|
||||
TransitionResume FocusTransitionOperation = "RESUME"
|
||||
TransitionSupersede FocusTransitionOperation = "SUPERSEDE"
|
||||
TransitionExpire FocusTransitionOperation = "EXPIRE"
|
||||
)
|
||||
|
||||
type ContextBinding struct {
|
||||
RoleKey RoleKey
|
||||
TargetRef SemanticReference
|
||||
MatchPolicy MatchPolicy
|
||||
Provenance string
|
||||
ConfidenceMillis int64
|
||||
SourceRef *SemanticReference
|
||||
}
|
||||
|
||||
type ContextScope struct {
|
||||
ID ContextScopeID
|
||||
PrincipalID identity.PrincipalID
|
||||
Revision FocusRevision
|
||||
Bindings []ContextBinding
|
||||
RecordedAt clock.Instant
|
||||
}
|
||||
|
||||
type FocusFrame struct {
|
||||
ID FocusFrameID
|
||||
PrincipalID identity.PrincipalID
|
||||
ActorID identity.ActorID
|
||||
Revision FocusRevision
|
||||
AuthorityScopeRef authority.ScopeID
|
||||
ContextScopeRef SemanticReference
|
||||
GoalRef *work.GoalID
|
||||
TaskRef *work.TaskID
|
||||
SourceUtteranceRef string
|
||||
Provenance FocusProvenance
|
||||
ConfidenceMillis int64
|
||||
State FocusFrameState
|
||||
ParentFrameRef *FocusFrameID
|
||||
ActivatedAt clock.Instant
|
||||
SuspendedAt *clock.Instant
|
||||
ExpiredAt *clock.Instant
|
||||
RecordedAt clock.Instant
|
||||
}
|
||||
|
||||
type FocusTransition struct {
|
||||
TransitionID FocusTransitionID
|
||||
FrameID FocusFrameID
|
||||
FromRevision FocusRevision
|
||||
ToRevision FocusRevision
|
||||
Operation FocusTransitionOperation
|
||||
CauseRef string
|
||||
RecordedAt clock.Instant
|
||||
}
|
||||
|
||||
func (s ContextScope) Validate() error {
|
||||
if s.ID == "" || s.PrincipalID == "" || !ids.Revision(s.Revision).Valid() || !s.RecordedAt.Valid() {
|
||||
return fmt.Errorf("invalid context scope")
|
||||
}
|
||||
for _, b := range s.Bindings {
|
||||
if b.RoleKey == "" || !b.TargetRef.Valid() || !validMatchPolicy(b.MatchPolicy) {
|
||||
return fmt.Errorf("invalid context binding")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f FocusFrame) Validate() error {
|
||||
if f.ID == "" || f.PrincipalID == "" || f.ActorID == "" || !ids.Revision(f.Revision).Valid() || !f.ContextScopeRef.Valid() || f.State == "" || f.Provenance == "" || !f.RecordedAt.Valid() {
|
||||
return fmt.Errorf("invalid focus frame")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t FocusTransition) Validate() error {
|
||||
if t.TransitionID == "" || t.FrameID == "" || !ids.Revision(t.FromRevision).Valid() || !ids.Revision(t.ToRevision).Valid() || t.Operation == "" || !t.RecordedAt.Valid() {
|
||||
return fmt.Errorf("invalid focus transition")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validMatchPolicy(p MatchPolicy) bool {
|
||||
return p == MatchRequired || p == MatchPreferred || p == MatchRelated || p == MatchExcluded
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package situation_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.cin.su/m/Assistant/contracts/identity"
|
||||
contract "git.cin.su/m/Assistant/contracts/situation"
|
||||
"git.cin.su/m/Assistant/platform/clock"
|
||||
"git.cin.su/m/Assistant/platform/ids"
|
||||
)
|
||||
|
||||
func TestFocusV2ContractsValidateTypedScopeAndFrame(t *testing.T) {
|
||||
rev := ids.Revision(1)
|
||||
scope := contract.ContextScope{ID: "scope", PrincipalID: identity.PrincipalID("company-a"), Revision: contract.FocusRevision(rev), RecordedAt: clock.Instant(1), Bindings: []contract.ContextBinding{{RoleKey: contract.RoleOrganization, TargetRef: contract.SemanticReference{OwnerNamespace: "world", SemanticKind: "organization", StableID: "a"}, MatchPolicy: contract.MatchRequired}}}
|
||||
if err := scope.Validate(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
frame := contract.FocusFrame{ID: "frame", PrincipalID: scope.PrincipalID, ActorID: "actor", Revision: 1, ContextScopeRef: contract.SemanticReference{OwnerNamespace: "situation", SemanticKind: "context_scope", StableID: ids.StableID(scope.ID), Revision: &rev}, State: contract.FocusActive, Provenance: contract.FocusExplicitHuman, RecordedAt: 1}
|
||||
if err := frame.Validate(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package situation
|
||||
|
||||
import "git.cin.su/m/Assistant/platform/ids"
|
||||
|
||||
// SemanticReference keeps Situation references transport-neutral while
|
||||
// ContextScope semantics remain owned by Situation.
|
||||
type SemanticReference = ids.SemanticRef
|
||||
@@ -0,0 +1,24 @@
|
||||
package work
|
||||
|
||||
type ConsequenceAssessment struct {
|
||||
ID string
|
||||
TargetRefs []string
|
||||
Impact int64
|
||||
Reversibility int64
|
||||
Uncertainty int64
|
||||
Scope int64
|
||||
Recoverability int64
|
||||
CostOfError int64
|
||||
ExpectedConsequences []string
|
||||
MitigationOptions []string
|
||||
RequiresDryRun bool
|
||||
RequiresBackup bool
|
||||
RecommendedConfirmation string
|
||||
OwnerAccepted bool
|
||||
EvidenceRefs []EvidenceRef
|
||||
RecordedAtUnixMillis int64
|
||||
}
|
||||
|
||||
func (a ConsequenceAssessment) Valid() bool {
|
||||
return a.ID != "" && len(a.TargetRefs) > 0 && a.Impact >= 0 && a.Impact <= 1000 && a.Reversibility >= 0 && a.Reversibility <= 1000 && a.Uncertainty >= 0 && a.Uncertainty <= 1000 && a.Recoverability >= 0 && a.Recoverability <= 1000 && a.RecordedAtUnixMillis > 0
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package work
|
||||
|
||||
import "git.cin.su/m/Assistant/contracts/world"
|
||||
|
||||
type FreshnessRequirementMode string
|
||||
type FreshnessFallbackPolicy string
|
||||
|
||||
const (
|
||||
FreshnessAny FreshnessRequirementMode = "ANY"
|
||||
FreshnessPreferCurrent FreshnessRequirementMode = "PREFER_CURRENT"
|
||||
FreshnessRequireCurrent FreshnessRequirementMode = "REQUIRE_CURRENT"
|
||||
FreshnessRequireWithinAge FreshnessRequirementMode = "REQUIRE_WITHIN_AGE"
|
||||
FreshnessRequireVersionMatch FreshnessRequirementMode = "REQUIRE_VERSION_MATCH"
|
||||
FreshnessLiveRequired FreshnessRequirementMode = "LIVE_REQUIRED"
|
||||
FallbackAllowStaleDisclosure FreshnessFallbackPolicy = "ALLOW_STALE_WITH_DISCLOSURE"
|
||||
FallbackHypothesisOnly FreshnessFallbackPolicy = "USE_AS_HYPOTHESIS_ONLY"
|
||||
FallbackWaitForSource FreshnessFallbackPolicy = "WAIT_FOR_SOURCE"
|
||||
FallbackAskOwner FreshnessFallbackPolicy = "ASK_OWNER"
|
||||
FallbackStopUnknown FreshnessFallbackPolicy = "STOP_WITH_UNKNOWN"
|
||||
)
|
||||
|
||||
type TaskFreshnessRequirement struct {
|
||||
Mode FreshnessRequirementMode
|
||||
MaximumAgeMillis *int64
|
||||
RequiredVersionRefs []world.SemanticReference
|
||||
RevalidationRequired bool
|
||||
FallbackPolicy FreshnessFallbackPolicy
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package work
|
||||
|
||||
type EvolutionRunStage string
|
||||
|
||||
const (
|
||||
EvolutionNeedRecorded EvolutionRunStage = "IMPROVEMENT_NEED"
|
||||
EvolutionCandidateBuilt EvolutionRunStage = "CANDIDATE"
|
||||
EvolutionIndependentlyEvaluated EvolutionRunStage = "INDEPENDENT_EVALUATION"
|
||||
EvolutionSandboxed EvolutionRunStage = "SANDBOX_BUILD"
|
||||
EvolutionBenchmarked EvolutionRunStage = "BENCHMARK"
|
||||
EvolutionCanaried EvolutionRunStage = "CANARY"
|
||||
EvolutionPromoted EvolutionRunStage = "PROMOTION"
|
||||
EvolutionRolledBack EvolutionRunStage = "ROLLBACK"
|
||||
)
|
||||
|
||||
type GovernedEvolutionResult struct {
|
||||
NeedRef string
|
||||
CandidateRef string
|
||||
EvaluationRef string
|
||||
Stages []EvolutionRunStage
|
||||
EvidenceRefs []EvidenceRef
|
||||
LearningLedgerOutcomeRef string
|
||||
Promoted bool
|
||||
RollbackRef string
|
||||
}
|
||||
|
||||
// GovernedEvolutionOps are explicit gates. The caller supplies existing
|
||||
// development executor/supervisor operations; this interface grants no new
|
||||
// authority and has no autonomous promotion implementation.
|
||||
type GovernedEvolutionOps struct {
|
||||
CreateCandidate func() (EvolutionCandidate, error)
|
||||
Evaluate func(EvolutionCandidate) (EvolutionEvaluation, error)
|
||||
SandboxBuild func(EvolutionCandidate) (EvidenceRef, error)
|
||||
Benchmark func(EvolutionCandidate) (EvidenceRef, bool, error)
|
||||
Canary func(EvolutionCandidate) (EvidenceRef, bool, error)
|
||||
Promote func(EvolutionCandidate, EvolutionEvaluation) (string, error)
|
||||
Rollback func(EvolutionCandidate) (string, error)
|
||||
LearningLedgerOutcome func(GovernedEvolutionResult) (string, error)
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package work
|
||||
|
||||
type LongRunningWorkID string
|
||||
type LongRunningState string
|
||||
|
||||
const (
|
||||
LongRunningProposed LongRunningState = "PROPOSED"
|
||||
LongRunningRunning LongRunningState = "RUNNING"
|
||||
LongRunningPaused LongRunningState = "PAUSED"
|
||||
LongRunningBlocked LongRunningState = "BLOCKED"
|
||||
LongRunningCompleted LongRunningState = "COMPLETED"
|
||||
LongRunningExhausted LongRunningState = "EXHAUSTED"
|
||||
LongRunningCancelled LongRunningState = "CANCELLED"
|
||||
)
|
||||
|
||||
type Investigation struct {
|
||||
ID LongRunningWorkID
|
||||
Objective string
|
||||
HypothesisRefs []EvidenceRef
|
||||
AttemptRefs []AgentRunID
|
||||
FindingRefs []EvidenceRef
|
||||
Blockers []string
|
||||
CheckpointRefs []AgentCheckpointID
|
||||
NextStep string
|
||||
State LongRunningState
|
||||
Revision int64
|
||||
TimeBudgetMillis int64
|
||||
CostBudgetMicros int64
|
||||
MaxAttempts int
|
||||
RecordedAtUnixMillis int64
|
||||
}
|
||||
|
||||
// InvestigationCheckpoint is the durable boundary between attempts. A
|
||||
// checkpoint is written only after an attempt has been verified, so a worker
|
||||
// restart can resume at NextStep without replaying that attempt.
|
||||
type InvestigationCheckpoint struct {
|
||||
ID AgentCheckpointID
|
||||
InvestigationID LongRunningWorkID
|
||||
CompletedAttemptRefs []AgentRunID
|
||||
CompletedFindingRefs []EvidenceRef
|
||||
NextStep string
|
||||
RecordedAtUnixMillis int64
|
||||
}
|
||||
|
||||
func (c InvestigationCheckpoint) Valid() bool {
|
||||
return c.ID != "" && c.InvestigationID != "" && c.NextStep != "" && c.RecordedAtUnixMillis > 0
|
||||
}
|
||||
|
||||
func (w Investigation) Valid() bool {
|
||||
return w.ID != "" && w.Objective != "" && w.State != "" && w.Revision > 0 && w.TimeBudgetMillis >= 0 && w.CostBudgetMicros >= 0 && w.MaxAttempts >= 0 && w.RecordedAtUnixMillis > 0
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package work
|
||||
|
||||
type PerspectiveID string
|
||||
type PerspectiveSpec struct {
|
||||
ID PerspectiveID
|
||||
ObjectiveLens string
|
||||
FocusRef string
|
||||
RetrievalProfileRef string
|
||||
SkillRefs []string
|
||||
ToolRefs []string
|
||||
EvaluationCriteria []string
|
||||
CapabilityRequirements []string
|
||||
ExpectedOutputShape string
|
||||
Critic bool
|
||||
}
|
||||
type SpecialistAssignment struct {
|
||||
Perspective PerspectiveSpec
|
||||
SubtaskRef string
|
||||
FocusRef string
|
||||
SkillRefs []string
|
||||
ToolRefs []string
|
||||
EvidenceRefs []EvidenceRef
|
||||
}
|
||||
type TeamDecision struct {
|
||||
UseTeam bool
|
||||
Reason string
|
||||
Assignments []SpecialistAssignment
|
||||
}
|
||||
|
||||
func (p PerspectiveSpec) Valid() bool {
|
||||
return p.ID != "" && p.ObjectiveLens != "" && p.FocusRef != "" && len(p.SkillRefs) > 0 && len(p.ToolRefs) > 0 && len(p.EvaluationCriteria) > 0 && p.ExpectedOutputShape != ""
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package work
|
||||
|
||||
type EvolutionChangeLevel string
|
||||
|
||||
const (
|
||||
EvolutionKnowledge EvolutionChangeLevel = "KNOWLEDGE"
|
||||
EvolutionBehavior EvolutionChangeLevel = "BEHAVIOR"
|
||||
EvolutionCapability EvolutionChangeLevel = "CAPABILITY"
|
||||
EvolutionCore EvolutionChangeLevel = "CORE"
|
||||
)
|
||||
|
||||
type EvolutionCandidate struct {
|
||||
ID string
|
||||
ProposalRef string
|
||||
ChangeLevel EvolutionChangeLevel
|
||||
CreatorIdentity string
|
||||
BaseVersionRef string
|
||||
CandidateVersionRef string
|
||||
MustNotWorsen []string
|
||||
SandboxEvidenceRefs []EvidenceRef
|
||||
BenchmarkEvidenceRefs []EvidenceRef
|
||||
RollbackRef string
|
||||
State string
|
||||
}
|
||||
type EvolutionEvaluation struct {
|
||||
CandidateRef string
|
||||
EvaluatorIdentity string
|
||||
Approved bool
|
||||
EvidenceRefs []EvidenceRef
|
||||
MustNotWorsenPassed bool
|
||||
RecordedAtUnixMillis int64
|
||||
}
|
||||
|
||||
func (e EvolutionEvaluation) ValidFor(c EvolutionCandidate) bool {
|
||||
return e.CandidateRef == c.ID && e.EvaluatorIdentity != "" && e.EvaluatorIdentity != c.CreatorIdentity && e.Approved && e.MustNotWorsenPassed && len(e.EvidenceRefs) > 0 && e.RecordedAtUnixMillis > 0
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# World contract boundary
|
||||
|
||||
World owns typed, versioned referents and vocabulary: `WorldObject`, kind and
|
||||
facet/relation definitions, aliases, identity-resolution decisions, and
|
||||
merge/split lineage. It does not own observations, claims, beliefs, or the
|
||||
truth of values about a referent; those remain Epistemics-owned evidence.
|
||||
|
||||
Cross-owner references are typed semantic references. They do not create
|
||||
cross-owner foreign keys, joins, writes, or a generic `Entity(any)` API.
|
||||
@@ -0,0 +1,322 @@
|
||||
package world
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.cin.su/m/Assistant/contracts/identity"
|
||||
"git.cin.su/m/Assistant/platform/clock"
|
||||
"git.cin.su/m/Assistant/platform/ids"
|
||||
)
|
||||
|
||||
// SemanticReference is the transport envelope used by World contracts. It is
|
||||
// a reference only; it does not expose another owner's storage.
|
||||
type SemanticReference = ids.SemanticRef
|
||||
|
||||
// VersionBinding is a typed applicability constraint. Absence of VersionRef
|
||||
// means unknown, never an implicit "latest".
|
||||
type VersionMatchMode string
|
||||
|
||||
const (
|
||||
VersionExact VersionMatchMode = "EXACT"
|
||||
VersionCompatibleFamily VersionMatchMode = "COMPATIBLE_FAMILY"
|
||||
VersionAtOrBefore VersionMatchMode = "AT_OR_BEFORE"
|
||||
VersionUnknown VersionMatchMode = "UNKNOWN"
|
||||
)
|
||||
|
||||
type VersionBinding struct {
|
||||
SubjectRef SemanticReference
|
||||
VersionRef SemanticReference
|
||||
MatchMode VersionMatchMode
|
||||
}
|
||||
|
||||
type EnvironmentReference struct {
|
||||
EnvironmentRef SemanticReference
|
||||
VersionRef *SemanticReference
|
||||
}
|
||||
|
||||
func (b VersionBinding) Validate() error {
|
||||
if !b.SubjectRef.Valid() || !b.VersionRef.Valid() {
|
||||
return fmt.Errorf("version binding references are invalid")
|
||||
}
|
||||
if b.MatchMode != VersionExact && b.MatchMode != VersionCompatibleFamily && b.MatchMode != VersionAtOrBefore && b.MatchMode != VersionUnknown {
|
||||
return fmt.Errorf("version binding match mode is invalid")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r EnvironmentReference) Validate() error {
|
||||
if !r.EnvironmentRef.Valid() {
|
||||
return fmt.Errorf("environment reference is invalid")
|
||||
}
|
||||
if r.VersionRef != nil && !r.VersionRef.Valid() {
|
||||
return fmt.Errorf("environment version reference is invalid")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type PrincipalRealm string
|
||||
|
||||
const (
|
||||
RealmPersonal PrincipalRealm = "PERSONAL"
|
||||
RealmShared PrincipalRealm = "SHARED"
|
||||
RealmPublic PrincipalRealm = "PUBLIC"
|
||||
RealmSystem PrincipalRealm = "SYSTEM"
|
||||
)
|
||||
|
||||
type SensitivityClass string
|
||||
|
||||
const (
|
||||
SensitivityPublic SensitivityClass = "PUBLIC"
|
||||
SensitivityPrivate SensitivityClass = "PRIVATE"
|
||||
SensitivityRestricted SensitivityClass = "RESTRICTED"
|
||||
SensitivitySecretReference SensitivityClass = "SECRET_REFERENCE"
|
||||
)
|
||||
|
||||
type ValueTypeKind string
|
||||
|
||||
const (
|
||||
ValueString ValueTypeKind = "STRING"
|
||||
ValueInteger ValueTypeKind = "INTEGER"
|
||||
ValueDecimal ValueTypeKind = "DECIMAL"
|
||||
ValueBoolean ValueTypeKind = "BOOLEAN"
|
||||
ValueTime ValueTypeKind = "TIME"
|
||||
ValueDuration ValueTypeKind = "DURATION"
|
||||
ValueEnum ValueTypeKind = "ENUM"
|
||||
ValueReference ValueTypeKind = "REFERENCE"
|
||||
ValueBinaryRef ValueTypeKind = "BINARY_REF"
|
||||
ValueList ValueTypeKind = "LIST"
|
||||
ValueStruct ValueTypeKind = "STRUCT"
|
||||
)
|
||||
|
||||
type ValueField struct {
|
||||
Name string
|
||||
Type ValueTypeSpec
|
||||
}
|
||||
|
||||
type ValueTypeSpec struct {
|
||||
Kind ValueTypeKind
|
||||
Element *ValueTypeSpec
|
||||
Fields []ValueField
|
||||
EnumValues []string
|
||||
ReferenceKind string
|
||||
}
|
||||
|
||||
type WorldObjectID ids.StableID
|
||||
type KindDefinitionID ids.StableID
|
||||
type WorldAliasID ids.StableID
|
||||
type PersonReferent struct {
|
||||
WorldObjectRef SemanticReference
|
||||
DisplayName string
|
||||
ConfidenceMillis int64
|
||||
}
|
||||
|
||||
func (p PersonReferent) Valid() bool {
|
||||
return p.WorldObjectRef.Valid() && p.DisplayName != "" && p.ConfidenceMillis >= 0 && p.ConfidenceMillis <= 1000
|
||||
}
|
||||
|
||||
type FacetDefinitionID ids.StableID
|
||||
type RelationDefinitionID ids.StableID
|
||||
type ResolutionDecisionID ids.StableID
|
||||
type LineageID ids.StableID
|
||||
|
||||
type LifecycleState string
|
||||
|
||||
const (
|
||||
LifecycleActive LifecycleState = "ACTIVE"
|
||||
LifecycleMerged LifecycleState = "MERGED"
|
||||
LifecycleSplit LifecycleState = "SPLIT"
|
||||
LifecycleRetired LifecycleState = "RETIRED"
|
||||
LifecycleUnknownIdentity LifecycleState = "UNKNOWN_IDENTITY"
|
||||
)
|
||||
|
||||
type WorldObject struct {
|
||||
ID WorldObjectID
|
||||
PrincipalID identity.PrincipalID
|
||||
KindDefinitionRef SemanticReference
|
||||
KindRevision ids.Revision
|
||||
Lifecycle LifecycleState
|
||||
CreatedAt clock.Instant
|
||||
}
|
||||
|
||||
type WorldObjectKindDefinition struct {
|
||||
ID KindDefinitionID
|
||||
KindKey string
|
||||
Revision ids.Revision
|
||||
DisplayName string
|
||||
ParentKindRef *SemanticReference
|
||||
Lifecycle LifecycleState
|
||||
DefinitionDigest string
|
||||
RecordedAt clock.Instant
|
||||
}
|
||||
|
||||
type WorldAlias struct {
|
||||
ID WorldAliasID
|
||||
PrincipalID identity.PrincipalID
|
||||
ObjectID WorldObjectID
|
||||
Alias string
|
||||
RecordedAt clock.Instant
|
||||
}
|
||||
|
||||
type FacetDefinition struct {
|
||||
ID FacetDefinitionID
|
||||
KindKey string
|
||||
Revision ids.Revision
|
||||
FacetKey string
|
||||
ValueType ValueTypeSpec
|
||||
DefinitionDigest string
|
||||
RecordedAt clock.Instant
|
||||
}
|
||||
|
||||
type RelationDefinition struct {
|
||||
ID RelationDefinitionID
|
||||
KindKey string
|
||||
Revision ids.Revision
|
||||
RelationKey string
|
||||
TargetKindKey string
|
||||
DefinitionDigest string
|
||||
RecordedAt clock.Instant
|
||||
}
|
||||
|
||||
type ResolutionDecision string
|
||||
|
||||
const (
|
||||
ResolutionSame ResolutionDecision = "SAME"
|
||||
ResolutionDistinct ResolutionDecision = "DISTINCT"
|
||||
ResolutionMerge ResolutionDecision = "MERGE"
|
||||
ResolutionSplit ResolutionDecision = "SPLIT"
|
||||
ResolutionUnresolved ResolutionDecision = "UNRESOLVED"
|
||||
)
|
||||
|
||||
type IdentityResolutionCandidate struct {
|
||||
ObjectRef SemanticReference
|
||||
Evidence []SemanticReference
|
||||
Confidence int64
|
||||
}
|
||||
|
||||
type IdentityResolutionDecisionRecord struct {
|
||||
ID ResolutionDecisionID
|
||||
PrincipalID identity.PrincipalID
|
||||
Candidates []IdentityResolutionCandidate
|
||||
Decision ResolutionDecision
|
||||
Evidence []SemanticReference
|
||||
ConfidenceMillis int64
|
||||
DecidedAt clock.Instant
|
||||
Revision ids.Revision
|
||||
}
|
||||
|
||||
type WorldLineage struct {
|
||||
ID LineageID
|
||||
Operation ResolutionDecision
|
||||
SourceRefs []SemanticReference
|
||||
SuccessorRefs []SemanticReference
|
||||
RecordedAt clock.Instant
|
||||
}
|
||||
|
||||
func (w WorldObject) Validate() error {
|
||||
if w.ID == "" || !w.KindDefinitionRef.Valid() || !w.KindRevision.Valid() || !validLifecycle(w.Lifecycle) || !w.CreatedAt.Valid() {
|
||||
return fmt.Errorf("world object is incomplete")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k WorldObjectKindDefinition) Validate() error {
|
||||
if k.ID == "" || k.KindKey == "" || !k.Revision.Valid() || k.DisplayName == "" || !validLifecycle(k.Lifecycle) || k.DefinitionDigest == "" || !k.RecordedAt.Valid() {
|
||||
return fmt.Errorf("world kind definition is incomplete")
|
||||
}
|
||||
if k.ParentKindRef != nil && !k.ParentKindRef.Valid() {
|
||||
return fmt.Errorf("world kind parent reference is invalid")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a WorldAlias) Validate() error {
|
||||
if a.ID == "" || a.ObjectID == "" || a.Alias == "" || !a.RecordedAt.Valid() {
|
||||
return fmt.Errorf("world alias is incomplete")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f FacetDefinition) Validate() error {
|
||||
if f.ID == "" || f.KindKey == "" || !f.Revision.Valid() || f.FacetKey == "" || f.DefinitionDigest == "" || !f.RecordedAt.Valid() {
|
||||
return fmt.Errorf("facet definition is incomplete")
|
||||
}
|
||||
return f.ValueType.Validate()
|
||||
}
|
||||
|
||||
func (r RelationDefinition) Validate() error {
|
||||
if r.ID == "" || r.KindKey == "" || !r.Revision.Valid() || r.RelationKey == "" || r.TargetKindKey == "" || r.DefinitionDigest == "" || !r.RecordedAt.Valid() {
|
||||
return fmt.Errorf("relation definition is incomplete")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d IdentityResolutionDecisionRecord) Validate() error {
|
||||
if d.ID == "" || !validDecision(d.Decision) || len(d.Candidates) == 0 || d.ConfidenceMillis < 0 || !d.DecidedAt.Valid() || !d.Revision.Valid() {
|
||||
return fmt.Errorf("identity resolution decision is incomplete")
|
||||
}
|
||||
for _, candidate := range d.Candidates {
|
||||
if !candidate.ObjectRef.Valid() || candidate.Confidence < 0 || candidate.Confidence > 1000 {
|
||||
return fmt.Errorf("identity resolution candidate is invalid")
|
||||
}
|
||||
for _, evidence := range candidate.Evidence {
|
||||
if !evidence.Valid() {
|
||||
return fmt.Errorf("identity resolution evidence reference is invalid")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l WorldLineage) Validate() error {
|
||||
if l.ID == "" || (l.Operation != ResolutionMerge && l.Operation != ResolutionSplit) || len(l.SourceRefs) == 0 || len(l.SuccessorRefs) == 0 || !l.RecordedAt.Valid() {
|
||||
return fmt.Errorf("world lineage is incomplete")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validLifecycle(value LifecycleState) bool {
|
||||
return value == LifecycleActive || value == LifecycleMerged || value == LifecycleSplit || value == LifecycleRetired || value == LifecycleUnknownIdentity
|
||||
}
|
||||
|
||||
func validDecision(value ResolutionDecision) bool {
|
||||
return value == ResolutionSame || value == ResolutionDistinct || value == ResolutionMerge || value == ResolutionSplit || value == ResolutionUnresolved
|
||||
}
|
||||
|
||||
func (s ValueTypeSpec) Validate() error {
|
||||
if s.Kind == "" {
|
||||
return fmt.Errorf("value type kind is required")
|
||||
}
|
||||
switch s.Kind {
|
||||
case ValueList:
|
||||
if s.Element == nil {
|
||||
return fmt.Errorf("list element type is required")
|
||||
}
|
||||
return s.Element.Validate()
|
||||
case ValueStruct:
|
||||
if len(s.Fields) == 0 {
|
||||
return fmt.Errorf("struct fields are required")
|
||||
}
|
||||
seen := map[string]struct{}{}
|
||||
for _, field := range s.Fields {
|
||||
if field.Name == "" {
|
||||
return fmt.Errorf("struct field name is required")
|
||||
}
|
||||
if _, ok := seen[field.Name]; ok {
|
||||
return fmt.Errorf("duplicate struct field %q", field.Name)
|
||||
}
|
||||
seen[field.Name] = struct{}{}
|
||||
if err := field.Type.Validate(); err != nil {
|
||||
return fmt.Errorf("field %s: %w", field.Name, err)
|
||||
}
|
||||
}
|
||||
case ValueEnum:
|
||||
if len(s.EnumValues) == 0 {
|
||||
return fmt.Errorf("enum values are required")
|
||||
}
|
||||
case ValueReference:
|
||||
if s.ReferenceKind == "" {
|
||||
return fmt.Errorf("reference kind is required")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package world
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestValueTypeSpecValidatesTypedRecursiveShape(t *testing.T) {
|
||||
spec := ValueTypeSpec{Kind: ValueStruct, Fields: []ValueField{{Name: "name", Type: ValueTypeSpec{Kind: ValueString}}, {Name: "refs", Type: ValueTypeSpec{Kind: ValueList, Element: &ValueTypeSpec{Kind: ValueReference, ReferenceKind: "world.object"}}}}}
|
||||
if err := spec.Validate(); err != nil {
|
||||
t.Fatalf("ValueTypeSpec.Validate() = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValueTypeSpecRejectsDuplicateFields(t *testing.T) {
|
||||
spec := ValueTypeSpec{Kind: ValueStruct, Fields: []ValueField{{Name: "same", Type: ValueTypeSpec{Kind: ValueString}}, {Name: "same", Type: ValueTypeSpec{Kind: ValueString}}}}
|
||||
if err := spec.Validate(); err == nil {
|
||||
t.Fatal("duplicate fields were accepted")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package world
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.cin.su/m/Assistant/platform/clock"
|
||||
"git.cin.su/m/Assistant/platform/ids"
|
||||
)
|
||||
|
||||
func TestWorldObjectValidationKeepsReferentSeparateFromClaims(t *testing.T) {
|
||||
object := WorldObject{ID: "object-1", KindDefinitionRef: ids.SemanticRef{OwnerNamespace: "world", SemanticKind: "kind", StableID: "kind-1"}, KindRevision: 1, Lifecycle: LifecycleActive, CreatedAt: clock.Instant(1)}
|
||||
if err := object.Validate(); err != nil {
|
||||
t.Fatalf("WorldObject.Validate() = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIdentityResolutionRequiresEvidenceBackedCandidates(t *testing.T) {
|
||||
decision := IdentityResolutionDecisionRecord{ID: "decision-1", Decision: ResolutionSame, Candidates: []IdentityResolutionCandidate{{ObjectRef: ids.SemanticRef{OwnerNamespace: "world", SemanticKind: "object", StableID: "object-1"}, Confidence: 900}}, DecidedAt: clock.Instant(1), Revision: 1}
|
||||
if err := decision.Validate(); err != nil {
|
||||
t.Fatalf("IdentityResolutionDecisionRecord.Validate() = %v", err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
BEGIN;
|
||||
|
||||
CREATE SCHEMA IF NOT EXISTS world;
|
||||
SET ROLE world_role;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS world.kind_definitions (
|
||||
kind_definition_id TEXT PRIMARY KEY,
|
||||
kind_key TEXT NOT NULL,
|
||||
revision BIGINT NOT NULL CHECK (revision > 0),
|
||||
display_name TEXT NOT NULL,
|
||||
parent_owner_namespace TEXT NOT NULL DEFAULT '',
|
||||
parent_semantic_kind TEXT NOT NULL DEFAULT '',
|
||||
parent_stable_id TEXT NOT NULL DEFAULT '',
|
||||
parent_revision BIGINT,
|
||||
lifecycle TEXT NOT NULL,
|
||||
definition_digest TEXT NOT NULL,
|
||||
recorded_at_unix_millis BIGINT NOT NULL CHECK (recorded_at_unix_millis > 0),
|
||||
UNIQUE (kind_key, revision)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS world.objects (
|
||||
world_object_id TEXT PRIMARY KEY,
|
||||
principal_id TEXT,
|
||||
kind_owner_namespace TEXT NOT NULL,
|
||||
kind_semantic_kind TEXT NOT NULL,
|
||||
kind_stable_id TEXT NOT NULL,
|
||||
kind_revision BIGINT,
|
||||
lifecycle TEXT NOT NULL,
|
||||
created_at_unix_millis BIGINT NOT NULL CHECK (created_at_unix_millis > 0)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS world.aliases (
|
||||
world_alias_id TEXT PRIMARY KEY,
|
||||
principal_id TEXT,
|
||||
world_object_id TEXT NOT NULL,
|
||||
alias TEXT NOT NULL,
|
||||
recorded_at_unix_millis BIGINT NOT NULL CHECK (recorded_at_unix_millis > 0)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS world.facet_definitions (
|
||||
facet_definition_id TEXT PRIMARY KEY,
|
||||
kind_key TEXT NOT NULL,
|
||||
revision BIGINT NOT NULL CHECK (revision > 0),
|
||||
facet_key TEXT NOT NULL,
|
||||
value_type JSONB NOT NULL,
|
||||
definition_digest TEXT NOT NULL,
|
||||
recorded_at_unix_millis BIGINT NOT NULL CHECK (recorded_at_unix_millis > 0),
|
||||
UNIQUE (kind_key, facet_key, revision)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS world.relation_definitions (
|
||||
relation_definition_id TEXT PRIMARY KEY,
|
||||
kind_key TEXT NOT NULL,
|
||||
revision BIGINT NOT NULL CHECK (revision > 0),
|
||||
relation_key TEXT NOT NULL,
|
||||
target_kind_key TEXT NOT NULL,
|
||||
definition_digest TEXT NOT NULL,
|
||||
recorded_at_unix_millis BIGINT NOT NULL CHECK (recorded_at_unix_millis > 0),
|
||||
UNIQUE (kind_key, relation_key, revision)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS world.identity_resolution_decisions (
|
||||
resolution_decision_id TEXT PRIMARY KEY,
|
||||
principal_id TEXT,
|
||||
candidates JSONB NOT NULL,
|
||||
decision TEXT NOT NULL,
|
||||
evidence JSONB NOT NULL,
|
||||
confidence_millis BIGINT NOT NULL CHECK (confidence_millis >= 0),
|
||||
decided_at_unix_millis BIGINT NOT NULL CHECK (decided_at_unix_millis > 0),
|
||||
revision BIGINT NOT NULL CHECK (revision > 0)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS world.lineage (
|
||||
lineage_id TEXT PRIMARY KEY,
|
||||
operation TEXT NOT NULL,
|
||||
source_refs JSONB NOT NULL,
|
||||
successor_refs JSONB NOT NULL,
|
||||
recorded_at_unix_millis BIGINT NOT NULL CHECK (recorded_at_unix_millis > 0)
|
||||
);
|
||||
|
||||
RESET ROLE;
|
||||
COMMIT;
|
||||
@@ -0,0 +1,47 @@
|
||||
BEGIN;
|
||||
|
||||
SET ROLE situation_role;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS situation.context_scopes (
|
||||
context_scope_id TEXT PRIMARY KEY,
|
||||
principal_id TEXT NOT NULL,
|
||||
revision BIGINT NOT NULL CHECK (revision > 0),
|
||||
bindings JSONB NOT NULL,
|
||||
recorded_at_unix_millis BIGINT NOT NULL CHECK (recorded_at_unix_millis > 0)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS situation.focus_frames (
|
||||
focus_frame_id TEXT PRIMARY KEY,
|
||||
principal_id TEXT NOT NULL,
|
||||
actor_id TEXT NOT NULL,
|
||||
revision BIGINT NOT NULL CHECK (revision > 0),
|
||||
authority_scope_ref TEXT NOT NULL,
|
||||
context_scope_ref JSONB NOT NULL,
|
||||
goal_ref TEXT,
|
||||
task_ref TEXT,
|
||||
source_utterance_ref TEXT,
|
||||
provenance TEXT NOT NULL,
|
||||
confidence_millis BIGINT NOT NULL CHECK (confidence_millis >= 0),
|
||||
state TEXT NOT NULL,
|
||||
parent_frame_ref TEXT,
|
||||
activated_at_unix_millis BIGINT NOT NULL CHECK (activated_at_unix_millis > 0),
|
||||
suspended_at_unix_millis BIGINT,
|
||||
expired_at_unix_millis BIGINT,
|
||||
recorded_at_unix_millis BIGINT NOT NULL CHECK (recorded_at_unix_millis > 0)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS situation.focus_transitions (
|
||||
transition_id TEXT PRIMARY KEY,
|
||||
focus_frame_id TEXT NOT NULL,
|
||||
from_revision BIGINT NOT NULL CHECK (from_revision > 0),
|
||||
to_revision BIGINT NOT NULL CHECK (to_revision > 0),
|
||||
operation TEXT NOT NULL,
|
||||
cause_ref TEXT NOT NULL,
|
||||
recorded_at_unix_millis BIGINT NOT NULL CHECK (recorded_at_unix_millis > 0)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS focus_transitions_frame_order ON situation.focus_transitions(focus_frame_id, recorded_at_unix_millis, transition_id);
|
||||
GRANT SELECT, INSERT, UPDATE ON situation.context_scopes, situation.focus_frames, situation.focus_transitions TO situation_role;
|
||||
|
||||
RESET ROLE;
|
||||
COMMIT;
|
||||
@@ -0,0 +1,7 @@
|
||||
BEGIN;
|
||||
SET ROLE configuration_role;
|
||||
CREATE TABLE IF NOT EXISTS configuration.setting_definitions (setting_definition_id TEXT PRIMARY KEY, owner_namespace TEXT NOT NULL, stable_key TEXT NOT NULL, revision BIGINT NOT NULL CHECK(revision>0), value_type TEXT NOT NULL, default_value TEXT, sensitivity TEXT NOT NULL, lifecycle TEXT NOT NULL, recorded_at_unix_millis BIGINT NOT NULL);
|
||||
CREATE TABLE IF NOT EXISTS configuration.setting_assignments (setting_assignment_id TEXT PRIMARY KEY, principal_id TEXT NOT NULL, definition_ref JSONB NOT NULL, revision BIGINT NOT NULL CHECK(revision>0), selector JSONB NOT NULL, typed_value TEXT, secret_ref TEXT, effective_from_unix_millis BIGINT NOT NULL, effective_until_unix_millis BIGINT, source TEXT NOT NULL, source_ref TEXT NOT NULL, lifecycle TEXT NOT NULL, recorded_at_unix_millis BIGINT NOT NULL);
|
||||
GRANT SELECT, INSERT, UPDATE ON configuration.setting_definitions, configuration.setting_assignments TO configuration_role;
|
||||
RESET ROLE;
|
||||
COMMIT;
|
||||
@@ -0,0 +1,7 @@
|
||||
BEGIN;
|
||||
SET ROLE self_role;
|
||||
CREATE TABLE IF NOT EXISTS self.competence_assessments (assessment_id TEXT PRIMARY KEY, principal_id TEXT NOT NULL, subject_ref JSONB NOT NULL, revision BIGINT NOT NULL CHECK(revision>0), context_matcher JSONB NOT NULL, confidence_millis BIGINT NOT NULL, evidence_refs JSONB NOT NULL, assessed_at_unix_millis BIGINT NOT NULL);
|
||||
CREATE TABLE IF NOT EXISTS self.learning_ledger (entry_id TEXT PRIMARY KEY, principal_id TEXT NOT NULL, observed_problem TEXT NOT NULL, evidence_refs JSONB NOT NULL, diagnosis TEXT NOT NULL, hypothesis TEXT NOT NULL, decision TEXT NOT NULL, recorded_at_unix_millis BIGINT NOT NULL);
|
||||
GRANT SELECT, INSERT, UPDATE ON self.competence_assessments, self.learning_ledger TO self_role;
|
||||
RESET ROLE;
|
||||
COMMIT;
|
||||
@@ -0,0 +1,22 @@
|
||||
BEGIN;
|
||||
SET ROLE work_role;
|
||||
CREATE TABLE IF NOT EXISTS work.long_running_work (
|
||||
investigation_id TEXT PRIMARY KEY, objective TEXT NOT NULL,
|
||||
hypothesis_refs TEXT[] NOT NULL DEFAULT ARRAY[]::TEXT[], attempt_refs TEXT[] NOT NULL DEFAULT ARRAY[]::TEXT[],
|
||||
finding_refs TEXT[] NOT NULL DEFAULT ARRAY[]::TEXT[], blockers TEXT[] NOT NULL DEFAULT ARRAY[]::TEXT[],
|
||||
checkpoint_refs TEXT[] NOT NULL DEFAULT ARRAY[]::TEXT[], next_step TEXT NOT NULL, state TEXT NOT NULL,
|
||||
revision BIGINT NOT NULL, time_budget_millis BIGINT NOT NULL, cost_budget_micros BIGINT NOT NULL,
|
||||
max_attempts INTEGER NOT NULL, recorded_at_unix_millis BIGINT NOT NULL,
|
||||
CHECK (investigation_id <> ''), CHECK (objective <> ''), CHECK (next_step <> ''), CHECK (revision > 0), CHECK (time_budget_millis >= 0), CHECK (cost_budget_micros >= 0), CHECK (max_attempts >= 0), CHECK (recorded_at_unix_millis > 0)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS work.investigation_checkpoints (
|
||||
checkpoint_id TEXT PRIMARY KEY, investigation_id TEXT NOT NULL, completed_attempt_refs TEXT[] NOT NULL DEFAULT ARRAY[]::TEXT[],
|
||||
completed_finding_refs TEXT[] NOT NULL DEFAULT ARRAY[]::TEXT[], next_step TEXT NOT NULL, recorded_at_unix_millis BIGINT NOT NULL,
|
||||
CHECK (checkpoint_id <> ''), CHECK (investigation_id <> ''), CHECK (next_step <> ''), CHECK (recorded_at_unix_millis > 0)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS work.investigation_attempts (
|
||||
investigation_id TEXT NOT NULL, attempt_ref TEXT NOT NULL, verified_at_unix_millis BIGINT NOT NULL,
|
||||
PRIMARY KEY (investigation_id, attempt_ref), CHECK (investigation_id <> ''), CHECK (attempt_ref <> ''), CHECK (verified_at_unix_millis > 0)
|
||||
);
|
||||
RESET ROLE;
|
||||
COMMIT;
|
||||
@@ -0,0 +1,11 @@
|
||||
BEGIN;
|
||||
SET ROLE identity_role;
|
||||
CREATE TABLE IF NOT EXISTS identity.principal_data_realms (
|
||||
realm_id TEXT PRIMARY KEY, owner_principal_id TEXT NOT NULL, realm_kind TEXT NOT NULL,
|
||||
member_principal_ids TEXT[] NOT NULL DEFAULT ARRAY[]::TEXT[], shared BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
revision BIGINT NOT NULL, recorded_at_unix_millis BIGINT NOT NULL,
|
||||
CHECK (realm_id <> ''), CHECK (owner_principal_id <> ''), CHECK (realm_kind IN ('PERSONAL','SHARED','PUBLIC')),
|
||||
CHECK (realm_kind <> 'SHARED' OR shared), CHECK (revision > 0), CHECK (recorded_at_unix_millis > 0)
|
||||
);
|
||||
RESET ROLE;
|
||||
COMMIT;
|
||||
@@ -1,5 +1 @@
|
||||
{
|
||||
"task_file": "dev/tasks/DEV-000295-household-scenario-audit.json",
|
||||
"task_id": "DEV-000295",
|
||||
"task_revision": 2
|
||||
}
|
||||
{"task_file":"dev/tasks/GEN2-025-multimodal-and-ambient-ingress-readiness-without-implementing-the-whole-.json","task_id":"GEN2-025","task_revision":1}
|
||||
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
{
|
||||
"task_id": "ARCH-GEN2-000",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ADR-0019",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
"intent": "Сделать Gen2 официальным архитектурным поколением, а не внешним пожеланием.",
|
||||
"requirement_ids": [
|
||||
"REQ-ARCH-GEN2-000-GOAL",
|
||||
"REQ-ARCH-GEN2-000-BOUNDARY",
|
||||
"REQ-ARCH-GEN2-000-GATE"
|
||||
],
|
||||
"allowed_change_scope": {
|
||||
"modules": [],
|
||||
"paths": [
|
||||
"architecture/**",
|
||||
"development/**",
|
||||
"docs/**",
|
||||
"AGENTS.md",
|
||||
"cmd/devctl/**",
|
||||
"ci/architecture/**",
|
||||
".gitea/workflows/ci.yml",
|
||||
"dev/**"
|
||||
]
|
||||
},
|
||||
"forbidden_change_scope": {
|
||||
"paths": [
|
||||
"security/**",
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
"deploy/**",
|
||||
"database/**",
|
||||
"adapters/models/**"
|
||||
]
|
||||
},
|
||||
"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": [],
|
||||
"may_change": []
|
||||
},
|
||||
"semantic_owner": "Architecture/Governance. Follow GEN2 target architecture: owner boundaries are authoritative; Memory remains a read/retrieval fabric, World is not truth owner, SELF is not a God orchestrator.",
|
||||
"canonical_state": [
|
||||
"нет runtime writer change",
|
||||
"No task may create a second canonical owner for an already-owned mutable semantic concept."
|
||||
],
|
||||
"state_transitions": [
|
||||
"V1 governance -> Gen2 documents accepted -> architecture authority/registry updated -> later implementation tasks become legal"
|
||||
],
|
||||
"side_effects_and_privileges": [
|
||||
"No external mutation may bypass existing Authority -> Commit -> Executor boundaries.",
|
||||
"Do not weaken architecture/security/test guardrails to make the task pass."
|
||||
],
|
||||
"persistence_and_migrations": [
|
||||
"No runtime schema change. This task adopts Gen2 canonical documents/governance only."
|
||||
],
|
||||
"failure_retry_restart": [
|
||||
"Rollback rule: revert architecture adoption до начала schema migration; baseline tag остаётся неизменным",
|
||||
"Retries/restarts must not create duplicate external side effects or a second canonical writer.",
|
||||
"Durable state required by this task must survive process restart; process-local memory is never the only source of truth."
|
||||
],
|
||||
"assumptions": [
|
||||
"Program milestone: M0; test profile: FAST.",
|
||||
"Task branch starts from local gen2-integration; base_repo_revision=gen2-integration must resolve to the untouched integration base while the task branch is active.",
|
||||
"Codex reads DOMOVOY_CONSTITUTION.md, GEN2_TARGET_ARCHITECTURE.md, GEN2_CANONICAL_DATA_MODEL.md, GEN2_MIGRATION_MAP.md, GEN2_MASTER_PLAN.md and GEN2_EXECUTION_CONTRACT.md before non-trivial edits.",
|
||||
"Do not expand scope silently. If a required change falls outside allowed paths or changes ownership, return a typed gap and request a task revision.",
|
||||
"Executable task id is ARCH-GEN2-000 because current devctl accepts ARCH-* as an architecture_revision root; this corresponds exactly to GEN2-000 in the Master Plan.",
|
||||
"Depends on: PRE-GEN2-000."
|
||||
],
|
||||
"acceptance_scenarios": [
|
||||
"ARCH-GEN2-000-SCN-01: внести DOMOVOY_CONSTITUTION, GEN2_TARGET_ARCHITECTURE, GEN2_CANONICAL_DATA_MODEL, GEN2_MIGRATION_MAP и GEN2_MASTER_PLAN в repo",
|
||||
"ARCH-GEN2-000-SCN-02: принять ADR/architecture-generation decision, который разрешает новые owners World/Configuration/SELF и Gen2 migrations",
|
||||
"ARCH-GEN2-000-SCN-03: обновить authority order архитектурных документов",
|
||||
"ARCH-GEN2-000-SCN-04: закрыть/заморозить DEV-000295 как V1 baseline/audit evidence или явно supersede его Gen2 program",
|
||||
"ARCH-GEN2-000-SCN-05: добавить Gen2 task validation в devctl",
|
||||
"ARCH-GEN2-000-SCN-06: добавить FAST/MILESTONE/FULL test profiles или эквивалентную profile metadata без тяжёлого переписывания CI",
|
||||
"ARCH-GEN2-000-SCN-07: внести GEN2_EXECUTION_CONTRACT, GEN2_TASK_INDEX и все последующие GEN2 task packets; сделать их canonical execution program",
|
||||
"ARCH-GEN2-000-EXIT: Codex перед каждым Gen2 task обязан читать canonical Gen2 docs; ordinary V1 task больше не имеет права менять Gen2 architecture."
|
||||
],
|
||||
"required_tests": [
|
||||
"FAST: go test ./cmd/devctl/...",
|
||||
"FAST: go vet ./cmd/devctl/...",
|
||||
"FAST governance: go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json",
|
||||
"FAST scope: go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json",
|
||||
"FAST independent: go run ./cmd/devctl/independent",
|
||||
"FAST whitespace: git diff --check gen2-integration --",
|
||||
"FAST architecture: go run ./cmd/devctl check-architecture"
|
||||
],
|
||||
"terminal_gap_states": [
|
||||
"BLOCKED_BY_SPEC_GAP",
|
||||
"BLOCKED_BY_ARCHITECTURE_GAP",
|
||||
"BLOCKED_BY_MISSING_CAPABILITY",
|
||||
"BLOCKED_BY_CONFLICT",
|
||||
"REQUIRES_MIGRATION_DECISION"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
{
|
||||
"task_id": "GEN2-001",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ARCH-GEN2-000",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
"intent": "Формально закрепить owners, forbidden dependencies и migration law before tables.",
|
||||
"requirement_ids": [
|
||||
"REQ-GEN2-001-GOAL",
|
||||
"REQ-GEN2-001-BOUNDARY",
|
||||
"REQ-GEN2-001-GATE"
|
||||
],
|
||||
"allowed_change_scope": {
|
||||
"modules": [],
|
||||
"paths": [
|
||||
"architecture/**",
|
||||
"contracts/world/**",
|
||||
"contracts/configuration/**",
|
||||
"contracts/self/**",
|
||||
"cmd/devctl/**",
|
||||
"ci/architecture/**",
|
||||
"dev/ACTIVE_TASK.json",
|
||||
"dev/tasks/GEN2-001-accept-gen2-owner-registry-and-dependency-rules.json"
|
||||
]
|
||||
},
|
||||
"forbidden_change_scope": {
|
||||
"paths": [
|
||||
"security/**",
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
".gitea/workflows/**",
|
||||
"deploy/**",
|
||||
"database/**",
|
||||
"adapters/models/**"
|
||||
]
|
||||
},
|
||||
"allowed_architecture_delta": {
|
||||
"new_cross_module_dependencies": 32,
|
||||
"new_canonical_owners": 3,
|
||||
"new_privileges": 0,
|
||||
"new_third_party_dependencies": 0,
|
||||
"new_database_owner_roles": 3
|
||||
},
|
||||
"contracts": {
|
||||
"consume": [],
|
||||
"may_change": [
|
||||
"Only contracts explicitly within GEN2-001 allowed_change_scope; preserve Gen2 owner boundaries and backward compatibility unless this packet says cutover."
|
||||
]
|
||||
},
|
||||
"semantic_owner": "Architecture Registry. Follow GEN2 target architecture: owner boundaries are authoritative; Memory remains a read/retrieval fabric, World is not truth owner, SELF is not a God orchestrator.",
|
||||
"canonical_state": [
|
||||
"нет data writer change",
|
||||
"No task may create a second canonical owner for an already-owned mutable semantic concept."
|
||||
],
|
||||
"state_transitions": [
|
||||
"V1 governance -> Gen2 documents accepted -> architecture authority/registry updated -> later implementation tasks become legal"
|
||||
],
|
||||
"side_effects_and_privileges": [
|
||||
"No external mutation may bypass existing Authority -> Commit -> Executor boundaries.",
|
||||
"Do not weaken architecture/security/test guardrails to make the task pass."
|
||||
],
|
||||
"persistence_and_migrations": [
|
||||
"No database schema change is authorized by this packet unless a revised task explicitly grants database/migration scope."
|
||||
],
|
||||
"failure_retry_restart": [
|
||||
"Rollback rule: architecture revert возможен только до первого зависимого Gen2 task; далее через новый ADR",
|
||||
"Retries/restarts must not create duplicate external side effects or a second canonical writer.",
|
||||
"Durable state required by this task must survive process restart; process-local memory is never the only source of truth."
|
||||
],
|
||||
"assumptions": [
|
||||
"Program milestone: M0; test profile: FAST.",
|
||||
"Task branch starts from local gen2-integration; base_repo_revision=gen2-integration must resolve to the untouched integration base while the task branch is active.",
|
||||
"Codex reads DOMOVOY_CONSTITUTION.md, GEN2_TARGET_ARCHITECTURE.md, GEN2_CANONICAL_DATA_MODEL.md, GEN2_MIGRATION_MAP.md, GEN2_MASTER_PLAN.md and GEN2_EXECUTION_CONTRACT.md before non-trivial edits.",
|
||||
"Do not expand scope silently. If a required change falls outside allowed paths or changes ownership, return a typed gap and request a task revision.",
|
||||
"Depends on: ARCH-GEN2-000."
|
||||
],
|
||||
"acceptance_scenarios": [
|
||||
"GEN2-001-SCN-01: принять owner registry Gen2",
|
||||
"GEN2-001-SCN-02: развести AuthorityScope и ContextScope",
|
||||
"GEN2-001-SCN-03: зафиксировать World != truth owner, SELF != orchestrator, Memory != owner",
|
||||
"GEN2-001-SCN-04: зафиксировать single canonical writer, no cross-owner SQL/FK/JOIN by default",
|
||||
"GEN2-001-SCN-05: зафиксировать Supervisor как runtime layer, не semantic owner",
|
||||
"GEN2-001-SCN-06: добавить architecture checks для новых границ там, где это возможно дешёво",
|
||||
"GEN2-001-EXIT: все последующие task packets могут однозначно указать current_owner/target_owner."
|
||||
],
|
||||
"required_tests": [
|
||||
"FAST: go test ./cmd/devctl/...",
|
||||
"FAST: go vet ./cmd/devctl/...",
|
||||
"FAST governance: go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json",
|
||||
"FAST scope: go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json",
|
||||
"FAST independent: go run ./cmd/devctl/independent",
|
||||
"FAST whitespace: git diff --check gen2-integration --",
|
||||
"FAST architecture: go run ./cmd/devctl check-architecture"
|
||||
],
|
||||
"terminal_gap_states": [
|
||||
"BLOCKED_BY_SPEC_GAP",
|
||||
"BLOCKED_BY_ARCHITECTURE_GAP",
|
||||
"BLOCKED_BY_MISSING_CAPABILITY",
|
||||
"BLOCKED_BY_CONFLICT",
|
||||
"REQUIRES_MIGRATION_DECISION"
|
||||
]
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
{
|
||||
"task_id": "GEN2-002",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ARCH-GEN2-000",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
"intent": "Дать Gen2 стабильные ID/revision/time/SemanticRef/PrincipalRealm/Sensitivity primitives без giant shared domain model.",
|
||||
"requirement_ids": [
|
||||
"REQ-GEN2-002-GOAL",
|
||||
"REQ-GEN2-002-BOUNDARY",
|
||||
"REQ-GEN2-002-GATE"
|
||||
],
|
||||
"allowed_change_scope": {
|
||||
"modules": [
|
||||
"world",
|
||||
"situation",
|
||||
"epistemics",
|
||||
"configuration",
|
||||
"self"
|
||||
],
|
||||
"paths": [
|
||||
"platform/ids/**",
|
||||
"platform/clock/**",
|
||||
"platform/errors/**",
|
||||
"contracts/world/**",
|
||||
"contracts/situation/**",
|
||||
"contracts/epistemics/**",
|
||||
"contracts/configuration/**",
|
||||
"contracts/self/**",
|
||||
"tests/**",
|
||||
"dev/ACTIVE_TASK.json",
|
||||
"dev/tasks/GEN2-002-introduce-shared-gen2-data-primitives-without-shared-domain-semantics.json"
|
||||
]
|
||||
},
|
||||
"forbidden_change_scope": {
|
||||
"paths": [
|
||||
"security/**",
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
"architecture/**",
|
||||
".gitea/workflows/**",
|
||||
"deploy/**",
|
||||
"database/**",
|
||||
"adapters/models/**"
|
||||
]
|
||||
},
|
||||
"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": [
|
||||
"contracts.identity@v1",
|
||||
"contracts.authority@v1"
|
||||
],
|
||||
"may_change": [
|
||||
"Only contracts explicitly within GEN2-002 allowed_change_scope; preserve Gen2 owner boundaries and backward compatibility unless this packet says cutover."
|
||||
]
|
||||
},
|
||||
"semantic_owner": "Platform/Contracts. Follow GEN2 target architecture: owner boundaries are authoritative; Memory remains a read/retrieval fabric, World is not truth owner, SELF is not a God orchestrator.",
|
||||
"canonical_state": [
|
||||
"существующие owners остаются writers",
|
||||
"No task may create a second canonical owner for an already-owned mutable semantic concept."
|
||||
],
|
||||
"state_transitions": [
|
||||
"current state -> additive/shadow Gen2 path -> evidence comparison -> controlled cutover -> compatibility retention; writer rule: существующие owners остаются writers"
|
||||
],
|
||||
"side_effects_and_privileges": [
|
||||
"No external mutation may bypass existing Authority -> Commit -> Executor boundaries.",
|
||||
"Do not weaken architecture/security/test guardrails to make the task pass."
|
||||
],
|
||||
"persistence_and_migrations": [
|
||||
"No database schema change is authorized by this packet unless a revised task explicitly grants database/migration scope."
|
||||
],
|
||||
"failure_retry_restart": [
|
||||
"Rollback rule: additive primitives; old code не удалять",
|
||||
"Retries/restarts must not create duplicate external side effects or a second canonical writer.",
|
||||
"Durable state required by this task must survive process restart; process-local memory is never the only source of truth."
|
||||
],
|
||||
"assumptions": [
|
||||
"Program milestone: M1; test profile: FAST.",
|
||||
"Task branch starts from local gen2-integration; base_repo_revision=gen2-integration must resolve to the untouched integration base while the task branch is active.",
|
||||
"Codex reads DOMOVOY_CONSTITUTION.md, GEN2_TARGET_ARCHITECTURE.md, GEN2_CANONICAL_DATA_MODEL.md, GEN2_MIGRATION_MAP.md, GEN2_MASTER_PLAN.md and GEN2_EXECUTION_CONTRACT.md before non-trivial edits.",
|
||||
"Do not expand scope silently. If a required change falls outside allowed paths or changes ownership, return a typed gap and request a task revision.",
|
||||
"Depends on: GEN2-001."
|
||||
],
|
||||
"acceptance_scenarios": [
|
||||
"GEN2-002-SCN-01: определить StableID/Revision primitives",
|
||||
"GEN2-002-SCN-02: определить SemanticRef как cross-owner reference без прямого DB ownership",
|
||||
"GEN2-002-SCN-03: определить time primitives и data realm/sensitivity primitives",
|
||||
"GEN2-002-SCN-04: реализовать typed value algebra там, где он действительно общий технический контракт",
|
||||
"GEN2-002-SCN-05: не переносить domain meaning в platform/common",
|
||||
"GEN2-002-EXIT: World/Focus/Epistemics/Configuration могут ссылаться на одинаковые технические refs без shared semantic owner."
|
||||
],
|
||||
"required_tests": [
|
||||
"FAST: go test ./platform/ids/... ./contracts/world/... ./contracts/situation/... ./contracts/epistemics/... ./contracts/configuration/... ./contracts/self/...",
|
||||
"FAST: go vet ./platform/ids/... ./contracts/world/... ./contracts/situation/... ./contracts/epistemics/... ./contracts/configuration/... ./contracts/self/...",
|
||||
"FAST governance: go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json",
|
||||
"FAST scope: go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json",
|
||||
"FAST independent: go run ./cmd/devctl/independent",
|
||||
"FAST whitespace: git diff --check gen2-integration --"
|
||||
],
|
||||
"terminal_gap_states": [
|
||||
"BLOCKED_BY_SPEC_GAP",
|
||||
"BLOCKED_BY_ARCHITECTURE_GAP",
|
||||
"BLOCKED_BY_MISSING_CAPABILITY",
|
||||
"BLOCKED_BY_CONFLICT",
|
||||
"REQUIRES_MIGRATION_DECISION"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
{
|
||||
"task_id": "GEN2-003",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ARCH-GEN2-000",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
"intent": "Создать устойчивые referents/aliases/types/facet definitions/relation definitions, не присваивая World владение истиной значений.",
|
||||
"requirement_ids": [
|
||||
"REQ-GEN2-003-GOAL",
|
||||
"REQ-GEN2-003-BOUNDARY",
|
||||
"REQ-GEN2-003-GATE"
|
||||
],
|
||||
"allowed_change_scope": {
|
||||
"modules": [
|
||||
"world"
|
||||
],
|
||||
"paths": [
|
||||
"modules/world/**",
|
||||
"contracts/world/**",
|
||||
"adapters/postgres/world_repository.go",
|
||||
"adapters/postgres/world_*.go",
|
||||
"database/migrations/*.sql",
|
||||
"tests/**",
|
||||
"dev/ACTIVE_TASK.json",
|
||||
"dev/tasks/GEN2-003-add-narrow-world-owner-and-typed-vocabulary.json"
|
||||
]
|
||||
},
|
||||
"forbidden_change_scope": {
|
||||
"paths": [
|
||||
"security/**",
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
"architecture/**",
|
||||
".gitea/workflows/**",
|
||||
"deploy/**",
|
||||
"adapters/models/**"
|
||||
]
|
||||
},
|
||||
"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": [
|
||||
"contracts.identity@v1",
|
||||
"contracts.authority@v1"
|
||||
],
|
||||
"may_change": [
|
||||
"Only contracts explicitly within GEN2-003 allowed_change_scope; preserve Gen2 owner boundaries and backward compatibility unless this packet says cutover."
|
||||
]
|
||||
},
|
||||
"semantic_owner": "World (new). Follow GEN2 target architecture: owner boundaries are authoritative; Memory remains a read/retrieval fabric, World is not truth owner, SELF is not a God orchestrator.",
|
||||
"canonical_state": [
|
||||
"before: project/situation/ad-hoc identifiers; after: World canonical only for referent vocabulary, not facts",
|
||||
"No task may create a second canonical owner for an already-owned mutable semantic concept."
|
||||
],
|
||||
"state_transitions": [
|
||||
"current state -> additive/shadow Gen2 path -> evidence comparison -> controlled cutover -> compatibility retention; writer rule: before: project/situation/ad-hoc identifiers; after: World canonical only for referent vocabulary, not facts"
|
||||
],
|
||||
"side_effects_and_privileges": [
|
||||
"No external mutation may bypass existing Authority -> Commit -> Executor boundaries.",
|
||||
"Do not weaken architecture/security/test guardrails to make the task pass."
|
||||
],
|
||||
"persistence_and_migrations": [
|
||||
"Database changes must be additive next-numbered migrations only until an explicitly approved retirement task; never edit historical V1 migrations 001-045.",
|
||||
"At most one canonical writer is active for each semantic state; backfill/shadow reads do not create a second canonical writer.",
|
||||
"The positive new_database_owner_roles delta in this packet is a devctl-v1 database-surface compatibility gate; it does not authorize cross-owner SQL/FK/JOIN or an extra owner unless explicitly stated by this task."
|
||||
],
|
||||
"failure_retry_restart": [
|
||||
"Rollback rule: V1 identifiers остаются compatibility refs; World tables additive; switch readers back без удаления World evidence",
|
||||
"Retries/restarts must not create duplicate external side effects or a second canonical writer.",
|
||||
"Durable state required by this task must survive process restart; process-local memory is never the only source of truth.",
|
||||
"Cutover must keep the previous read/compatibility path available until the task or milestone explicitly proves retirement."
|
||||
],
|
||||
"assumptions": [
|
||||
"Program milestone: M1; test profile: FAST.",
|
||||
"Task branch starts from local gen2-integration; base_repo_revision=gen2-integration must resolve to the untouched integration base while the task branch is active.",
|
||||
"Codex reads DOMOVOY_CONSTITUTION.md, GEN2_TARGET_ARCHITECTURE.md, GEN2_CANONICAL_DATA_MODEL.md, GEN2_MIGRATION_MAP.md, GEN2_MASTER_PLAN.md and GEN2_EXECUTION_CONTRACT.md before non-trivial edits.",
|
||||
"Do not expand scope silently. If a required change falls outside allowed paths or changes ownership, return a typed gap and request a task revision.",
|
||||
"Depends on: GEN2-002."
|
||||
],
|
||||
"acceptance_scenarios": [
|
||||
"GEN2-003-SCN-01: создать WorldObject stable identity",
|
||||
"GEN2-003-SCN-02: WorldObjectKindDefinition",
|
||||
"GEN2-003-SCN-03: WorldAlias",
|
||||
"GEN2-003-SCN-04: FacetDefinition и RelationDefinition с versioned typed schemas",
|
||||
"GEN2-003-SCN-05: identity-resolution candidate/decision structures",
|
||||
"GEN2-003-SCN-06: обеспечить split/merge lineage",
|
||||
"GEN2-003-SCN-07: начать с минимального набора объектов, необходимых для Focus/Scope migration",
|
||||
"GEN2-003-EXIT: одинаковые имена разных компаний/баз могут иметь разные stable referents; World не хранит claim «какое значение истинно»."
|
||||
],
|
||||
"required_tests": [
|
||||
"FAST: go test ./modules/world/... ./contracts/world/... ./adapters/postgres",
|
||||
"FAST: go vet ./modules/world/... ./contracts/world/... ./adapters/postgres",
|
||||
"FAST governance: go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json",
|
||||
"FAST scope: go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json",
|
||||
"FAST independent: go run ./cmd/devctl/independent",
|
||||
"FAST whitespace: git diff --check gen2-integration --"
|
||||
],
|
||||
"terminal_gap_states": [
|
||||
"BLOCKED_BY_SPEC_GAP",
|
||||
"BLOCKED_BY_ARCHITECTURE_GAP",
|
||||
"BLOCKED_BY_MISSING_CAPABILITY",
|
||||
"BLOCKED_BY_CONFLICT",
|
||||
"REQUIRES_MIGRATION_DECISION"
|
||||
]
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
{
|
||||
"task_id": "GEN2-004",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ARCH-GEN2-000",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
"intent": "Сделать Focus многомерным: person/org/system/database/repository/version/activity/goal/task/entities, а Project оставить лишь возможной гранью.",
|
||||
"requirement_ids": [
|
||||
"REQ-GEN2-004-GOAL",
|
||||
"REQ-GEN2-004-BOUNDARY",
|
||||
"REQ-GEN2-004-GATE"
|
||||
],
|
||||
"allowed_change_scope": {
|
||||
"modules": [
|
||||
"situation",
|
||||
"interaction"
|
||||
],
|
||||
"paths": [
|
||||
"modules/situation/**",
|
||||
"contracts/situation/**",
|
||||
"adapters/postgres/situation_repository.go",
|
||||
"database/migrations/*.sql",
|
||||
"cmd/core-runtime/**",
|
||||
"modules/interaction/**",
|
||||
"contracts/interaction/**",
|
||||
"tests/**",
|
||||
"dev/ACTIVE_TASK.json",
|
||||
"dev/tasks/GEN2-004-replace-project-centric-focus-with-contextscope-focusframe-focusstack.json"
|
||||
]
|
||||
},
|
||||
"forbidden_change_scope": {
|
||||
"paths": [
|
||||
"security/**",
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
"architecture/**",
|
||||
".gitea/workflows/**",
|
||||
"deploy/**",
|
||||
"adapters/models/**"
|
||||
]
|
||||
},
|
||||
"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": [
|
||||
"contracts.identity@v1",
|
||||
"contracts.authority@v1",
|
||||
"contracts.world@gen2",
|
||||
"contracts.work@v1",
|
||||
"contracts.capability@v1"
|
||||
],
|
||||
"may_change": [
|
||||
"Only contracts explicitly within GEN2-004 allowed_change_scope; preserve Gen2 owner boundaries and backward compatibility unless this packet says cutover."
|
||||
]
|
||||
},
|
||||
"semantic_owner": "Situation. Follow GEN2 target architecture: owner boundaries are authoritative; Memory remains a read/retrieval fabric, World is not truth owner, SELF is not a God orchestrator.",
|
||||
"canonical_state": [
|
||||
"before: V1 situation/project focus; after: Situation FocusFrame/Transition; ActiveProjectFocus becomes projection/compatibility",
|
||||
"No task may create a second canonical owner for an already-owned mutable semantic concept."
|
||||
],
|
||||
"state_transitions": [
|
||||
"current state -> additive/shadow Gen2 path -> evidence comparison -> controlled cutover -> compatibility retention; writer rule: before: V1 situation/project focus; after: Situation FocusFrame/Transition; ActiveProjectFocus becomes projection/compatibility"
|
||||
],
|
||||
"side_effects_and_privileges": [
|
||||
"No external mutation may bypass existing Authority -> Commit -> Executor boundaries.",
|
||||
"Do not weaken architecture/security/test guardrails to make the task pass."
|
||||
],
|
||||
"persistence_and_migrations": [
|
||||
"Database changes must be additive next-numbered migrations only until an explicitly approved retirement task; never edit historical V1 migrations 001-045.",
|
||||
"At most one canonical writer is active for each semantic state; backfill/shadow reads do not create a second canonical writer.",
|
||||
"The positive new_database_owner_roles delta in this packet is a devctl-v1 database-surface compatibility gate; it does not authorize cross-owner SQL/FK/JOIN or an extra owner unless explicitly stated by this task."
|
||||
],
|
||||
"failure_retry_restart": [
|
||||
"Rollback rule: feature switch возвращает V1 read path; Gen2 transitions сохраняются",
|
||||
"Retries/restarts must not create duplicate external side effects or a second canonical writer.",
|
||||
"Durable state required by this task must survive process restart; process-local memory is never the only source of truth.",
|
||||
"Cutover must keep the previous read/compatibility path available until the task or milestone explicitly proves retirement."
|
||||
],
|
||||
"assumptions": [
|
||||
"Program milestone: M1; test profile: FAST.",
|
||||
"Task branch starts from local gen2-integration; base_repo_revision=gen2-integration must resolve to the untouched integration base while the task branch is active.",
|
||||
"Codex reads DOMOVOY_CONSTITUTION.md, GEN2_TARGET_ARCHITECTURE.md, GEN2_CANONICAL_DATA_MODEL.md, GEN2_MIGRATION_MAP.md, GEN2_MASTER_PLAN.md and GEN2_EXECUTION_CONTRACT.md before non-trivial edits.",
|
||||
"Do not expand scope silently. If a required change falls outside allowed paths or changes ownership, return a typed gap and request a task revision.",
|
||||
"Depends on: GEN2-003."
|
||||
],
|
||||
"acceptance_scenarios": [
|
||||
"GEN2-004-SCN-01: добавить ContextScope RoleKey/ScopeValue/MatchPolicy",
|
||||
"GEN2-004-SCN-02: добавить FocusFrame stable ID + revision",
|
||||
"GEN2-004-SCN-03: добавить FocusTransition append-only ledger",
|
||||
"GEN2-004-SCN-04: добавить FocusStack/parent/suspended/resume semantics",
|
||||
"GEN2-004-SCN-05: реализовать explicit focus command и inferred focus как разные evidence paths",
|
||||
"GEN2-004-SCN-06: построить compatibility projection ActiveProjectFocus из FocusFrame",
|
||||
"GEN2-004-SCN-07: переключить core/context readers на Focus V2 постепенно",
|
||||
"GEN2-004-EXIT: короткое отвлечение может push/pop focus; Company A/UNF и Company B/UNF не смешиваются по текущему scope."
|
||||
],
|
||||
"required_tests": [
|
||||
"FAST: go test ./modules/situation/... ./contracts/situation/... ./modules/interaction/... ./cmd/core-runtime/... ./adapters/postgres",
|
||||
"FAST: go vet ./modules/situation/... ./contracts/situation/... ./modules/interaction/... ./cmd/core-runtime/... ./adapters/postgres",
|
||||
"FAST governance: go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json",
|
||||
"FAST scope: go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json",
|
||||
"FAST independent: go run ./cmd/devctl/independent",
|
||||
"FAST whitespace: git diff --check gen2-integration --"
|
||||
],
|
||||
"terminal_gap_states": [
|
||||
"BLOCKED_BY_SPEC_GAP",
|
||||
"BLOCKED_BY_ARCHITECTURE_GAP",
|
||||
"BLOCKED_BY_MISSING_CAPABILITY",
|
||||
"BLOCKED_BY_CONFLICT",
|
||||
"REQUIRES_MIGRATION_DECISION"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
{
|
||||
"task_id": "GEN2-005",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ARCH-GEN2-000",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
"intent": "Заложить безопасное same_as?/merge/split и привязку Focus к конкретной версии/окружению.",
|
||||
"requirement_ids": [
|
||||
"REQ-GEN2-005-GOAL",
|
||||
"REQ-GEN2-005-BOUNDARY",
|
||||
"REQ-GEN2-005-GATE"
|
||||
],
|
||||
"allowed_change_scope": {
|
||||
"modules": [
|
||||
"world",
|
||||
"situation"
|
||||
],
|
||||
"paths": [
|
||||
"modules/world/**",
|
||||
"contracts/world/**",
|
||||
"modules/situation/**",
|
||||
"contracts/situation/**",
|
||||
"tests/**",
|
||||
"dev/ACTIVE_TASK.json",
|
||||
"dev/tasks/GEN2-005-world-identity-resolution-and-version-environment-bindings.json"
|
||||
]
|
||||
},
|
||||
"forbidden_change_scope": {
|
||||
"paths": [
|
||||
"security/**",
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
"architecture/**",
|
||||
".gitea/workflows/**",
|
||||
"deploy/**",
|
||||
"database/**",
|
||||
"adapters/models/**"
|
||||
]
|
||||
},
|
||||
"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": [
|
||||
"contracts.world@gen2",
|
||||
"contracts.situation@gen2"
|
||||
],
|
||||
"may_change": [
|
||||
"Only contracts explicitly within GEN2-005 allowed_change_scope; preserve Gen2 owner boundaries and backward compatibility unless this packet says cutover."
|
||||
]
|
||||
},
|
||||
"semantic_owner": "World + Situation contracts, no truth ownership. Follow GEN2 target architecture: owner boundaries are authoritative; Memory remains a read/retrieval fabric, World is not truth owner, SELF is not a God orchestrator.",
|
||||
"canonical_state": [
|
||||
"World writer только identity/referent resolution",
|
||||
"No task may create a second canonical owner for an already-owned mutable semantic concept."
|
||||
],
|
||||
"state_transitions": [
|
||||
"current state -> additive/shadow Gen2 path -> evidence comparison -> controlled cutover -> compatibility retention; writer rule: World writer только identity/referent resolution"
|
||||
],
|
||||
"side_effects_and_privileges": [
|
||||
"No external mutation may bypass existing Authority -> Commit -> Executor boundaries.",
|
||||
"Do not weaken architecture/security/test guardrails to make the task pass."
|
||||
],
|
||||
"persistence_and_migrations": [
|
||||
"No database schema change is authorized by this packet unless a revised task explicitly grants database/migration scope."
|
||||
],
|
||||
"failure_retry_restart": [
|
||||
"Rollback rule: resolution decisions versioned/reversible; no destructive identity merge",
|
||||
"Retries/restarts must not create duplicate external side effects or a second canonical writer.",
|
||||
"Durable state required by this task must survive process restart; process-local memory is never the only source of truth."
|
||||
],
|
||||
"assumptions": [
|
||||
"Program milestone: M1; test profile: MILESTONE.",
|
||||
"Task branch starts from local gen2-integration; base_repo_revision=gen2-integration must resolve to the untouched integration base while the task branch is active.",
|
||||
"Codex reads DOMOVOY_CONSTITUTION.md, GEN2_TARGET_ARCHITECTURE.md, GEN2_CANONICAL_DATA_MODEL.md, GEN2_MIGRATION_MAP.md, GEN2_MASTER_PLAN.md and GEN2_EXECUTION_CONTRACT.md before non-trivial edits.",
|
||||
"Do not expand scope silently. If a required change falls outside allowed paths or changes ownership, return a typed gap and request a task revision.",
|
||||
"Depends on: GEN2-004."
|
||||
],
|
||||
"acceptance_scenarios": [
|
||||
"GEN2-005-SCN-01: реализовать identity resolution candidate с confidence/evidence refs",
|
||||
"GEN2-005-SCN-02: не объединять автоматически неоднозначных людей/системы",
|
||||
"GEN2-005-SCN-03: добавить reversible merge/split lineage",
|
||||
"GEN2-005-SCN-04: добавить version/environment reference primitives, используемые позже Epistemics freshness",
|
||||
"GEN2-005-EXIT: «Иван Telegram» и «Иван CRM» могут быть связаны с уверенностью без необратимого склеивания; две УНФ не путаются."
|
||||
],
|
||||
"required_tests": [
|
||||
"FAST: go test ./modules/world/... ./modules/situation/...",
|
||||
"FAST: go vet ./modules/world/... ./modules/situation/...",
|
||||
"FAST governance: go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json",
|
||||
"FAST scope: go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json",
|
||||
"FAST independent: go run ./cmd/devctl/independent",
|
||||
"FAST whitespace: git diff --check gen2-integration --",
|
||||
"MILESTONE M1: go test ./modules/world/... ./modules/situation/... ./modules/interaction/... ./cmd/core-runtime/...",
|
||||
"MILESTONE M1: run focused scope/focus company-A-vs-company-B and focus-stack regressions; do not run full repository suite."
|
||||
],
|
||||
"terminal_gap_states": [
|
||||
"BLOCKED_BY_SPEC_GAP",
|
||||
"BLOCKED_BY_ARCHITECTURE_GAP",
|
||||
"BLOCKED_BY_MISSING_CAPABILITY",
|
||||
"BLOCKED_BY_CONFLICT",
|
||||
"REQUIRES_MIGRATION_DECISION"
|
||||
]
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
{
|
||||
"task_id": "GEN2-006",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ARCH-GEN2-000",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
"intent": "Убрать зависимость от denormalized truth_bundles как конечной модели истины и создать переоцениваемую канонику evidence/claim/belief.",
|
||||
"requirement_ids": [
|
||||
"REQ-GEN2-006-GOAL",
|
||||
"REQ-GEN2-006-BOUNDARY",
|
||||
"REQ-GEN2-006-GATE"
|
||||
],
|
||||
"allowed_change_scope": {
|
||||
"modules": [
|
||||
"epistemics"
|
||||
],
|
||||
"paths": [
|
||||
"modules/epistemics/**",
|
||||
"contracts/epistemics/**",
|
||||
"adapters/postgres/epistemics_repository.go",
|
||||
"adapters/research/**",
|
||||
"database/migrations/*.sql",
|
||||
"tests/**",
|
||||
"dev/ACTIVE_TASK.json",
|
||||
"dev/tasks/GEN2-006-normalize-epistemics-evidence-propositions-claims-and-belief-assessments.json"
|
||||
]
|
||||
},
|
||||
"forbidden_change_scope": {
|
||||
"paths": [
|
||||
"security/**",
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
"architecture/**",
|
||||
".gitea/workflows/**",
|
||||
"deploy/**",
|
||||
"adapters/models/**"
|
||||
]
|
||||
},
|
||||
"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": [
|
||||
"contracts.identity@v1",
|
||||
"contracts.artifact@v1",
|
||||
"contracts.world@gen2"
|
||||
],
|
||||
"may_change": [
|
||||
"Only contracts explicitly within GEN2-006 allowed_change_scope; preserve Gen2 owner boundaries and backward compatibility unless this packet says cutover."
|
||||
]
|
||||
},
|
||||
"semantic_owner": "Epistemics. Follow GEN2 target architecture: owner boundaries are authoritative; Memory remains a read/retrieval fabric, World is not truth owner, SELF is not a God orchestrator.",
|
||||
"canonical_state": [
|
||||
"before: V1 truth bundle paths; after: normalized Epistemics for new Gen2 knowledge; old bundle writer disabled at cutover or made projection",
|
||||
"No task may create a second canonical owner for an already-owned mutable semantic concept."
|
||||
],
|
||||
"state_transitions": [
|
||||
"current state -> additive/shadow Gen2 path -> evidence comparison -> controlled cutover -> compatibility retention; writer rule: before: V1 truth bundle paths; after: normalized Epistemics for new Gen2 knowledge; old bundle writer disabled at cutover or made projection"
|
||||
],
|
||||
"side_effects_and_privileges": [
|
||||
"No external mutation may bypass existing Authority -> Commit -> Executor boundaries.",
|
||||
"Do not weaken architecture/security/test guardrails to make the task pass."
|
||||
],
|
||||
"persistence_and_migrations": [
|
||||
"Database changes must be additive next-numbered migrations only until an explicitly approved retirement task; never edit historical V1 migrations 001-045.",
|
||||
"At most one canonical writer is active for each semantic state; backfill/shadow reads do not create a second canonical writer.",
|
||||
"The positive new_database_owner_roles delta in this packet is a devctl-v1 database-surface compatibility gate; it does not authorize cross-owner SQL/FK/JOIN or an extra owner unless explicitly stated by this task."
|
||||
],
|
||||
"failure_retry_restart": [
|
||||
"Rollback rule: read switch назад на V1 bundle; normalized evidence не удалять",
|
||||
"Retries/restarts must not create duplicate external side effects or a second canonical writer.",
|
||||
"Durable state required by this task must survive process restart; process-local memory is never the only source of truth.",
|
||||
"Cutover must keep the previous read/compatibility path available until the task or milestone explicitly proves retirement."
|
||||
],
|
||||
"assumptions": [
|
||||
"Program milestone: M2; test profile: FAST.",
|
||||
"Task branch starts from local gen2-integration; base_repo_revision=gen2-integration must resolve to the untouched integration base while the task branch is active.",
|
||||
"Codex reads DOMOVOY_CONSTITUTION.md, GEN2_TARGET_ARCHITECTURE.md, GEN2_CANONICAL_DATA_MODEL.md, GEN2_MIGRATION_MAP.md, GEN2_MASTER_PLAN.md and GEN2_EXECUTION_CONTRACT.md before non-trivial edits.",
|
||||
"Do not expand scope silently. If a required change falls outside allowed paths or changes ownership, return a typed gap and request a task revision.",
|
||||
"Depends on: GEN2-005."
|
||||
],
|
||||
"acceptance_scenarios": [
|
||||
"GEN2-006-SCN-01: добавить Source/SourceSnapshot/SourceExcerpt",
|
||||
"GEN2-006-SCN-02: добавить immutable Observation",
|
||||
"GEN2-006-SCN-03: добавить Proposition/Claim",
|
||||
"GEN2-006-SCN-04: добавить versioned BeliefAssessment",
|
||||
"GEN2-006-SCN-05: backfill существующих truth/research данных с lineage",
|
||||
"GEN2-006-SCN-06: оставить truth_bundles read compatibility до позднего retirement",
|
||||
"GEN2-006-SCN-07: обеспечить возможность новой модели переоценить старый Claim без переписывания Observation",
|
||||
"GEN2-006-EXIT: источник, наблюдение, утверждение и текущая оценка уверенности раздельны и traceable."
|
||||
],
|
||||
"required_tests": [
|
||||
"FAST: go test ./modules/epistemics/... ./contracts/epistemics/... ./adapters/research/... ./adapters/postgres",
|
||||
"FAST: go vet ./modules/epistemics/... ./contracts/epistemics/... ./adapters/research/... ./adapters/postgres",
|
||||
"FAST governance: go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json",
|
||||
"FAST scope: go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json",
|
||||
"FAST independent: go run ./cmd/devctl/independent",
|
||||
"FAST whitespace: git diff --check gen2-integration --"
|
||||
],
|
||||
"terminal_gap_states": [
|
||||
"BLOCKED_BY_SPEC_GAP",
|
||||
"BLOCKED_BY_ARCHITECTURE_GAP",
|
||||
"BLOCKED_BY_MISSING_CAPABILITY",
|
||||
"BLOCKED_BY_CONFLICT",
|
||||
"REQUIRES_MIGRATION_DECISION"
|
||||
]
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
{
|
||||
"task_id": "GEN2-007",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ARCH-GEN2-000",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
"intent": "Сделать старение знания контекстным: прошлогодняя УНФ может быть stale для live-debug и полезной аналогией без live-доступа.",
|
||||
"requirement_ids": [
|
||||
"REQ-GEN2-007-GOAL",
|
||||
"REQ-GEN2-007-BOUNDARY",
|
||||
"REQ-GEN2-007-GATE"
|
||||
],
|
||||
"allowed_change_scope": {
|
||||
"modules": [
|
||||
"epistemics",
|
||||
"work"
|
||||
],
|
||||
"paths": [
|
||||
"modules/epistemics/**",
|
||||
"contracts/epistemics/**",
|
||||
"contracts/work/**",
|
||||
"database/migrations/*.sql",
|
||||
"tests/**",
|
||||
"dev/ACTIVE_TASK.json",
|
||||
"dev/tasks/GEN2-007-general-freshness-volatility-temporal-applicability-and-revalidation.json"
|
||||
]
|
||||
},
|
||||
"forbidden_change_scope": {
|
||||
"paths": [
|
||||
"security/**",
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
"architecture/**",
|
||||
".gitea/workflows/**",
|
||||
"deploy/**",
|
||||
"adapters/models/**"
|
||||
]
|
||||
},
|
||||
"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": [
|
||||
"contracts.epistemics@gen2",
|
||||
"contracts.work@v1",
|
||||
"contracts.world@gen2"
|
||||
],
|
||||
"may_change": [
|
||||
"Only contracts explicitly within GEN2-007 allowed_change_scope; preserve Gen2 owner boundaries and backward compatibility unless this packet says cutover."
|
||||
]
|
||||
},
|
||||
"semantic_owner": "Epistemics. Follow GEN2 target architecture: owner boundaries are authoritative; Memory remains a read/retrieval fabric, World is not truth owner, SELF is not a God orchestrator.",
|
||||
"canonical_state": [
|
||||
"Epistemics writes observation/verification metadata; freshness status computed",
|
||||
"No task may create a second canonical owner for an already-owned mutable semantic concept."
|
||||
],
|
||||
"state_transitions": [
|
||||
"current state -> additive/shadow Gen2 path -> evidence comparison -> controlled cutover -> compatibility retention; writer rule: Epistemics writes observation/verification metadata; freshness status computed"
|
||||
],
|
||||
"side_effects_and_privileges": [
|
||||
"No external mutation may bypass existing Authority -> Commit -> Executor boundaries.",
|
||||
"Do not weaken architecture/security/test guardrails to make the task pass."
|
||||
],
|
||||
"persistence_and_migrations": [
|
||||
"Database changes must be additive next-numbered migrations only until an explicitly approved retirement task; never edit historical V1 migrations 001-045.",
|
||||
"At most one canonical writer is active for each semantic state; backfill/shadow reads do not create a second canonical writer.",
|
||||
"The positive new_database_owner_roles delta in this packet is a devctl-v1 database-surface compatibility gate; it does not authorize cross-owner SQL/FK/JOIN or an extra owner unless explicitly stated by this task."
|
||||
],
|
||||
"failure_retry_restart": [
|
||||
"Rollback rule: metadata additive; old readers ignore it",
|
||||
"Retries/restarts must not create duplicate external side effects or a second canonical writer.",
|
||||
"Durable state required by this task must survive process restart; process-local memory is never the only source of truth."
|
||||
],
|
||||
"assumptions": [
|
||||
"Program milestone: M2; test profile: FAST.",
|
||||
"Task branch starts from local gen2-integration; base_repo_revision=gen2-integration must resolve to the untouched integration base while the task branch is active.",
|
||||
"Codex reads DOMOVOY_CONSTITUTION.md, GEN2_TARGET_ARCHITECTURE.md, GEN2_CANONICAL_DATA_MODEL.md, GEN2_MIGRATION_MAP.md, GEN2_MASTER_PLAN.md and GEN2_EXECUTION_CONTRACT.md before non-trivial edits.",
|
||||
"Do not expand scope silently. If a required change falls outside allowed paths or changes ownership, return a typed gap and request a task revision.",
|
||||
"Depends on: GEN2-006."
|
||||
],
|
||||
"acceptance_scenarios": [
|
||||
"GEN2-007-SCN-01: добавить TemporalApplicability",
|
||||
"GEN2-007-SCN-02: VolatilitySpec/decay class",
|
||||
"GEN2-007-SCN-03: last_verified/observed metadata",
|
||||
"GEN2-007-SCN-04: VersionBinding",
|
||||
"GEN2-007-SCN-05: TaskFreshnessRequirement",
|
||||
"GEN2-007-SCN-06: вычисляемый FreshnessAssessment вместо permanent `stale` flag",
|
||||
"GEN2-007-SCN-07: active revalidation request/response contract",
|
||||
"GEN2-007-SCN-08: fallback semantics: memory-only solution должен быть явно маркирован как непроверенный текущими данными",
|
||||
"GEN2-007-EXIT: один и тот же Claim получает разную пригодность в зависимости от task freshness/risk."
|
||||
],
|
||||
"required_tests": [
|
||||
"FAST: go test ./modules/epistemics/... ./contracts/epistemics/... ./contracts/work/...",
|
||||
"FAST: go vet ./modules/epistemics/... ./contracts/epistemics/... ./contracts/work/...",
|
||||
"FAST governance: go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json",
|
||||
"FAST scope: go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json",
|
||||
"FAST independent: go run ./cmd/devctl/independent",
|
||||
"FAST whitespace: git diff --check gen2-integration --"
|
||||
],
|
||||
"terminal_gap_states": [
|
||||
"BLOCKED_BY_SPEC_GAP",
|
||||
"BLOCKED_BY_ARCHITECTURE_GAP",
|
||||
"BLOCKED_BY_MISSING_CAPABILITY",
|
||||
"BLOCKED_BY_CONFLICT",
|
||||
"REQUIRES_MIGRATION_DECISION"
|
||||
]
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
{
|
||||
"task_id": "GEN2-008",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ARCH-GEN2-000",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
"intent": "Сохранять не только «что сработало», но problem/attempt/why/result/why failed/conditions/evidence.",
|
||||
"requirement_ids": [
|
||||
"REQ-GEN2-008-GOAL",
|
||||
"REQ-GEN2-008-BOUNDARY",
|
||||
"REQ-GEN2-008-GATE"
|
||||
],
|
||||
"allowed_change_scope": {
|
||||
"modules": [
|
||||
"epistemics",
|
||||
"artifact"
|
||||
],
|
||||
"paths": [
|
||||
"modules/epistemics/**",
|
||||
"contracts/epistemics/**",
|
||||
"adapters/postgres/epistemics_repository.go",
|
||||
"contracts/artifact/**",
|
||||
"database/migrations/*.sql",
|
||||
"tests/**",
|
||||
"dev/ACTIVE_TASK.json",
|
||||
"dev/tasks/GEN2-008-promote-episode-and-experience-v2-including-negative-experience.json"
|
||||
]
|
||||
},
|
||||
"forbidden_change_scope": {
|
||||
"paths": [
|
||||
"security/**",
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
"architecture/**",
|
||||
".gitea/workflows/**",
|
||||
"deploy/**",
|
||||
"adapters/models/**"
|
||||
]
|
||||
},
|
||||
"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": [
|
||||
"contracts.epistemics@gen2",
|
||||
"contracts.artifact@v1",
|
||||
"contracts.world@gen2"
|
||||
],
|
||||
"may_change": [
|
||||
"Only contracts explicitly within GEN2-008 allowed_change_scope; preserve Gen2 owner boundaries and backward compatibility unless this packet says cutover."
|
||||
]
|
||||
},
|
||||
"semantic_owner": "Epistemics. Follow GEN2 target architecture: owner boundaries are authoritative; Memory remains a read/retrieval fabric, World is not truth owner, SELF is not a God orchestrator.",
|
||||
"canonical_state": [
|
||||
"Epistemics remains canonical Experience writer; Episode projection derived",
|
||||
"No task may create a second canonical owner for an already-owned mutable semantic concept."
|
||||
],
|
||||
"state_transitions": [
|
||||
"current state -> additive/shadow Gen2 path -> evidence comparison -> controlled cutover -> compatibility retention; writer rule: Epistemics remains canonical Experience writer; Episode projection derived"
|
||||
],
|
||||
"side_effects_and_privileges": [
|
||||
"No external mutation may bypass existing Authority -> Commit -> Executor boundaries.",
|
||||
"Do not weaken architecture/security/test guardrails to make the task pass."
|
||||
],
|
||||
"persistence_and_migrations": [
|
||||
"Database changes must be additive next-numbered migrations only until an explicitly approved retirement task; never edit historical V1 migrations 001-045.",
|
||||
"At most one canonical writer is active for each semantic state; backfill/shadow reads do not create a second canonical writer.",
|
||||
"The positive new_database_owner_roles delta in this packet is a devctl-v1 database-surface compatibility gate; it does not authorize cross-owner SQL/FK/JOIN or an extra owner unless explicitly stated by this task."
|
||||
],
|
||||
"failure_retry_restart": [
|
||||
"Rollback rule: старые Experience records остаются валидны; Episode projection rebuildable",
|
||||
"Retries/restarts must not create duplicate external side effects or a second canonical writer.",
|
||||
"Durable state required by this task must survive process restart; process-local memory is never the only source of truth."
|
||||
],
|
||||
"assumptions": [
|
||||
"Program milestone: M2; test profile: FAST.",
|
||||
"Task branch starts from local gen2-integration; base_repo_revision=gen2-integration must resolve to the untouched integration base while the task branch is active.",
|
||||
"Codex reads DOMOVOY_CONSTITUTION.md, GEN2_TARGET_ARCHITECTURE.md, GEN2_CANONICAL_DATA_MODEL.md, GEN2_MIGRATION_MAP.md, GEN2_MASTER_PLAN.md and GEN2_EXECUTION_CONTRACT.md before non-trivial edits.",
|
||||
"Do not expand scope silently. If a required change falls outside allowed paths or changes ownership, return a typed gap and request a task revision.",
|
||||
"Depends on: GEN2-007."
|
||||
],
|
||||
"acceptance_scenarios": [
|
||||
"GEN2-008-SCN-01: расширить ExperienceRecord V1 без потери старых IDs/evidence",
|
||||
"GEN2-008-SCN-02: сделать success/failure first-class",
|
||||
"GEN2-008-SCN-03: добавить conditions/applicability/version bindings",
|
||||
"GEN2-008-SCN-04: создать Episode как derived/rebuildable episodic projection",
|
||||
"GEN2-008-SCN-05: связать user correction с ReflectionCandidate/Experience",
|
||||
"GEN2-008-SCN-06: добавить anti-pattern: failure under conditions != forever wrong",
|
||||
"GEN2-008-EXIT: retrieval позже может отдельно запросить «что уже пробовали и почему не сработало»."
|
||||
],
|
||||
"required_tests": [
|
||||
"FAST: go test ./modules/epistemics/... ./contracts/epistemics/... ./adapters/postgres",
|
||||
"FAST: go vet ./modules/epistemics/... ./contracts/epistemics/... ./adapters/postgres",
|
||||
"FAST governance: go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json",
|
||||
"FAST scope: go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json",
|
||||
"FAST independent: go run ./cmd/devctl/independent",
|
||||
"FAST whitespace: git diff --check gen2-integration --"
|
||||
],
|
||||
"terminal_gap_states": [
|
||||
"BLOCKED_BY_SPEC_GAP",
|
||||
"BLOCKED_BY_ARCHITECTURE_GAP",
|
||||
"BLOCKED_BY_MISSING_CAPABILITY",
|
||||
"BLOCKED_BY_CONFLICT",
|
||||
"REQUIRES_MIGRATION_DECISION"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
{
|
||||
"task_id": "GEN2-009",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ARCH-GEN2-000",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
"intent": "Дать многоканальный retrieval по canonical owners без единой memory_items каноники.",
|
||||
"requirement_ids": [
|
||||
"REQ-GEN2-009-GOAL",
|
||||
"REQ-GEN2-009-BOUNDARY",
|
||||
"REQ-GEN2-009-GATE"
|
||||
],
|
||||
"allowed_change_scope": {
|
||||
"modules": [],
|
||||
"paths": [
|
||||
"runtime/retrieval/**",
|
||||
"contracts/cognitive/**",
|
||||
"adapters/postgres/*retrieval*.go",
|
||||
"adapters/postgres/*search*.go",
|
||||
"database/migrations/*.sql",
|
||||
"tests/**",
|
||||
"dev/ACTIVE_TASK.json",
|
||||
"dev/tasks/GEN2-009-build-memory-retrieval-fabric-as-rebuildable-projections.json"
|
||||
]
|
||||
},
|
||||
"forbidden_change_scope": {
|
||||
"paths": [
|
||||
"security/**",
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
"architecture/**",
|
||||
".gitea/workflows/**",
|
||||
"deploy/**",
|
||||
"adapters/models/**"
|
||||
]
|
||||
},
|
||||
"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": [
|
||||
"contracts.interaction@v1",
|
||||
"contracts.world@gen2",
|
||||
"contracts.epistemics@gen2",
|
||||
"contracts.work@v1",
|
||||
"contracts.continuity@v1",
|
||||
"contracts.capability@v1",
|
||||
"contracts.configuration@gen2",
|
||||
"contracts.self@gen2"
|
||||
],
|
||||
"may_change": [
|
||||
"Only contracts explicitly within GEN2-009 allowed_change_scope; preserve Gen2 owner boundaries and backward compatibility unless this packet says cutover."
|
||||
]
|
||||
},
|
||||
"semantic_owner": "No canonical Memory owner; projection layer. Follow GEN2 target architecture: owner boundaries are authoritative; Memory remains a read/retrieval fabric, World is not truth owner, SELF is not a God orchestrator.",
|
||||
"canonical_state": [
|
||||
"canonical writers unchanged; retrieval index only projection writer",
|
||||
"No task may create a second canonical owner for an already-owned mutable semantic concept."
|
||||
],
|
||||
"state_transitions": [
|
||||
"current state -> additive/shadow Gen2 path -> evidence comparison -> controlled cutover -> compatibility retention; writer rule: canonical writers unchanged; retrieval index only projection writer"
|
||||
],
|
||||
"side_effects_and_privileges": [
|
||||
"No external mutation may bypass existing Authority -> Commit -> Executor boundaries.",
|
||||
"Do not weaken architecture/security/test guardrails to make the task pass."
|
||||
],
|
||||
"persistence_and_migrations": [
|
||||
"Database changes must be additive next-numbered migrations only until an explicitly approved retirement task; never edit historical V1 migrations 001-045.",
|
||||
"At most one canonical writer is active for each semantic state; backfill/shadow reads do not create a second canonical writer.",
|
||||
"The positive new_database_owner_roles delta in this packet is a devctl-v1 database-surface compatibility gate; it does not authorize cross-owner SQL/FK/JOIN or an extra owner unless explicitly stated by this task."
|
||||
],
|
||||
"failure_retry_restart": [
|
||||
"Rollback rule: drop/rebuild projection and use V1 memory retrieval",
|
||||
"Retries/restarts must not create duplicate external side effects or a second canonical writer.",
|
||||
"Durable state required by this task must survive process restart; process-local memory is never the only source of truth.",
|
||||
"Cutover must keep the previous read/compatibility path available until the task or milestone explicitly proves retirement."
|
||||
],
|
||||
"assumptions": [
|
||||
"Program milestone: M2; test profile: FAST.",
|
||||
"Task branch starts from local gen2-integration; base_repo_revision=gen2-integration must resolve to the untouched integration base while the task branch is active.",
|
||||
"Codex reads DOMOVOY_CONSTITUTION.md, GEN2_TARGET_ARCHITECTURE.md, GEN2_CANONICAL_DATA_MODEL.md, GEN2_MIGRATION_MAP.md, GEN2_MASTER_PLAN.md and GEN2_EXECUTION_CONTRACT.md before non-trivial edits.",
|
||||
"Do not expand scope silently. If a required change falls outside allowed paths or changes ownership, return a typed gap and request a task revision.",
|
||||
"Depends on: GEN2-008."
|
||||
],
|
||||
"acceptance_scenarios": [
|
||||
"GEN2-009-SCN-01: создать RetrievalDocument projection с source SemanticRef",
|
||||
"GEN2-009-SCN-02: projection builders из Interaction/World/Epistemics/Work/Continuity/Capability/Configuration/SELF",
|
||||
"GEN2-009-SCN-03: жёсткий ContextScope/realm/version filter before similarity",
|
||||
"GEN2-009-SCN-04: lanes: semantic, entity/graph-like refs, temporal, goal/commitment, positive experience, negative experience, contradiction, skill",
|
||||
"GEN2-009-SCN-05: ranking signals: relevance, freshness, confidence, salience, scope distance, cost",
|
||||
"GEN2-009-SCN-06: hot/warm/cold/archive retrieval policy как operational storage concern, не truth",
|
||||
"GEN2-009-SCN-07: projection versioning/rebuild command",
|
||||
"GEN2-009-EXIT: vector index можно полностью перестроить без изменения canonical memory; cross-company leak blocked before embedding rank."
|
||||
],
|
||||
"required_tests": [
|
||||
"FAST: go test ./runtime/retrieval/... ./contracts/cognitive/... ./adapters/postgres",
|
||||
"FAST: go vet ./runtime/retrieval/... ./contracts/cognitive/... ./adapters/postgres",
|
||||
"FAST governance: go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json",
|
||||
"FAST scope: go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json",
|
||||
"FAST independent: go run ./cmd/devctl/independent",
|
||||
"FAST whitespace: git diff --check gen2-integration --"
|
||||
],
|
||||
"terminal_gap_states": [
|
||||
"BLOCKED_BY_SPEC_GAP",
|
||||
"BLOCKED_BY_ARCHITECTURE_GAP",
|
||||
"BLOCKED_BY_MISSING_CAPABILITY",
|
||||
"BLOCKED_BY_CONFLICT",
|
||||
"REQUIRES_MIGRATION_DECISION"
|
||||
]
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
{
|
||||
"task_id": "GEN2-010",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ARCH-GEN2-000",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
"intent": "Формировать минимально достаточный model context под текущий Task/Focus/Model/Risk/Freshness и уметь дозапрашивать память во время reasoning.",
|
||||
"requirement_ids": [
|
||||
"REQ-GEN2-010-GOAL",
|
||||
"REQ-GEN2-010-BOUNDARY",
|
||||
"REQ-GEN2-010-GATE"
|
||||
],
|
||||
"allowed_change_scope": {
|
||||
"modules": [],
|
||||
"paths": [
|
||||
"contracts/cognitive/**",
|
||||
"cmd/cognitive-worker/**",
|
||||
"cmd/core-runtime/**",
|
||||
"runtime/retrieval/**",
|
||||
"tests/context_compiler/**",
|
||||
"tests/**",
|
||||
"dev/ACTIVE_TASK.json",
|
||||
"dev/tasks/GEN2-010-context-compiler-v2-with-budgets-coverage-and-iterative-retrieval.json"
|
||||
]
|
||||
},
|
||||
"forbidden_change_scope": {
|
||||
"paths": [
|
||||
"security/**",
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
"architecture/**",
|
||||
".gitea/workflows/**",
|
||||
"deploy/**",
|
||||
"database/**",
|
||||
"adapters/models/**"
|
||||
]
|
||||
},
|
||||
"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": [
|
||||
"contracts.cognitive@v1",
|
||||
"contracts.situation@gen2",
|
||||
"contracts.epistemics@gen2",
|
||||
"contracts.capability@v1"
|
||||
],
|
||||
"may_change": [
|
||||
"Only contracts explicitly within GEN2-010 allowed_change_scope; preserve Gen2 owner boundaries and backward compatibility unless this packet says cutover."
|
||||
]
|
||||
},
|
||||
"semantic_owner": "Cognitive contract/read path, no canonical owner. Follow GEN2 target architecture: owner boundaries are authoritative; Memory remains a read/retrieval fabric, World is not truth owner, SELF is not a God orchestrator.",
|
||||
"canonical_state": [
|
||||
"none; read/compile only",
|
||||
"No task may create a second canonical owner for an already-owned mutable semantic concept."
|
||||
],
|
||||
"state_transitions": [
|
||||
"task input -> bounded Gen2 change -> FAST verification -> merge to gen2-integration; canonical writer rule: none; read/compile only"
|
||||
],
|
||||
"side_effects_and_privileges": [
|
||||
"No external mutation may bypass existing Authority -> Commit -> Executor boundaries.",
|
||||
"Do not weaken architecture/security/test guardrails to make the task pass."
|
||||
],
|
||||
"persistence_and_migrations": [
|
||||
"No database schema change is authorized by this packet unless a revised task explicitly grants database/migration scope."
|
||||
],
|
||||
"failure_retry_restart": [
|
||||
"Rollback rule: feature switch to Context Compiler V1; V2 trace/projections retained",
|
||||
"Retries/restarts must not create duplicate external side effects or a second canonical writer.",
|
||||
"Durable state required by this task must survive process restart; process-local memory is never the only source of truth."
|
||||
],
|
||||
"assumptions": [
|
||||
"Program milestone: M2; test profile: FAST.",
|
||||
"Task branch starts from local gen2-integration; base_repo_revision=gen2-integration must resolve to the untouched integration base while the task branch is active.",
|
||||
"Codex reads DOMOVOY_CONSTITUTION.md, GEN2_TARGET_ARCHITECTURE.md, GEN2_CANONICAL_DATA_MODEL.md, GEN2_MIGRATION_MAP.md, GEN2_MASTER_PLAN.md and GEN2_EXECUTION_CONTRACT.md before non-trivial edits.",
|
||||
"Do not expand scope silently. If a required change falls outside allowed paths or changes ownership, return a typed gap and request a task revision.",
|
||||
"Depends on: GEN2-009."
|
||||
],
|
||||
"acceptance_scenarios": [
|
||||
"GEN2-010-SCN-01: ContextCompileRequest: task/focus/scope/model/token budget/freshness/risk",
|
||||
"GEN2-010-SCN-02: candidate lanes + deterministic scope filtering",
|
||||
"GEN2-010-SCN-03: positive+negative experience balancing",
|
||||
"GEN2-010-SCN-04: contradiction pass",
|
||||
"GEN2-010-SCN-05: coverage check: не упущены ли critical commitments/known failures",
|
||||
"GEN2-010-SCN-06: ContextBundle с explicit token allocation",
|
||||
"GEN2-010-SCN-07: ContextAssemblyTrace: почему элемент включён/исключён",
|
||||
"GEN2-010-SCN-08: iterative memory search tool для model reasoning",
|
||||
"GEN2-010-SCN-09: маленькой локальной модели давать меньше и точнее, а не просто урезать хвост",
|
||||
"GEN2-010-EXIT: локальная модель получает релевантный пакет без любимого цвета в 1C-debug; нужные прошлые грабли попадают в context."
|
||||
],
|
||||
"required_tests": [
|
||||
"FAST: go test ./contracts/cognitive/... ./cmd/cognitive-worker/... ./cmd/core-runtime/... ./runtime/retrieval/... ./tests/context_compiler/...",
|
||||
"FAST: go vet ./contracts/cognitive/... ./cmd/cognitive-worker/... ./cmd/core-runtime/... ./runtime/retrieval/... ./tests/context_compiler/...",
|
||||
"FAST governance: go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json",
|
||||
"FAST scope: go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json",
|
||||
"FAST independent: go run ./cmd/devctl/independent",
|
||||
"FAST whitespace: git diff --check gen2-integration --"
|
||||
],
|
||||
"terminal_gap_states": [
|
||||
"BLOCKED_BY_SPEC_GAP",
|
||||
"BLOCKED_BY_ARCHITECTURE_GAP",
|
||||
"BLOCKED_BY_MISSING_CAPABILITY",
|
||||
"BLOCKED_BY_CONFLICT",
|
||||
"REQUIRES_MIGRATION_DECISION"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
{
|
||||
"task_id": "GEN2-011",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ARCH-GEN2-000",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
"intent": "Перестать использовать ad-hoc PersonalMemory ContextText как долгосрочную основу памяти, не теряя utterance history.",
|
||||
"requirement_ids": [
|
||||
"REQ-GEN2-011-GOAL",
|
||||
"REQ-GEN2-011-BOUNDARY",
|
||||
"REQ-GEN2-011-GATE"
|
||||
],
|
||||
"allowed_change_scope": {
|
||||
"modules": [
|
||||
"interaction"
|
||||
],
|
||||
"paths": [
|
||||
"modules/interaction/**",
|
||||
"contracts/interaction/**",
|
||||
"adapters/postgres/interaction_repository.go",
|
||||
"cmd/core-runtime/**",
|
||||
"cmd/cognitive-worker/**",
|
||||
"runtime/retrieval/**",
|
||||
"tests/**",
|
||||
"dev/ACTIVE_TASK.json",
|
||||
"dev/tasks/GEN2-011-cut-personalmemory-v1-to-compatibility-fallback.json"
|
||||
]
|
||||
},
|
||||
"forbidden_change_scope": {
|
||||
"paths": [
|
||||
"security/**",
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
"architecture/**",
|
||||
".gitea/workflows/**",
|
||||
"deploy/**",
|
||||
"database/**",
|
||||
"adapters/models/**"
|
||||
]
|
||||
},
|
||||
"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": [
|
||||
"contracts.interaction@v1",
|
||||
"contracts.cognitive@gen2",
|
||||
"contracts.situation@gen2"
|
||||
],
|
||||
"may_change": [
|
||||
"Only contracts explicitly within GEN2-011 allowed_change_scope; preserve Gen2 owner boundaries and backward compatibility unless this packet says cutover."
|
||||
]
|
||||
},
|
||||
"semantic_owner": "Interaction -> retrieval fabric reads multiple owners. Follow GEN2 target architecture: owner boundaries are authoritative; Memory remains a read/retrieval fabric, World is not truth owner, SELF is not a God orchestrator.",
|
||||
"canonical_state": [
|
||||
"utterance writer unchanged; PersonalMemory derived writer frozen; retrieval projection primary",
|
||||
"No task may create a second canonical owner for an already-owned mutable semantic concept."
|
||||
],
|
||||
"state_transitions": [
|
||||
"current state -> additive/shadow Gen2 path -> evidence comparison -> controlled cutover -> compatibility retention; writer rule: utterance writer unchanged; PersonalMemory derived writer frozen; retrieval projection primary"
|
||||
],
|
||||
"side_effects_and_privileges": [
|
||||
"No external mutation may bypass existing Authority -> Commit -> Executor boundaries.",
|
||||
"Do not weaken architecture/security/test guardrails to make the task pass."
|
||||
],
|
||||
"persistence_and_migrations": [
|
||||
"No database schema change is authorized by this packet unless a revised task explicitly grants database/migration scope."
|
||||
],
|
||||
"failure_retry_restart": [
|
||||
"Rollback rule: switch reads back to PersonalMemory; no canonical loss",
|
||||
"Retries/restarts must not create duplicate external side effects or a second canonical writer.",
|
||||
"Durable state required by this task must survive process restart; process-local memory is never the only source of truth.",
|
||||
"Cutover must keep the previous read/compatibility path available until the task or milestone explicitly proves retirement."
|
||||
],
|
||||
"assumptions": [
|
||||
"Program milestone: M2; test profile: MILESTONE.",
|
||||
"Task branch starts from local gen2-integration; base_repo_revision=gen2-integration must resolve to the untouched integration base while the task branch is active.",
|
||||
"Codex reads DOMOVOY_CONSTITUTION.md, GEN2_TARGET_ARCHITECTURE.md, GEN2_CANONICAL_DATA_MODEL.md, GEN2_MIGRATION_MAP.md, GEN2_MASTER_PLAN.md and GEN2_EXECUTION_CONTRACT.md before non-trivial edits.",
|
||||
"Do not expand scope silently. If a required change falls outside allowed paths or changes ownership, return a typed gap and request a task revision.",
|
||||
"Depends on: GEN2-010."
|
||||
],
|
||||
"acceptance_scenarios": [
|
||||
"GEN2-011-SCN-01: оставить HumanUtterance append-only canonical history",
|
||||
"GEN2-011-SCN-02: построить Gen2 retrieval из этой истории",
|
||||
"GEN2-011-SCN-03: сравнить V1 PersonalMemory vs Gen2 context on representative cases",
|
||||
"GEN2-011-SCN-04: переключить primary read на Gen2",
|
||||
"GEN2-011-SCN-05: оставить PersonalMemory read fallback до M6",
|
||||
"GEN2-011-SCN-06: запретить новый semantic growth V1 PersonalMemory",
|
||||
"GEN2-011-EXIT: PersonalMemory больше не определяет архитектуру новых features."
|
||||
],
|
||||
"required_tests": [
|
||||
"FAST: go test ./modules/interaction/... ./cmd/core-runtime/... ./cmd/cognitive-worker/... ./runtime/retrieval/...",
|
||||
"FAST: go vet ./modules/interaction/... ./cmd/core-runtime/... ./cmd/cognitive-worker/... ./runtime/retrieval/...",
|
||||
"FAST governance: go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json",
|
||||
"FAST scope: go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json",
|
||||
"FAST independent: go run ./cmd/devctl/independent",
|
||||
"FAST whitespace: git diff --check gen2-integration --",
|
||||
"MILESTONE M2: go test ./modules/epistemics/... ./modules/interaction/... ./cmd/cognitive-worker/... ./cmd/core-runtime/... ./runtime/retrieval/... ./tests/context_compiler/...",
|
||||
"MILESTONE M2: run freshness, negative-experience, contradiction, scope-isolation and PersonalMemory-fallback regressions only."
|
||||
],
|
||||
"terminal_gap_states": [
|
||||
"BLOCKED_BY_SPEC_GAP",
|
||||
"BLOCKED_BY_ARCHITECTURE_GAP",
|
||||
"BLOCKED_BY_MISSING_CAPABILITY",
|
||||
"BLOCKED_BY_CONFLICT",
|
||||
"REQUIRES_MIGRATION_DECISION"
|
||||
]
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
{
|
||||
"task_id": "GEN2-012",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ARCH-GEN2-000",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
"intent": "Убрать ordinary runtime settings из ENV и дать Skills/capabilities возможность объявлять новые typed scoped settings без глобальной schema rewrite.",
|
||||
"requirement_ids": [
|
||||
"REQ-GEN2-012-GOAL",
|
||||
"REQ-GEN2-012-BOUNDARY",
|
||||
"REQ-GEN2-012-GATE"
|
||||
],
|
||||
"allowed_change_scope": {
|
||||
"modules": [
|
||||
"configuration"
|
||||
],
|
||||
"paths": [
|
||||
"modules/configuration/**",
|
||||
"contracts/configuration/**",
|
||||
"adapters/postgres/configuration_repository.go",
|
||||
"platform/secrets/**",
|
||||
"database/migrations/*.sql",
|
||||
"cmd/core-runtime/**",
|
||||
"cmd/cognitive-worker/**",
|
||||
"adapters/models/**",
|
||||
"adapters/research/**",
|
||||
"adapters/multimodal/**",
|
||||
"deploy/docker-compose.prod.yml",
|
||||
"tests/**",
|
||||
"dev/ACTIVE_TASK.json",
|
||||
"dev/tasks/GEN2-012-add-typed-scoped-versioned-configuration-owner-and-bootstrap-boundary.json"
|
||||
]
|
||||
},
|
||||
"forbidden_change_scope": {
|
||||
"paths": [
|
||||
"security/**",
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
"architecture/**",
|
||||
".gitea/workflows/**"
|
||||
]
|
||||
},
|
||||
"allowed_architecture_delta": {
|
||||
"new_cross_module_dependencies": 0,
|
||||
"new_canonical_owners": 0,
|
||||
"new_privileges": 1,
|
||||
"new_third_party_dependencies": 0,
|
||||
"new_database_owner_roles": 1
|
||||
},
|
||||
"contracts": {
|
||||
"consume": [
|
||||
"contracts.identity@v1",
|
||||
"contracts.world@gen2",
|
||||
"contracts.secrets@v1"
|
||||
],
|
||||
"may_change": [
|
||||
"Only contracts explicitly within GEN2-012 allowed_change_scope; preserve Gen2 owner boundaries and backward compatibility unless this packet says cutover."
|
||||
]
|
||||
},
|
||||
"semantic_owner": "Configuration (new). Follow GEN2 target architecture: owner boundaries are authoritative; Memory remains a read/retrieval fabric, World is not truth owner, SELF is not a God orchestrator.",
|
||||
"canonical_state": [
|
||||
"before: ENV/files/ad-hoc DB; after: Configuration for migrated non-bootstrap settings",
|
||||
"No task may create a second canonical owner for an already-owned mutable semantic concept."
|
||||
],
|
||||
"state_transitions": [
|
||||
"current state -> additive/shadow Gen2 path -> evidence comparison -> controlled cutover -> compatibility retention; writer rule: before: ENV/files/ad-hoc DB; after: Configuration for migrated non-bootstrap settings"
|
||||
],
|
||||
"side_effects_and_privileges": [
|
||||
"No external mutation may bypass existing Authority -> Commit -> Executor boundaries.",
|
||||
"Do not weaken architecture/security/test guardrails to make the task pass.",
|
||||
"Deploy/release surface changes are allowed only for the bounded purpose of this packet; production promotion requires the packet's stated evidence and rollback path."
|
||||
],
|
||||
"persistence_and_migrations": [
|
||||
"Database changes must be additive next-numbered migrations only until an explicitly approved retirement task; never edit historical V1 migrations 001-045.",
|
||||
"At most one canonical writer is active for each semantic state; backfill/shadow reads do not create a second canonical writer.",
|
||||
"The positive new_database_owner_roles delta in this packet is a devctl-v1 database-surface compatibility gate; it does not authorize cross-owner SQL/FK/JOIN or an extra owner unless explicitly stated by this task."
|
||||
],
|
||||
"failure_retry_restart": [
|
||||
"Rollback rule: per-consumer fallback to old read path; Configuration values retained",
|
||||
"Retries/restarts must not create duplicate external side effects or a second canonical writer.",
|
||||
"Durable state required by this task must survive process restart; process-local memory is never the only source of truth.",
|
||||
"Cutover must keep the previous read/compatibility path available until the task or milestone explicitly proves retirement."
|
||||
],
|
||||
"assumptions": [
|
||||
"Program milestone: M3; test profile: FAST.",
|
||||
"Task branch starts from local gen2-integration; base_repo_revision=gen2-integration must resolve to the untouched integration base while the task branch is active.",
|
||||
"Codex reads DOMOVOY_CONSTITUTION.md, GEN2_TARGET_ARCHITECTURE.md, GEN2_CANONICAL_DATA_MODEL.md, GEN2_MIGRATION_MAP.md, GEN2_MASTER_PLAN.md and GEN2_EXECUTION_CONTRACT.md before non-trivial edits.",
|
||||
"Do not expand scope silently. If a required change falls outside allowed paths or changes ownership, return a typed gap and request a task revision.",
|
||||
"Depends on: GEN2-011."
|
||||
],
|
||||
"acceptance_scenarios": [
|
||||
"GEN2-012-SCN-01: inventory всех ENV/config consumers",
|
||||
"GEN2-012-SCN-02: создать SettingDefinition stable identity + schema/version/owner/type/default/validation",
|
||||
"GEN2-012-SCN-03: SettingAssignment + ScopeSelector",
|
||||
"GEN2-012-SCN-04: deterministic inheritance/resolution",
|
||||
"GEN2-012-SCN-05: EffectiveConfiguration trace",
|
||||
"GEN2-012-SCN-06: SecretRef only for credentials",
|
||||
"GEN2-012-SCN-07: определить минимальный bootstrap, который может остаться вне Configuration",
|
||||
"GEN2-012-SCN-08: импортировать non-secret текущие значения",
|
||||
"GEN2-012-SCN-09: shadow compare old vs resolved configuration",
|
||||
"GEN2-012-SCN-10: cutover consumers по одному, Commit runtime последним из критических",
|
||||
"GEN2-012-EXIT: можно сказать «для Skill X в Company A значение Y», а Company B получает другое значение без новых ENV vars/columns."
|
||||
],
|
||||
"required_tests": [
|
||||
"FAST: go test ./modules/configuration/... ./contracts/configuration/... ./platform/secrets/... ./cmd/core-runtime/... ./adapters/models/... ./adapters/research/...",
|
||||
"FAST: go vet ./modules/configuration/... ./contracts/configuration/... ./platform/secrets/... ./cmd/core-runtime/... ./adapters/models/... ./adapters/research/...",
|
||||
"FAST governance: go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json",
|
||||
"FAST scope: go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json",
|
||||
"FAST independent: go run ./cmd/devctl/independent",
|
||||
"FAST whitespace: git diff --check gen2-integration --"
|
||||
],
|
||||
"terminal_gap_states": [
|
||||
"BLOCKED_BY_SPEC_GAP",
|
||||
"BLOCKED_BY_ARCHITECTURE_GAP",
|
||||
"BLOCKED_BY_MISSING_CAPABILITY",
|
||||
"BLOCKED_BY_CONFLICT",
|
||||
"REQUIRES_MIGRATION_DECISION"
|
||||
]
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
{
|
||||
"task_id": "GEN2-013",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ARCH-GEN2-000",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
"intent": "Сделать Skill долговечным обучаемым способом решения, а не только профилем capability.",
|
||||
"requirement_ids": [
|
||||
"REQ-GEN2-013-GOAL",
|
||||
"REQ-GEN2-013-BOUNDARY",
|
||||
"REQ-GEN2-013-GATE"
|
||||
],
|
||||
"allowed_change_scope": {
|
||||
"modules": [
|
||||
"capability",
|
||||
"work"
|
||||
],
|
||||
"paths": [
|
||||
"modules/capability/**",
|
||||
"contracts/capability/**",
|
||||
"adapters/postgres/capability_repository.go",
|
||||
"database/migrations/*.sql",
|
||||
"modules/work/**",
|
||||
"contracts/work/**",
|
||||
"tests/**",
|
||||
"dev/ACTIVE_TASK.json",
|
||||
"dev/tasks/GEN2-013-migrate-skill-v1-to-skill-v2-with-applicability-versioning-and-evidence.json"
|
||||
]
|
||||
},
|
||||
"forbidden_change_scope": {
|
||||
"paths": [
|
||||
"security/**",
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
"architecture/**",
|
||||
".gitea/workflows/**",
|
||||
"deploy/**",
|
||||
"adapters/models/**"
|
||||
]
|
||||
},
|
||||
"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": [
|
||||
"contracts.capability@v1",
|
||||
"contracts.work@v1",
|
||||
"contracts.epistemics@gen2"
|
||||
],
|
||||
"may_change": [
|
||||
"Only contracts explicitly within GEN2-013 allowed_change_scope; preserve Gen2 owner boundaries and backward compatibility unless this packet says cutover."
|
||||
]
|
||||
},
|
||||
"semantic_owner": "Capability. Follow GEN2 target architecture: owner boundaries are authoritative; Memory remains a read/retrieval fabric, World is not truth owner, SELF is not a God orchestrator.",
|
||||
"canonical_state": [
|
||||
"Capability remains Skill canonical writer",
|
||||
"No task may create a second canonical owner for an already-owned mutable semantic concept."
|
||||
],
|
||||
"state_transitions": [
|
||||
"current state -> additive/shadow Gen2 path -> evidence comparison -> controlled cutover -> compatibility retention; writer rule: Capability remains Skill canonical writer"
|
||||
],
|
||||
"side_effects_and_privileges": [
|
||||
"No external mutation may bypass existing Authority -> Commit -> Executor boundaries.",
|
||||
"Do not weaken architecture/security/test guardrails to make the task pass."
|
||||
],
|
||||
"persistence_and_migrations": [
|
||||
"Database changes must be additive next-numbered migrations only until an explicitly approved retirement task; never edit historical V1 migrations 001-045.",
|
||||
"At most one canonical writer is active for each semantic state; backfill/shadow reads do not create a second canonical writer.",
|
||||
"The positive new_database_owner_roles delta in this packet is a devctl-v1 database-surface compatibility gate; it does not authorize cross-owner SQL/FK/JOIN or an extra owner unless explicitly stated by this task."
|
||||
],
|
||||
"failure_retry_restart": [
|
||||
"Rollback rule: V1 skill view/projected compatibility; revisions additive",
|
||||
"Retries/restarts must not create duplicate external side effects or a second canonical writer.",
|
||||
"Durable state required by this task must survive process restart; process-local memory is never the only source of truth.",
|
||||
"Cutover must keep the previous read/compatibility path available until the task or milestone explicitly proves retirement."
|
||||
],
|
||||
"assumptions": [
|
||||
"Program milestone: M3; test profile: FAST.",
|
||||
"Task branch starts from local gen2-integration; base_repo_revision=gen2-integration must resolve to the untouched integration base while the task branch is active.",
|
||||
"Codex reads DOMOVOY_CONSTITUTION.md, GEN2_TARGET_ARCHITECTURE.md, GEN2_CANONICAL_DATA_MODEL.md, GEN2_MIGRATION_MAP.md, GEN2_MASTER_PLAN.md and GEN2_EXECUTION_CONTRACT.md before non-trivial edits.",
|
||||
"Do not expand scope silently. If a required change falls outside allowed paths or changes ownership, return a typed gap and request a task revision.",
|
||||
"Depends on: GEN2-012."
|
||||
],
|
||||
"acceptance_scenarios": [
|
||||
"GEN2-013-SCN-01: сохранить stable Skill identity",
|
||||
"GEN2-013-SCN-02: ввести revisions",
|
||||
"GEN2-013-SCN-03: ApplicabilitySpec с Scope/version/conditions",
|
||||
"GEN2-013-SCN-04: связать positive/negative Experience evidence",
|
||||
"GEN2-013-SCN-05: validation/promotion evidence",
|
||||
"GEN2-013-SCN-06: known failure conditions",
|
||||
"GEN2-013-SCN-07: quality history",
|
||||
"GEN2-013-SCN-08: dependency refs",
|
||||
"GEN2-013-SCN-09: сохранить совместимость с существующими SkillProfile/ReflectionCandidate",
|
||||
"GEN2-013-EXIT: успешное решение может стать Candidate/Validated/Active Skill и знает, где он применим."
|
||||
],
|
||||
"required_tests": [
|
||||
"FAST: go test ./modules/capability/... ./contracts/capability/... ./modules/work/... ./adapters/postgres",
|
||||
"FAST: go vet ./modules/capability/... ./contracts/capability/... ./modules/work/... ./adapters/postgres",
|
||||
"FAST governance: go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json",
|
||||
"FAST scope: go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json",
|
||||
"FAST independent: go run ./cmd/devctl/independent",
|
||||
"FAST whitespace: git diff --check gen2-integration --"
|
||||
],
|
||||
"terminal_gap_states": [
|
||||
"BLOCKED_BY_SPEC_GAP",
|
||||
"BLOCKED_BY_ARCHITECTURE_GAP",
|
||||
"BLOCKED_BY_MISSING_CAPABILITY",
|
||||
"BLOCKED_BY_CONFLICT",
|
||||
"REQUIRES_MIGRATION_DECISION"
|
||||
]
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
{
|
||||
"task_id": "GEN2-014",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ARCH-GEN2-000",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
"intent": "Дать быстрый локальный путь для проверенных повторяемых задач без закостенения.",
|
||||
"requirement_ids": [
|
||||
"REQ-GEN2-014-GOAL",
|
||||
"REQ-GEN2-014-BOUNDARY",
|
||||
"REQ-GEN2-014-GATE"
|
||||
],
|
||||
"allowed_change_scope": {
|
||||
"modules": [
|
||||
"capability"
|
||||
],
|
||||
"paths": [
|
||||
"modules/capability/**",
|
||||
"contracts/capability/**",
|
||||
"adapters/postgres/capability_repository.go",
|
||||
"database/migrations/*.sql",
|
||||
"cmd/cognitive-worker/**",
|
||||
"tests/**",
|
||||
"dev/ACTIVE_TASK.json",
|
||||
"dev/tasks/GEN2-014-add-explicit-reflex-lifecycle-shadow-reasoning-and-revalidation.json"
|
||||
]
|
||||
},
|
||||
"forbidden_change_scope": {
|
||||
"paths": [
|
||||
"security/**",
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
"architecture/**",
|
||||
".gitea/workflows/**",
|
||||
"deploy/**",
|
||||
"adapters/models/**"
|
||||
]
|
||||
},
|
||||
"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": [
|
||||
"contracts.capability@gen2",
|
||||
"contracts.cognitive@gen2",
|
||||
"contracts.epistemics@gen2"
|
||||
],
|
||||
"may_change": [
|
||||
"Only contracts explicitly within GEN2-014 allowed_change_scope; preserve Gen2 owner boundaries and backward compatibility unless this packet says cutover."
|
||||
]
|
||||
},
|
||||
"semantic_owner": "Capability. Follow GEN2 target architecture: owner boundaries are authoritative; Memory remains a read/retrieval fabric, World is not truth owner, SELF is not a God orchestrator.",
|
||||
"canonical_state": [
|
||||
"Capability writes Reflex lifecycle; routing reads only ACTIVE applicable reflex",
|
||||
"No task may create a second canonical owner for an already-owned mutable semantic concept."
|
||||
],
|
||||
"state_transitions": [
|
||||
"task input -> bounded Gen2 change -> FAST verification -> merge to gen2-integration; canonical writer rule: Capability writes Reflex lifecycle; routing reads only ACTIVE applicable reflex"
|
||||
],
|
||||
"side_effects_and_privileges": [
|
||||
"No external mutation may bypass existing Authority -> Commit -> Executor boundaries.",
|
||||
"Do not weaken architecture/security/test guardrails to make the task pass."
|
||||
],
|
||||
"persistence_and_migrations": [
|
||||
"Database changes must be additive next-numbered migrations only until an explicitly approved retirement task; never edit historical V1 migrations 001-045.",
|
||||
"At most one canonical writer is active for each semantic state; backfill/shadow reads do not create a second canonical writer.",
|
||||
"The positive new_database_owner_roles delta in this packet is a devctl-v1 database-surface compatibility gate; it does not authorize cross-owner SQL/FK/JOIN or an extra owner unless explicitly stated by this task."
|
||||
],
|
||||
"failure_retry_restart": [
|
||||
"Rollback rule: disable reflex feature switch; route returns to Skill/model",
|
||||
"Retries/restarts must not create duplicate external side effects or a second canonical writer.",
|
||||
"Durable state required by this task must survive process restart; process-local memory is never the only source of truth.",
|
||||
"Cutover must keep the previous read/compatibility path available until the task or milestone explicitly proves retirement."
|
||||
],
|
||||
"assumptions": [
|
||||
"Program milestone: M3; test profile: FAST.",
|
||||
"Task branch starts from local gen2-integration; base_repo_revision=gen2-integration must resolve to the untouched integration base while the task branch is active.",
|
||||
"Codex reads DOMOVOY_CONSTITUTION.md, GEN2_TARGET_ARCHITECTURE.md, GEN2_CANONICAL_DATA_MODEL.md, GEN2_MIGRATION_MAP.md, GEN2_MASTER_PLAN.md and GEN2_EXECUTION_CONTRACT.md before non-trivial edits.",
|
||||
"Do not expand scope silently. If a required change falls outside allowed paths or changes ownership, return a typed gap and request a task revision.",
|
||||
"Depends on: GEN2-013."
|
||||
],
|
||||
"acceptance_scenarios": [
|
||||
"GEN2-014-SCN-01: Reflex stable ID/revisions",
|
||||
"GEN2-014-SCN-02: link Reflex -> originating Skill/evidence",
|
||||
"GEN2-014-SCN-03: applicability/dependencies",
|
||||
"GEN2-014-SCN-04: ShadowPolicy/RevalidationPolicy",
|
||||
"GEN2-014-SCN-05: fallback/escalation Reflex -> Skill -> reasoning",
|
||||
"GEN2-014-SCN-06: drift/stale detection",
|
||||
"GEN2-014-SCN-07: candidate/active/suspended/retired states",
|
||||
"GEN2-014-SCN-08: не считать один успешный run достаточным для promotion",
|
||||
"GEN2-014-EXIT: частая стабильная задача может выполняться быстро локально и автоматически вернуться к reasoning при сомнении."
|
||||
],
|
||||
"required_tests": [
|
||||
"FAST: go test ./modules/capability/... ./contracts/capability/... ./cmd/cognitive-worker/... ./adapters/postgres",
|
||||
"FAST: go vet ./modules/capability/... ./contracts/capability/... ./cmd/cognitive-worker/... ./adapters/postgres",
|
||||
"FAST governance: go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json",
|
||||
"FAST scope: go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json",
|
||||
"FAST independent: go run ./cmd/devctl/independent",
|
||||
"FAST whitespace: git diff --check gen2-integration --"
|
||||
],
|
||||
"terminal_gap_states": [
|
||||
"BLOCKED_BY_SPEC_GAP",
|
||||
"BLOCKED_BY_ARCHITECTURE_GAP",
|
||||
"BLOCKED_BY_MISSING_CAPABILITY",
|
||||
"BLOCKED_BY_CONFLICT",
|
||||
"REQUIRES_MIGRATION_DECISION"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
{
|
||||
"task_id": "GEN2-015",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ARCH-GEN2-000",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
"intent": "Домовой должен знать, что умеет, при каких условиях, где ошибается и что изменилось — без права самому объявить себя хорошим.",
|
||||
"requirement_ids": [
|
||||
"REQ-GEN2-015-GOAL",
|
||||
"REQ-GEN2-015-BOUNDARY",
|
||||
"REQ-GEN2-015-GATE"
|
||||
],
|
||||
"allowed_change_scope": {
|
||||
"modules": [
|
||||
"self"
|
||||
],
|
||||
"paths": [
|
||||
"modules/self/**",
|
||||
"contracts/self/**",
|
||||
"adapters/postgres/self_repository.go",
|
||||
"database/migrations/*.sql",
|
||||
"tests/**",
|
||||
"dev/ACTIVE_TASK.json",
|
||||
"dev/tasks/GEN2-015-add-evidence-backed-self-foundation-and-learning-ledger.json"
|
||||
]
|
||||
},
|
||||
"forbidden_change_scope": {
|
||||
"paths": [
|
||||
"security/**",
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
"architecture/**",
|
||||
".gitea/workflows/**",
|
||||
"deploy/**",
|
||||
"adapters/models/**"
|
||||
]
|
||||
},
|
||||
"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": [
|
||||
"contracts.capability@gen2",
|
||||
"contracts.work@v1",
|
||||
"contracts.epistemics@gen2"
|
||||
],
|
||||
"may_change": [
|
||||
"Only contracts explicitly within GEN2-015 allowed_change_scope; preserve Gen2 owner boundaries and backward compatibility unless this packet says cutover."
|
||||
]
|
||||
},
|
||||
"semantic_owner": "SELF (new). Follow GEN2 target architecture: owner boundaries are authoritative; Memory remains a read/retrieval fabric, World is not truth owner, SELF is not a God orchestrator.",
|
||||
"canonical_state": [
|
||||
"SELF writes derived evidence-backed self assessments only",
|
||||
"No task may create a second canonical owner for an already-owned mutable semantic concept."
|
||||
],
|
||||
"state_transitions": [
|
||||
"task input -> bounded Gen2 change -> FAST verification -> merge to gen2-integration; canonical writer rule: SELF writes derived evidence-backed self assessments only"
|
||||
],
|
||||
"side_effects_and_privileges": [
|
||||
"No external mutation may bypass existing Authority -> Commit -> Executor boundaries.",
|
||||
"Do not weaken architecture/security/test guardrails to make the task pass."
|
||||
],
|
||||
"persistence_and_migrations": [
|
||||
"Database changes must be additive next-numbered migrations only until an explicitly approved retirement task; never edit historical V1 migrations 001-045.",
|
||||
"At most one canonical writer is active for each semantic state; backfill/shadow reads do not create a second canonical writer.",
|
||||
"The positive new_database_owner_roles delta in this packet is a devctl-v1 database-surface compatibility gate; it does not authorize cross-owner SQL/FK/JOIN or an extra owner unless explicitly stated by this task."
|
||||
],
|
||||
"failure_retry_restart": [
|
||||
"Rollback rule: SELF can be disabled; no mutation path depends on it initially",
|
||||
"Retries/restarts must not create duplicate external side effects or a second canonical writer.",
|
||||
"Durable state required by this task must survive process restart; process-local memory is never the only source of truth."
|
||||
],
|
||||
"assumptions": [
|
||||
"Program milestone: M3; test profile: FAST.",
|
||||
"Task branch starts from local gen2-integration; base_repo_revision=gen2-integration must resolve to the untouched integration base while the task branch is active.",
|
||||
"Codex reads DOMOVOY_CONSTITUTION.md, GEN2_TARGET_ARCHITECTURE.md, GEN2_CANONICAL_DATA_MODEL.md, GEN2_MIGRATION_MAP.md, GEN2_MASTER_PLAN.md and GEN2_EXECUTION_CONTRACT.md before non-trivial edits.",
|
||||
"Do not expand scope silently. If a required change falls outside allowed paths or changes ownership, return a typed gap and request a task revision.",
|
||||
"Depends on: GEN2-014."
|
||||
],
|
||||
"acceptance_scenarios": [
|
||||
"GEN2-015-SCN-01: CompetenceAssessment from outcomes/tests/task evaluations",
|
||||
"GEN2-015-SCN-02: WeaknessPattern",
|
||||
"GEN2-015-SCN-03: DependencyAssessment",
|
||||
"GEN2-015-SCN-04: ImprovementNeed",
|
||||
"GEN2-015-SCN-05: LearningLedgerEntry",
|
||||
"GEN2-015-SCN-06: SelfCompositionSnapshot",
|
||||
"GEN2-015-SCN-07: provenance каждого self-assessment",
|
||||
"GEN2-015-SCN-08: запрет arbitrary `competence=1.0` от Skill",
|
||||
"GEN2-015-SCN-09: первый режим SELF только observe/evaluate/propose",
|
||||
"GEN2-015-EXIT: система отличает «я разучился» от «зависимость IFCM временно недоступна» и хранит собственные повторяемые weakness patterns."
|
||||
],
|
||||
"required_tests": [
|
||||
"FAST: go test ./modules/self/... ./contracts/self/... ./adapters/postgres",
|
||||
"FAST: go vet ./modules/self/... ./contracts/self/... ./adapters/postgres",
|
||||
"FAST governance: go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json",
|
||||
"FAST scope: go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json",
|
||||
"FAST independent: go run ./cmd/devctl/independent",
|
||||
"FAST whitespace: git diff --check gen2-integration --"
|
||||
],
|
||||
"terminal_gap_states": [
|
||||
"BLOCKED_BY_SPEC_GAP",
|
||||
"BLOCKED_BY_ARCHITECTURE_GAP",
|
||||
"BLOCKED_BY_MISSING_CAPABILITY",
|
||||
"BLOCKED_BY_CONFLICT",
|
||||
"REQUIRES_MIGRATION_DECISION"
|
||||
]
|
||||
}
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
{
|
||||
"task_id": "GEN2-016",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ARCH-GEN2-000",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
"intent": "Выбирать минимально достаточный интеллект и доказуемо сравнивать модели/skills на реальных задачах владельца.",
|
||||
"requirement_ids": [
|
||||
"REQ-GEN2-016-GOAL",
|
||||
"REQ-GEN2-016-BOUNDARY",
|
||||
"REQ-GEN2-016-GATE"
|
||||
],
|
||||
"allowed_change_scope": {
|
||||
"modules": [
|
||||
"capability",
|
||||
"work",
|
||||
"self"
|
||||
],
|
||||
"paths": [
|
||||
"modules/capability/**",
|
||||
"contracts/capability/**",
|
||||
"modules/work/**",
|
||||
"contracts/work/**",
|
||||
"modules/self/**",
|
||||
"contracts/self/**",
|
||||
"cmd/cognitive-worker/**",
|
||||
"tests/**",
|
||||
"dev/ACTIVE_TASK.json",
|
||||
"dev/tasks/GEN2-016-intelligence-strategy-resource-cost-policy-and-personal-benchmark.json"
|
||||
]
|
||||
},
|
||||
"forbidden_change_scope": {
|
||||
"paths": [
|
||||
"security/**",
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
"architecture/**",
|
||||
".gitea/workflows/**",
|
||||
"deploy/**",
|
||||
"database/**",
|
||||
"adapters/models/**"
|
||||
]
|
||||
},
|
||||
"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": [
|
||||
"contracts.capability@gen2",
|
||||
"contracts.work@gen2",
|
||||
"contracts.self@gen2",
|
||||
"contracts.cognitive@gen2"
|
||||
],
|
||||
"may_change": [
|
||||
"Only contracts explicitly within GEN2-016 allowed_change_scope; preserve Gen2 owner boundaries and backward compatibility unless this packet says cutover."
|
||||
]
|
||||
},
|
||||
"semantic_owner": "Capability + Work evaluation; SELF observes. Follow GEN2 target architecture: owner boundaries are authoritative; Memory remains a read/retrieval fabric, World is not truth owner, SELF is not a God orchestrator.",
|
||||
"canonical_state": [
|
||||
"Capability/Work existing evaluation writers extended; SELF reads assessments",
|
||||
"No task may create a second canonical owner for an already-owned mutable semantic concept."
|
||||
],
|
||||
"state_transitions": [
|
||||
"current state -> additive/shadow Gen2 path -> evidence comparison -> controlled cutover -> compatibility retention; writer rule: Capability/Work existing evaluation writers extended; SELF reads assessments"
|
||||
],
|
||||
"side_effects_and_privileges": [
|
||||
"No external mutation may bypass existing Authority -> Commit -> Executor boundaries.",
|
||||
"Do not weaken architecture/security/test guardrails to make the task pass."
|
||||
],
|
||||
"persistence_and_migrations": [
|
||||
"No database schema change is authorized by this packet unless a revised task explicitly grants database/migration scope."
|
||||
],
|
||||
"failure_retry_restart": [
|
||||
"Rollback rule: routing returns to V1 selection; evidence retained",
|
||||
"Retries/restarts must not create duplicate external side effects or a second canonical writer.",
|
||||
"Durable state required by this task must survive process restart; process-local memory is never the only source of truth."
|
||||
],
|
||||
"assumptions": [
|
||||
"Program milestone: M3; test profile: FULL.",
|
||||
"Task branch starts from local gen2-integration; base_repo_revision=gen2-integration must resolve to the untouched integration base while the task branch is active.",
|
||||
"Codex reads DOMOVOY_CONSTITUTION.md, GEN2_TARGET_ARCHITECTURE.md, GEN2_CANONICAL_DATA_MODEL.md, GEN2_MIGRATION_MAP.md, GEN2_MASTER_PLAN.md and GEN2_EXECUTION_CONTRACT.md before non-trivial edits.",
|
||||
"Do not expand scope silently. If a required change falls outside allowed paths or changes ownership, return a typed gap and request a task revision.",
|
||||
"Depends on: GEN2-015."
|
||||
],
|
||||
"acceptance_scenarios": [
|
||||
"GEN2-016-SCN-01: formalize path: rule/reflex/skill/small local/strong local/external/research/team",
|
||||
"GEN2-016-SCN-02: input signals: required quality, latency preference, cost budget, risk, freshness, cost of error",
|
||||
"GEN2-016-SCN-03: создать Personal Benchmark dataset refs из реальных approved task cases",
|
||||
"GEN2-016-SCN-04: определить Goal/Preference evaluation profile: owner intent → long-term goals → current task → contextual preferences",
|
||||
"GEN2-016-SCN-05: inferred preferences хранить как evidence-backed scoped hypotheses, а не вечные глобальные настройки",
|
||||
"GEN2-016-SCN-06: сохранять decision-regret evidence: мог ли Домовой с доступными тогда данными выбрать лучше",
|
||||
"GEN2-016-SCN-07: сохранять task-specific evaluation, user correction, latency/cost/retry evidence",
|
||||
"GEN2-016-SCN-08: не сводить качество к одной метрике и не оптимизироваться под согласие владельца",
|
||||
"GEN2-016-SCN-09: добавить exploration/shadow для low-risk paths",
|
||||
"GEN2-016-SCN-10: новая модель не получает automatic promotion только из-за public benchmark",
|
||||
"GEN2-016-EXIT: простая задача не запускает команду из 20 агентов; сложная high-risk задача может эскалировать глубину reasoning."
|
||||
],
|
||||
"required_tests": [
|
||||
"FAST: go test ./modules/capability/... ./modules/work/... ./modules/self/... ./cmd/cognitive-worker/...",
|
||||
"FAST: go vet ./modules/capability/... ./modules/work/... ./modules/self/... ./cmd/cognitive-worker/...",
|
||||
"FAST governance: go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json",
|
||||
"FAST scope: go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json",
|
||||
"FAST independent: go run ./cmd/devctl/independent",
|
||||
"FAST whitespace: git diff --check gen2-integration --",
|
||||
"FULL: go test ./...",
|
||||
"FULL: go vet ./...",
|
||||
"FULL M3: architecture + migration + configuration + skill/reflex + SELF benchmark regressions."
|
||||
],
|
||||
"terminal_gap_states": [
|
||||
"BLOCKED_BY_SPEC_GAP",
|
||||
"BLOCKED_BY_ARCHITECTURE_GAP",
|
||||
"BLOCKED_BY_MISSING_CAPABILITY",
|
||||
"BLOCKED_BY_CONFLICT",
|
||||
"REQUIRES_MIGRATION_DECISION"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
{
|
||||
"task_id": "GEN2-017",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ARCH-GEN2-000",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
"intent": "«Разбирайся хоть сутки» должно быть durable process с checkpoint, а не один длинный LLM request.",
|
||||
"requirement_ids": [
|
||||
"REQ-GEN2-017-GOAL",
|
||||
"REQ-GEN2-017-BOUNDARY",
|
||||
"REQ-GEN2-017-GATE"
|
||||
],
|
||||
"allowed_change_scope": {
|
||||
"modules": [
|
||||
"work",
|
||||
"continuity"
|
||||
],
|
||||
"paths": [
|
||||
"modules/work/**",
|
||||
"contracts/work/**",
|
||||
"adapters/postgres/work_repository.go",
|
||||
"database/migrations/*.sql",
|
||||
"cmd/work-graph-runtime/**",
|
||||
"cmd/research-runtime/**",
|
||||
"modules/continuity/**",
|
||||
"contracts/continuity/**",
|
||||
"tests/**",
|
||||
"dev/ACTIVE_TASK.json",
|
||||
"dev/tasks/GEN2-017-extend-work-into-durable-longrunningwork-investigation.json"
|
||||
]
|
||||
},
|
||||
"forbidden_change_scope": {
|
||||
"paths": [
|
||||
"security/**",
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
"architecture/**",
|
||||
".gitea/workflows/**",
|
||||
"deploy/**",
|
||||
"adapters/models/**"
|
||||
]
|
||||
},
|
||||
"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": [
|
||||
"contracts.work@v1",
|
||||
"contracts.continuity@v1",
|
||||
"contracts.capability@gen2",
|
||||
"contracts.epistemics@gen2"
|
||||
],
|
||||
"may_change": [
|
||||
"Only contracts explicitly within GEN2-017 allowed_change_scope; preserve Gen2 owner boundaries and backward compatibility unless this packet says cutover."
|
||||
]
|
||||
},
|
||||
"semantic_owner": "Work. Follow GEN2 target architecture: owner boundaries are authoritative; Memory remains a read/retrieval fabric, World is not truth owner, SELF is not a God orchestrator.",
|
||||
"canonical_state": [
|
||||
"Work canonical writer for long-running job state; Continuity only future relevance",
|
||||
"No task may create a second canonical owner for an already-owned mutable semantic concept."
|
||||
],
|
||||
"state_transitions": [
|
||||
"current state -> additive/shadow Gen2 path -> evidence comparison -> controlled cutover -> compatibility retention; writer rule: Work canonical writer for long-running job state; Continuity only future relevance"
|
||||
],
|
||||
"side_effects_and_privileges": [
|
||||
"No external mutation may bypass existing Authority -> Commit -> Executor boundaries.",
|
||||
"Do not weaken architecture/security/test guardrails to make the task pass."
|
||||
],
|
||||
"persistence_and_migrations": [
|
||||
"Database changes must be additive next-numbered migrations only until an explicitly approved retirement task; never edit historical V1 migrations 001-045.",
|
||||
"At most one canonical writer is active for each semantic state; backfill/shadow reads do not create a second canonical writer.",
|
||||
"The positive new_database_owner_roles delta in this packet is a devctl-v1 database-surface compatibility gate; it does not authorize cross-owner SQL/FK/JOIN or an extra owner unless explicitly stated by this task."
|
||||
],
|
||||
"failure_retry_restart": [
|
||||
"Rollback rule: old Work Graph path remains for normal tasks; new jobs can be paused/read-only",
|
||||
"Retries/restarts must not create duplicate external side effects or a second canonical writer.",
|
||||
"Durable state required by this task must survive process restart; process-local memory is never the only source of truth."
|
||||
],
|
||||
"assumptions": [
|
||||
"Program milestone: M4; test profile: FAST.",
|
||||
"Task branch starts from local gen2-integration; base_repo_revision=gen2-integration must resolve to the untouched integration base while the task branch is active.",
|
||||
"Codex reads DOMOVOY_CONSTITUTION.md, GEN2_TARGET_ARCHITECTURE.md, GEN2_CANONICAL_DATA_MODEL.md, GEN2_MIGRATION_MAP.md, GEN2_MASTER_PLAN.md and GEN2_EXECUTION_CONTRACT.md before non-trivial edits.",
|
||||
"Do not expand scope silently. If a required change falls outside allowed paths or changes ownership, return a typed gap and request a task revision.",
|
||||
"Depends on: GEN2-016."
|
||||
],
|
||||
"acceptance_scenarios": [
|
||||
"GEN2-017-SCN-01: LongRunningWork/Investigation state",
|
||||
"GEN2-017-SCN-02: objective, hypotheses, attempts, findings, blockers, next step",
|
||||
"GEN2-017-SCN-03: budget: time/cost/model/resource",
|
||||
"GEN2-017-SCN-04: checkpoint/resume/preempt semantics",
|
||||
"GEN2-017-SCN-05: model/provider replacement between attempts",
|
||||
"GEN2-017-SCN-06: stop condition «решение не найдено в бюджете» с сохранением проверенных вариантов",
|
||||
"GEN2-017-SCN-07: Continuity integration для wake/attention without second scheduler",
|
||||
"GEN2-017-EXIT: долгая задача переживает restart и продолжает с next step, не повторяя уже проверенные попытки."
|
||||
],
|
||||
"required_tests": [
|
||||
"FAST: go test ./modules/work/... ./modules/continuity/... ./cmd/work-graph-runtime/... ./cmd/research-runtime/... ./adapters/postgres",
|
||||
"FAST: go vet ./modules/work/... ./modules/continuity/... ./cmd/work-graph-runtime/... ./cmd/research-runtime/... ./adapters/postgres",
|
||||
"FAST governance: go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json",
|
||||
"FAST scope: go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json",
|
||||
"FAST independent: go run ./cmd/devctl/independent",
|
||||
"FAST whitespace: git diff --check gen2-integration --"
|
||||
],
|
||||
"terminal_gap_states": [
|
||||
"BLOCKED_BY_SPEC_GAP",
|
||||
"BLOCKED_BY_ARCHITECTURE_GAP",
|
||||
"BLOCKED_BY_MISSING_CAPABILITY",
|
||||
"BLOCKED_BY_CONFLICT",
|
||||
"REQUIRES_MIGRATION_DECISION"
|
||||
]
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
{
|
||||
"task_id": "GEN2-018",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ARCH-GEN2-000",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
"intent": "Домовой может смотреть на задачу как архитектор/программист/директор/дизайнер или собрать временную команду без roleplay и без постоянного staff.",
|
||||
"requirement_ids": [
|
||||
"REQ-GEN2-018-GOAL",
|
||||
"REQ-GEN2-018-BOUNDARY",
|
||||
"REQ-GEN2-018-GATE"
|
||||
],
|
||||
"allowed_change_scope": {
|
||||
"modules": [
|
||||
"work",
|
||||
"capability"
|
||||
],
|
||||
"paths": [
|
||||
"modules/work/**",
|
||||
"contracts/work/**",
|
||||
"modules/capability/**",
|
||||
"contracts/capability/**",
|
||||
"cmd/work-graph-runtime/**",
|
||||
"tests/**",
|
||||
"dev/ACTIVE_TASK.json",
|
||||
"dev/tasks/GEN2-018-add-perspective-and-dynamic-specialist-team-semantics-on-existing-work-g.json"
|
||||
]
|
||||
},
|
||||
"forbidden_change_scope": {
|
||||
"paths": [
|
||||
"security/**",
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
"architecture/**",
|
||||
".gitea/workflows/**",
|
||||
"deploy/**",
|
||||
"database/**",
|
||||
"adapters/models/**"
|
||||
]
|
||||
},
|
||||
"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": [
|
||||
"contracts.work@gen2",
|
||||
"contracts.capability@gen2"
|
||||
],
|
||||
"may_change": [
|
||||
"Only contracts explicitly within GEN2-018 allowed_change_scope; preserve Gen2 owner boundaries and backward compatibility unless this packet says cutover."
|
||||
]
|
||||
},
|
||||
"semantic_owner": "Work. Follow GEN2 target architecture: owner boundaries are authoritative; Memory remains a read/retrieval fabric, World is not truth owner, SELF is not a God orchestrator.",
|
||||
"canonical_state": [
|
||||
"Work graph remains owner; no separate Agent Memory owner",
|
||||
"No task may create a second canonical owner for an already-owned mutable semantic concept."
|
||||
],
|
||||
"state_transitions": [
|
||||
"current state -> additive/shadow Gen2 path -> evidence comparison -> controlled cutover -> compatibility retention; writer rule: Work graph remains owner; no separate Agent Memory owner"
|
||||
],
|
||||
"side_effects_and_privileges": [
|
||||
"No external mutation may bypass existing Authority -> Commit -> Executor boundaries.",
|
||||
"Do not weaken architecture/security/test guardrails to make the task pass."
|
||||
],
|
||||
"persistence_and_migrations": [
|
||||
"No database schema change is authorized by this packet unless a revised task explicitly grants database/migration scope."
|
||||
],
|
||||
"failure_retry_restart": [
|
||||
"Rollback rule: disable perspective/team routing; tasks execute single path",
|
||||
"Retries/restarts must not create duplicate external side effects or a second canonical writer.",
|
||||
"Durable state required by this task must survive process restart; process-local memory is never the only source of truth."
|
||||
],
|
||||
"assumptions": [
|
||||
"Program milestone: M4; test profile: FAST.",
|
||||
"Task branch starts from local gen2-integration; base_repo_revision=gen2-integration must resolve to the untouched integration base while the task branch is active.",
|
||||
"Codex reads DOMOVOY_CONSTITUTION.md, GEN2_TARGET_ARCHITECTURE.md, GEN2_CANONICAL_DATA_MODEL.md, GEN2_MIGRATION_MAP.md, GEN2_MASTER_PLAN.md and GEN2_EXECUTION_CONTRACT.md before non-trivial edits.",
|
||||
"Do not expand scope silently. If a required change falls outside allowed paths or changes ownership, return a typed gap and request a task revision.",
|
||||
"Depends on: GEN2-017."
|
||||
],
|
||||
"acceptance_scenarios": [
|
||||
"GEN2-018-SCN-01: PerspectiveSpec = objectives/methods/knowledge profile/evaluation criteria",
|
||||
"GEN2-018-SCN-02: dynamic specialist assembly from Perspective + Focus + Skills + tools",
|
||||
"GEN2-018-SCN-03: work decomposition by independent subtasks",
|
||||
"GEN2-018-SCN-04: structured findings/evidence/risks/artifacts",
|
||||
"GEN2-018-SCN-05: critic/adversarial perspective optional",
|
||||
"GEN2-018-SCN-06: criteria when team is justified vs single reasoning path",
|
||||
"GEN2-018-SCN-07: не передавать каждому агенту весь глобальный context",
|
||||
"GEN2-018-EXIT: «спроектируй систему» может вызвать несколько независимых перспектив; «исправь функцию» остаётся одной задачей."
|
||||
],
|
||||
"required_tests": [
|
||||
"FAST: go test ./modules/work/... ./modules/capability/... ./cmd/work-graph-runtime/...",
|
||||
"FAST: go vet ./modules/work/... ./modules/capability/... ./cmd/work-graph-runtime/...",
|
||||
"FAST governance: go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json",
|
||||
"FAST scope: go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json",
|
||||
"FAST independent: go run ./cmd/devctl/independent",
|
||||
"FAST whitespace: git diff --check gen2-integration --"
|
||||
],
|
||||
"terminal_gap_states": [
|
||||
"BLOCKED_BY_SPEC_GAP",
|
||||
"BLOCKED_BY_ARCHITECTURE_GAP",
|
||||
"BLOCKED_BY_MISSING_CAPABILITY",
|
||||
"BLOCKED_BY_CONFLICT",
|
||||
"REQUIRES_MIGRATION_DECISION"
|
||||
]
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
{
|
||||
"task_id": "GEN2-019",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ARCH-GEN2-000",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
"intent": "Оценивать последствия, обратимость, cost-of-error и mitigation до значимого действия, но не превращать систему в «нельзя».",
|
||||
"requirement_ids": [
|
||||
"REQ-GEN2-019-GOAL",
|
||||
"REQ-GEN2-019-BOUNDARY",
|
||||
"REQ-GEN2-019-GATE"
|
||||
],
|
||||
"allowed_change_scope": {
|
||||
"modules": [
|
||||
"work",
|
||||
"authority"
|
||||
],
|
||||
"paths": [
|
||||
"modules/work/**",
|
||||
"contracts/work/**",
|
||||
"contracts/authority/**",
|
||||
"modules/authority/**",
|
||||
"tests/**",
|
||||
"dev/ACTIVE_TASK.json",
|
||||
"dev/tasks/GEN2-019-add-consequence-risk-assessment-without-artificial-capability-blacklist.json"
|
||||
]
|
||||
},
|
||||
"forbidden_change_scope": {
|
||||
"paths": [
|
||||
"security/**",
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
"architecture/**",
|
||||
".gitea/workflows/**",
|
||||
"deploy/**",
|
||||
"database/**",
|
||||
"adapters/models/**"
|
||||
]
|
||||
},
|
||||
"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": [
|
||||
"contracts.work@gen2",
|
||||
"contracts.authority@v1",
|
||||
"contracts.commit@v1"
|
||||
],
|
||||
"may_change": [
|
||||
"Only contracts explicitly within GEN2-019 allowed_change_scope; preserve Gen2 owner boundaries and backward compatibility unless this packet says cutover."
|
||||
]
|
||||
},
|
||||
"semantic_owner": "Work assessment feeding Authority/Commit. Follow GEN2 target architecture: owner boundaries are authoritative; Memory remains a read/retrieval fabric, World is not truth owner, SELF is not a God orchestrator.",
|
||||
"canonical_state": [
|
||||
"Work writes assessment; Authority remains grant decision owner",
|
||||
"No task may create a second canonical owner for an already-owned mutable semantic concept."
|
||||
],
|
||||
"state_transitions": [
|
||||
"task input -> bounded Gen2 change -> FAST verification -> merge to gen2-integration; canonical writer rule: Work writes assessment; Authority remains grant decision owner"
|
||||
],
|
||||
"side_effects_and_privileges": [
|
||||
"No external mutation may bypass existing Authority -> Commit -> Executor boundaries.",
|
||||
"Do not weaken architecture/security/test guardrails to make the task pass.",
|
||||
"Risk assessment may recommend mitigations but must not invent a blanket capability blacklist or override authenticated owner intent."
|
||||
],
|
||||
"persistence_and_migrations": [
|
||||
"No database schema change is authorized by this packet unless a revised task explicitly grants database/migration scope."
|
||||
],
|
||||
"failure_retry_restart": [
|
||||
"Rollback rule: risk assessment advisory disable; strict existing Authority unchanged",
|
||||
"Retries/restarts must not create duplicate external side effects or a second canonical writer.",
|
||||
"Durable state required by this task must survive process restart; process-local memory is never the only source of truth."
|
||||
],
|
||||
"assumptions": [
|
||||
"Program milestone: M4; test profile: FAST.",
|
||||
"Task branch starts from local gen2-integration; base_repo_revision=gen2-integration must resolve to the untouched integration base while the task branch is active.",
|
||||
"Codex reads DOMOVOY_CONSTITUTION.md, GEN2_TARGET_ARCHITECTURE.md, GEN2_CANONICAL_DATA_MODEL.md, GEN2_MIGRATION_MAP.md, GEN2_MASTER_PLAN.md and GEN2_EXECUTION_CONTRACT.md before non-trivial edits.",
|
||||
"Do not expand scope silently. If a required change falls outside allowed paths or changes ownership, return a typed gap and request a task revision.",
|
||||
"Depends on: GEN2-018."
|
||||
],
|
||||
"acceptance_scenarios": [
|
||||
"GEN2-019-SCN-01: ConsequenceAssessment",
|
||||
"GEN2-019-SCN-02: risk dimensions: impact, reversibility, uncertainty, scope, recoverability",
|
||||
"GEN2-019-SCN-03: mitigation: backup/snapshot/dry-run/canary/extra confirmation",
|
||||
"GEN2-019-SCN-04: clarification policy based on uncertainty × cost_of_wrong_action × irreversibility",
|
||||
"GEN2-019-SCN-05: owner may explicitly accept high risk after warning",
|
||||
"GEN2-019-SCN-06: consequence result becomes input Authority/Commit, не replacement",
|
||||
"GEN2-019-EXIT: Домовой предупреждает о последствиях и предлагает rollback plan, но не самовольно запрещает владельцу разрешённое действие."
|
||||
],
|
||||
"required_tests": [
|
||||
"FAST: go test ./modules/work/... ./modules/authority/...",
|
||||
"FAST: go vet ./modules/work/... ./modules/authority/...",
|
||||
"FAST governance: go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json",
|
||||
"FAST scope: go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json",
|
||||
"FAST independent: go run ./cmd/devctl/independent",
|
||||
"FAST whitespace: git diff --check gen2-integration --"
|
||||
],
|
||||
"terminal_gap_states": [
|
||||
"BLOCKED_BY_SPEC_GAP",
|
||||
"BLOCKED_BY_ARCHITECTURE_GAP",
|
||||
"BLOCKED_BY_MISSING_CAPABILITY",
|
||||
"BLOCKED_BY_CONFLICT",
|
||||
"REQUIRES_MIGRATION_DECISION"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
{
|
||||
"task_id": "GEN2-020",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ARCH-GEN2-000",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
"intent": "Не считать «API 200» доказательством того, что пользовательская цель достигнута.",
|
||||
"requirement_ids": [
|
||||
"REQ-GEN2-020-GOAL",
|
||||
"REQ-GEN2-020-BOUNDARY",
|
||||
"REQ-GEN2-020-GATE"
|
||||
],
|
||||
"allowed_change_scope": {
|
||||
"modules": [
|
||||
"commit",
|
||||
"epistemics",
|
||||
"continuity"
|
||||
],
|
||||
"paths": [
|
||||
"modules/commit/**",
|
||||
"contracts/commit/**",
|
||||
"modules/epistemics/**",
|
||||
"contracts/epistemics/**",
|
||||
"modules/continuity/**",
|
||||
"contracts/continuity/**",
|
||||
"adapters/postgres/commit_repository.go",
|
||||
"adapters/postgres/epistemics_repository.go",
|
||||
"database/migrations/*.sql",
|
||||
"tests/**",
|
||||
"dev/ACTIVE_TASK.json",
|
||||
"dev/tasks/GEN2-020-separate-execution-receipt-from-verified-world-outcome.json"
|
||||
]
|
||||
},
|
||||
"forbidden_change_scope": {
|
||||
"paths": [
|
||||
"security/**",
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
"architecture/**",
|
||||
".gitea/workflows/**",
|
||||
"deploy/**",
|
||||
"adapters/models/**"
|
||||
]
|
||||
},
|
||||
"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": [
|
||||
"contracts.commit@v1",
|
||||
"contracts.epistemics@gen2",
|
||||
"contracts.continuity@v1",
|
||||
"contracts.work@gen2"
|
||||
],
|
||||
"may_change": [
|
||||
"Only contracts explicitly within GEN2-020 allowed_change_scope; preserve Gen2 owner boundaries and backward compatibility unless this packet says cutover."
|
||||
]
|
||||
},
|
||||
"semantic_owner": "Commit + Epistemics. Follow GEN2 target architecture: owner boundaries are authoritative; Memory remains a read/retrieval fabric, World is not truth owner, SELF is not a God orchestrator.",
|
||||
"canonical_state": [
|
||||
"Commit writer unchanged for execution facts; Epistemics writes world assessment",
|
||||
"No task may create a second canonical owner for an already-owned mutable semantic concept."
|
||||
],
|
||||
"state_transitions": [
|
||||
"current state -> additive/shadow Gen2 path -> evidence comparison -> controlled cutover -> compatibility retention; writer rule: Commit writer unchanged for execution facts; Epistemics writes world assessment"
|
||||
],
|
||||
"side_effects_and_privileges": [
|
||||
"No external mutation may bypass existing Authority -> Commit -> Executor boundaries.",
|
||||
"Do not weaken architecture/security/test guardrails to make the task pass."
|
||||
],
|
||||
"persistence_and_migrations": [
|
||||
"Database changes must be additive next-numbered migrations only until an explicitly approved retirement task; never edit historical V1 migrations 001-045.",
|
||||
"At most one canonical writer is active for each semantic state; backfill/shadow reads do not create a second canonical writer.",
|
||||
"The positive new_database_owner_roles delta in this packet is a devctl-v1 database-surface compatibility gate; it does not authorize cross-owner SQL/FK/JOIN or an extra owner unless explicitly stated by this task."
|
||||
],
|
||||
"failure_retry_restart": [
|
||||
"Rollback rule: old Commit status still readable; new assessments additive",
|
||||
"Retries/restarts must not create duplicate external side effects or a second canonical writer.",
|
||||
"Durable state required by this task must survive process restart; process-local memory is never the only source of truth.",
|
||||
"Cutover must keep the previous read/compatibility path available until the task or milestone explicitly proves retirement."
|
||||
],
|
||||
"assumptions": [
|
||||
"Program milestone: M4; test profile: FAST.",
|
||||
"Task branch starts from local gen2-integration; base_repo_revision=gen2-integration must resolve to the untouched integration base while the task branch is active.",
|
||||
"Codex reads DOMOVOY_CONSTITUTION.md, GEN2_TARGET_ARCHITECTURE.md, GEN2_CANONICAL_DATA_MODEL.md, GEN2_MIGRATION_MAP.md, GEN2_MASTER_PLAN.md and GEN2_EXECUTION_CONTRACT.md before non-trivial edits.",
|
||||
"Do not expand scope silently. If a required change falls outside allowed paths or changes ownership, return a typed gap and request a task revision.",
|
||||
"Depends on: GEN2-019."
|
||||
],
|
||||
"acceptance_scenarios": [
|
||||
"GEN2-020-SCN-01: ExecutionReceipt остаётся Commit-owned факт попытки",
|
||||
"GEN2-020-SCN-02: WorldOutcomeAssessment становится Epistemics-owned verification",
|
||||
"GEN2-020-SCN-03: связать expected effect / observation / evidence",
|
||||
"GEN2-020-SCN-04: поддержать UNKNOWN/PARTIAL/CONTRADICTED outcome",
|
||||
"GEN2-020-SCN-05: не выполнять destructive retry только потому, что verification отсутствует",
|
||||
"GEN2-020-SCN-06: Continuity может создать follow-up/attention при неизвестном Outcome",
|
||||
"GEN2-020-EXIT: ответ «выполнено» возможен только когда семантика операции это позволяет; неизвестность честно отражается."
|
||||
],
|
||||
"required_tests": [
|
||||
"FAST: go test ./modules/commit/... ./modules/epistemics/... ./modules/continuity/... ./adapters/postgres",
|
||||
"FAST: go vet ./modules/commit/... ./modules/epistemics/... ./modules/continuity/... ./adapters/postgres",
|
||||
"FAST governance: go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json",
|
||||
"FAST scope: go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json",
|
||||
"FAST independent: go run ./cmd/devctl/independent",
|
||||
"FAST whitespace: git diff --check gen2-integration --"
|
||||
],
|
||||
"terminal_gap_states": [
|
||||
"BLOCKED_BY_SPEC_GAP",
|
||||
"BLOCKED_BY_ARCHITECTURE_GAP",
|
||||
"BLOCKED_BY_MISSING_CAPABILITY",
|
||||
"BLOCKED_BY_CONFLICT",
|
||||
"REQUIRES_MIGRATION_DECISION"
|
||||
]
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
{
|
||||
"task_id": "GEN2-021",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ARCH-GEN2-000",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
"intent": "Самоизменение Домового не должно уметь уничтожить единственный механизм собственного отката.",
|
||||
"requirement_ids": [
|
||||
"REQ-GEN2-021-GOAL",
|
||||
"REQ-GEN2-021-BOUNDARY",
|
||||
"REQ-GEN2-021-GATE"
|
||||
],
|
||||
"allowed_change_scope": {
|
||||
"modules": [],
|
||||
"paths": [
|
||||
"runtime/supervisor/**",
|
||||
"cmd/supervisor/**",
|
||||
"deploy/**",
|
||||
"runtime/health/**",
|
||||
"scripts/**",
|
||||
"tests/**",
|
||||
"dev/ACTIVE_TASK.json",
|
||||
"dev/tasks/GEN2-021-add-independent-supervisor-lifeboat-for-release-health-and-rollback.json"
|
||||
]
|
||||
},
|
||||
"forbidden_change_scope": {
|
||||
"paths": [
|
||||
"security/**",
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
"architecture/**",
|
||||
".gitea/workflows/**",
|
||||
"database/**",
|
||||
"adapters/models/**"
|
||||
]
|
||||
},
|
||||
"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": [
|
||||
"contracts.commit@v1",
|
||||
"contracts.configuration@gen2"
|
||||
],
|
||||
"may_change": []
|
||||
},
|
||||
"semantic_owner": "Runtime only. Follow GEN2 target architecture: owner boundaries are authoritative; Memory remains a read/retrieval fabric, World is not truth owner, SELF is not a God orchestrator.",
|
||||
"canonical_state": [
|
||||
"Supervisor writes runtime release state only",
|
||||
"No task may create a second canonical owner for an already-owned mutable semantic concept."
|
||||
],
|
||||
"state_transitions": [
|
||||
"task input -> bounded Gen2 change -> FAST verification -> merge to gen2-integration; canonical writer rule: Supervisor writes runtime release state only"
|
||||
],
|
||||
"side_effects_and_privileges": [
|
||||
"No external mutation may bypass existing Authority -> Commit -> Executor boundaries.",
|
||||
"Do not weaken architecture/security/test guardrails to make the task pass.",
|
||||
"Deploy/release surface changes are allowed only for the bounded purpose of this packet; production promotion requires the packet's stated evidence and rollback path.",
|
||||
"Supervisor/Lifeboat must remain operationally independent from the candidate runtime it watches."
|
||||
],
|
||||
"persistence_and_migrations": [
|
||||
"No database schema change is authorized by this packet unless a revised task explicitly grants database/migration scope."
|
||||
],
|
||||
"failure_retry_restart": [
|
||||
"Rollback rule: Supervisor itself versioned conservatively; previous release remains bootable",
|
||||
"Retries/restarts must not create duplicate external side effects or a second canonical writer.",
|
||||
"Durable state required by this task must survive process restart; process-local memory is never the only source of truth."
|
||||
],
|
||||
"assumptions": [
|
||||
"Program milestone: M4; test profile: FAST.",
|
||||
"Task branch starts from local gen2-integration; base_repo_revision=gen2-integration must resolve to the untouched integration base while the task branch is active.",
|
||||
"Codex reads DOMOVOY_CONSTITUTION.md, GEN2_TARGET_ARCHITECTURE.md, GEN2_CANONICAL_DATA_MODEL.md, GEN2_MIGRATION_MAP.md, GEN2_MASTER_PLAN.md and GEN2_EXECUTION_CONTRACT.md before non-trivial edits.",
|
||||
"Do not expand scope silently. If a required change falls outside allowed paths or changes ownership, return a typed gap and request a task revision.",
|
||||
"Depends on: GEN2-020."
|
||||
],
|
||||
"acceptance_scenarios": [
|
||||
"GEN2-021-SCN-01: candidate release start",
|
||||
"GEN2-021-SCN-02: health/readiness contract",
|
||||
"GEN2-021-SCN-03: controlled traffic/switch",
|
||||
"GEN2-021-SCN-04: watchdog observation",
|
||||
"GEN2-021-SCN-05: automatic rollback to last known good",
|
||||
"GEN2-021-SCN-06: persist release/switch evidence outside candidate process dependency",
|
||||
"GEN2-021-SCN-07: emergency manual control path",
|
||||
"GEN2-021-SCN-08: не давать Supervisor доступ к semantic decisions пользователя",
|
||||
"GEN2-021-EXIT: сломанный candidate Assistant не блокирует восстановление последней рабочей версии."
|
||||
],
|
||||
"required_tests": [
|
||||
"FAST: go test ./runtime/supervisor/... ./runtime/health/... ./cmd/supervisor/...",
|
||||
"FAST: go vet ./runtime/supervisor/... ./runtime/health/... ./cmd/supervisor/...",
|
||||
"FAST governance: go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json",
|
||||
"FAST scope: go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json",
|
||||
"FAST independent: go run ./cmd/devctl/independent",
|
||||
"FAST whitespace: git diff --check gen2-integration --"
|
||||
],
|
||||
"terminal_gap_states": [
|
||||
"BLOCKED_BY_SPEC_GAP",
|
||||
"BLOCKED_BY_ARCHITECTURE_GAP",
|
||||
"BLOCKED_BY_MISSING_CAPABILITY",
|
||||
"BLOCKED_BY_CONFLICT",
|
||||
"REQUIRES_MIGRATION_DECISION"
|
||||
]
|
||||
}
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
{
|
||||
"task_id": "GEN2-022",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ARCH-GEN2-000",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
"intent": "Замкнуть цикл weakness → proposal → candidate → evaluator → canary → promote/rollback без автономного обхода governance.",
|
||||
"requirement_ids": [
|
||||
"REQ-GEN2-022-GOAL",
|
||||
"REQ-GEN2-022-BOUNDARY",
|
||||
"REQ-GEN2-022-GATE"
|
||||
],
|
||||
"allowed_change_scope": {
|
||||
"modules": [
|
||||
"work",
|
||||
"self",
|
||||
"capability"
|
||||
],
|
||||
"paths": [
|
||||
"contracts/work/**",
|
||||
"modules/work/**",
|
||||
"adapters/developmentexecutor/**",
|
||||
"modules/self/**",
|
||||
"contracts/self/**",
|
||||
"modules/capability/**",
|
||||
"contracts/capability/**",
|
||||
"runtime/supervisor/**",
|
||||
"cmd/work-graph-runtime/**",
|
||||
"tests/**",
|
||||
"dev/ACTIVE_TASK.json",
|
||||
"dev/tasks/GEN2-022-wire-governed-self-evolution-loop-using-existing-development-gates.json"
|
||||
]
|
||||
},
|
||||
"forbidden_change_scope": {
|
||||
"paths": [
|
||||
"security/**",
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
"architecture/**",
|
||||
".gitea/workflows/**",
|
||||
"deploy/**",
|
||||
"database/**",
|
||||
"adapters/models/**"
|
||||
]
|
||||
},
|
||||
"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": [
|
||||
"contracts.work@gen2",
|
||||
"contracts.self@gen2",
|
||||
"contracts.capability@gen2"
|
||||
],
|
||||
"may_change": [
|
||||
"Only contracts explicitly within GEN2-022 allowed_change_scope; preserve Gen2 owner boundaries and backward compatibility unless this packet says cutover."
|
||||
]
|
||||
},
|
||||
"semantic_owner": "Work + SELF evidence + Supervisor promotion. Follow GEN2 target architecture: owner boundaries are authoritative; Memory remains a read/retrieval fabric, World is not truth owner, SELF is not a God orchestrator.",
|
||||
"canonical_state": [
|
||||
"existing governed-development writer remains; SELF proposes/evaluates, Supervisor handles release state",
|
||||
"No task may create a second canonical owner for an already-owned mutable semantic concept."
|
||||
],
|
||||
"state_transitions": [
|
||||
"current state -> additive/shadow Gen2 path -> evidence comparison -> controlled cutover -> compatibility retention; writer rule: existing governed-development writer remains; SELF proposes/evaluates, Supervisor handles release state"
|
||||
],
|
||||
"side_effects_and_privileges": [
|
||||
"No external mutation may bypass existing Authority -> Commit -> Executor boundaries.",
|
||||
"Do not weaken architecture/security/test guardrails to make the task pass.",
|
||||
"Self-evolution may propose/build/test candidates but promotion remains governed and independently evaluated."
|
||||
],
|
||||
"persistence_and_migrations": [
|
||||
"No database schema change is authorized by this packet unless a revised task explicitly grants database/migration scope."
|
||||
],
|
||||
"failure_retry_restart": [
|
||||
"Rollback rule: existing development gates remain; autonomous promotion feature switch off",
|
||||
"Retries/restarts must not create duplicate external side effects or a second canonical writer.",
|
||||
"Durable state required by this task must survive process restart; process-local memory is never the only source of truth."
|
||||
],
|
||||
"assumptions": [
|
||||
"Program milestone: M4; test profile: MILESTONE_RECOVERY.",
|
||||
"Task branch starts from local gen2-integration; base_repo_revision=gen2-integration must resolve to the untouched integration base while the task branch is active.",
|
||||
"Codex reads DOMOVOY_CONSTITUTION.md, GEN2_TARGET_ARCHITECTURE.md, GEN2_CANONICAL_DATA_MODEL.md, GEN2_MIGRATION_MAP.md, GEN2_MASTER_PLAN.md and GEN2_EXECUTION_CONTRACT.md before non-trivial edits.",
|
||||
"Do not expand scope silently. If a required change falls outside allowed paths or changes ownership, return a typed gap and request a task revision.",
|
||||
"Depends on: GEN2-021."
|
||||
],
|
||||
"acceptance_scenarios": [
|
||||
"GEN2-022-SCN-01: SELF ImprovementNeed -> Work development candidate",
|
||||
"GEN2-022-SCN-02: expected improvement + must-not-worsen criteria",
|
||||
"GEN2-022-SCN-03: Creator != Evaluator enforced",
|
||||
"GEN2-022-SCN-04: historical replay/personal benchmark",
|
||||
"GEN2-022-SCN-05: sandbox/candidate build",
|
||||
"GEN2-022-SCN-06: shadow/canary where appropriate",
|
||||
"GEN2-022-SCN-07: Supervisor promotion/rollback",
|
||||
"GEN2-022-SCN-08: LearningLedger records accepted/rejected change and actual outcome",
|
||||
"GEN2-022-SCN-09: уровни change: knowledge, behavior, capability, core — с возрастающей строгостью",
|
||||
"GEN2-022-EXIT: Домовой способен доказуемо улучшить компонент и объяснить историю изменения, но не тайно переписать core."
|
||||
],
|
||||
"required_tests": [
|
||||
"FAST: go test ./modules/work/... ./modules/self/... ./modules/capability/... ./adapters/developmentexecutor/... ./runtime/supervisor/...",
|
||||
"FAST: go vet ./modules/work/... ./modules/self/... ./modules/capability/... ./adapters/developmentexecutor/... ./runtime/supervisor/...",
|
||||
"FAST governance: go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json",
|
||||
"FAST scope: go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json",
|
||||
"FAST independent: go run ./cmd/devctl/independent",
|
||||
"FAST whitespace: git diff --check gen2-integration --",
|
||||
"MILESTONE M4: go test ./modules/work/... ./modules/commit/... ./modules/epistemics/... ./modules/continuity/... ./runtime/supervisor/... ./cmd/work-graph-runtime/... ./cmd/research-runtime/...",
|
||||
"MILESTONE M4 recovery: run focused restart/checkpoint/unknown-outcome/rollback drill; no automatic go test ./... if M3 FULL remains fresh and no global schema cutover occurred."
|
||||
],
|
||||
"terminal_gap_states": [
|
||||
"BLOCKED_BY_SPEC_GAP",
|
||||
"BLOCKED_BY_ARCHITECTURE_GAP",
|
||||
"BLOCKED_BY_MISSING_CAPABILITY",
|
||||
"BLOCKED_BY_CONFLICT",
|
||||
"REQUIRES_MIGRATION_DECISION"
|
||||
]
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
{
|
||||
"task_id": "GEN2-023",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ARCH-GEN2-000",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
"intent": "Пользователь говорит, чего хочет; Домовой меняет scoped configuration/starts work through normal semantics instead of forcing admin panels.",
|
||||
"requirement_ids": [
|
||||
"REQ-GEN2-023-GOAL",
|
||||
"REQ-GEN2-023-BOUNDARY",
|
||||
"REQ-GEN2-023-GATE"
|
||||
],
|
||||
"allowed_change_scope": {
|
||||
"modules": [
|
||||
"interaction",
|
||||
"configuration",
|
||||
"work",
|
||||
"authority"
|
||||
],
|
||||
"paths": [
|
||||
"modules/interaction/**",
|
||||
"contracts/interaction/**",
|
||||
"cmd/core-runtime/**",
|
||||
"cmd/creator-web/**",
|
||||
"modules/configuration/**",
|
||||
"contracts/configuration/**",
|
||||
"modules/work/**",
|
||||
"contracts/work/**",
|
||||
"contracts/authority/**",
|
||||
"tests/**",
|
||||
"dev/ACTIVE_TASK.json",
|
||||
"dev/tasks/GEN2-023-make-conversational-control-the-primary-configuration-and-operational-ux.json"
|
||||
]
|
||||
},
|
||||
"forbidden_change_scope": {
|
||||
"paths": [
|
||||
"security/**",
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
"architecture/**",
|
||||
".gitea/workflows/**",
|
||||
"deploy/**",
|
||||
"database/**",
|
||||
"adapters/models/**"
|
||||
]
|
||||
},
|
||||
"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": [
|
||||
"contracts.interaction@v1",
|
||||
"contracts.configuration@gen2",
|
||||
"contracts.work@gen2",
|
||||
"contracts.authority@v1"
|
||||
],
|
||||
"may_change": [
|
||||
"Only contracts explicitly within GEN2-023 allowed_change_scope; preserve Gen2 owner boundaries and backward compatibility unless this packet says cutover."
|
||||
]
|
||||
},
|
||||
"semantic_owner": "Interaction + Configuration/Work contracts. Follow GEN2 target architecture: owner boundaries are authoritative; Memory remains a read/retrieval fabric, World is not truth owner, SELF is not a God orchestrator.",
|
||||
"canonical_state": [
|
||||
"Configuration remains setting writer; Interaction writes user instruction/intent only",
|
||||
"No task may create a second canonical owner for an already-owned mutable semantic concept."
|
||||
],
|
||||
"state_transitions": [
|
||||
"current state -> additive/shadow Gen2 path -> evidence comparison -> controlled cutover -> compatibility retention; writer rule: Configuration remains setting writer; Interaction writes user instruction/intent only"
|
||||
],
|
||||
"side_effects_and_privileges": [
|
||||
"No external mutation may bypass existing Authority -> Commit -> Executor boundaries.",
|
||||
"Do not weaken architecture/security/test guardrails to make the task pass."
|
||||
],
|
||||
"persistence_and_migrations": [
|
||||
"No database schema change is authorized by this packet unless a revised task explicitly grants database/migration scope."
|
||||
],
|
||||
"failure_retry_restart": [
|
||||
"Rollback rule: admin/read APIs remain available; conversational mutations can be disabled",
|
||||
"Retries/restarts must not create duplicate external side effects or a second canonical writer.",
|
||||
"Durable state required by this task must survive process restart; process-local memory is never the only source of truth."
|
||||
],
|
||||
"assumptions": [
|
||||
"Program milestone: M5; test profile: FAST.",
|
||||
"Task branch starts from local gen2-integration; base_repo_revision=gen2-integration must resolve to the untouched integration base while the task branch is active.",
|
||||
"Codex reads DOMOVOY_CONSTITUTION.md, GEN2_TARGET_ARCHITECTURE.md, GEN2_CANONICAL_DATA_MODEL.md, GEN2_MIGRATION_MAP.md, GEN2_MASTER_PLAN.md and GEN2_EXECUTION_CONTRACT.md before non-trivial edits.",
|
||||
"Do not expand scope silently. If a required change falls outside allowed paths or changes ownership, return a typed gap and request a task revision.",
|
||||
"Depends on: GEN2-022."
|
||||
],
|
||||
"acceptance_scenarios": [
|
||||
"GEN2-023-SCN-01: intent patterns for setting/querying configuration",
|
||||
"GEN2-023-SCN-02: explain effective scope/value in natural language",
|
||||
"GEN2-023-SCN-03: one-time override vs durable preference distinction",
|
||||
"GEN2-023-SCN-04: conversation creates SettingAssignment only through Configuration contract",
|
||||
"GEN2-023-SCN-05: dangerous config/core changes route through Consequence + Authority/Commit",
|
||||
"GEN2-023-SCN-06: Creator Web admin screens остаются diagnostics/emergency, не primary UX",
|
||||
"GEN2-023-EXIT: обычные настройки не требуют ручного редактирования ENV/admin UI."
|
||||
],
|
||||
"required_tests": [
|
||||
"FAST: go test ./modules/interaction/... ./modules/configuration/... ./modules/work/... ./cmd/core-runtime/... ./cmd/creator-web/...",
|
||||
"FAST: go vet ./modules/interaction/... ./modules/configuration/... ./modules/work/... ./cmd/core-runtime/... ./cmd/creator-web/...",
|
||||
"FAST governance: go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json",
|
||||
"FAST scope: go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json",
|
||||
"FAST independent: go run ./cmd/devctl/independent",
|
||||
"FAST whitespace: git diff --check gen2-integration --"
|
||||
],
|
||||
"terminal_gap_states": [
|
||||
"BLOCKED_BY_SPEC_GAP",
|
||||
"BLOCKED_BY_ARCHITECTURE_GAP",
|
||||
"BLOCKED_BY_MISSING_CAPABILITY",
|
||||
"BLOCKED_BY_CONFLICT",
|
||||
"REQUIRES_MIGRATION_DECISION"
|
||||
]
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
{
|
||||
"task_id": "GEN2-024",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ARCH-GEN2-000",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
"intent": "Не смешивать владельца, членов семьи, гостей, голосовые источники и shared/private memory.",
|
||||
"requirement_ids": [
|
||||
"REQ-GEN2-024-GOAL",
|
||||
"REQ-GEN2-024-BOUNDARY",
|
||||
"REQ-GEN2-024-GATE"
|
||||
],
|
||||
"allowed_change_scope": {
|
||||
"modules": [
|
||||
"identity",
|
||||
"world",
|
||||
"epistemics",
|
||||
"interaction"
|
||||
],
|
||||
"paths": [
|
||||
"modules/identity/**",
|
||||
"contracts/identity/**",
|
||||
"modules/world/**",
|
||||
"contracts/world/**",
|
||||
"modules/epistemics/**",
|
||||
"contracts/epistemics/**",
|
||||
"modules/interaction/**",
|
||||
"contracts/interaction/**",
|
||||
"adapters/postgres/identity_repository.go",
|
||||
"adapters/postgres/world_repository.go",
|
||||
"adapters/postgres/epistemics_repository.go",
|
||||
"adapters/postgres/interaction_repository.go",
|
||||
"database/migrations/*.sql",
|
||||
"tests/**",
|
||||
"dev/ACTIVE_TASK.json",
|
||||
"dev/tasks/GEN2-024-multi-person-realms-identity-boundaries-and-contextual-source-trust.json"
|
||||
]
|
||||
},
|
||||
"forbidden_change_scope": {
|
||||
"paths": [
|
||||
"security/**",
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
"architecture/**",
|
||||
".gitea/workflows/**",
|
||||
"deploy/**",
|
||||
"adapters/models/**"
|
||||
]
|
||||
},
|
||||
"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": [
|
||||
"contracts.identity@v1",
|
||||
"contracts.world@gen2",
|
||||
"contracts.epistemics@gen2",
|
||||
"contracts.interaction@v1"
|
||||
],
|
||||
"may_change": [
|
||||
"Only contracts explicitly within GEN2-024 allowed_change_scope; preserve Gen2 owner boundaries and backward compatibility unless this packet says cutover."
|
||||
]
|
||||
},
|
||||
"semantic_owner": "Identity + World + Epistemics, separate responsibilities. Follow GEN2 target architecture: owner boundaries are authoritative; Memory remains a read/retrieval fabric, World is not truth owner, SELF is not a God orchestrator.",
|
||||
"canonical_state": [
|
||||
"Identity authenticates principals; World resolves referents; Epistemics evaluates source reliability",
|
||||
"No task may create a second canonical owner for an already-owned mutable semantic concept."
|
||||
],
|
||||
"state_transitions": [
|
||||
"current state -> additive/shadow Gen2 path -> evidence comparison -> controlled cutover -> compatibility retention; writer rule: Identity authenticates principals; World resolves referents; Epistemics evaluates source reliability"
|
||||
],
|
||||
"side_effects_and_privileges": [
|
||||
"No external mutation may bypass existing Authority -> Commit -> Executor boundaries.",
|
||||
"Do not weaken architecture/security/test guardrails to make the task pass."
|
||||
],
|
||||
"persistence_and_migrations": [
|
||||
"Database changes must be additive next-numbered migrations only until an explicitly approved retirement task; never edit historical V1 migrations 001-045.",
|
||||
"At most one canonical writer is active for each semantic state; backfill/shadow reads do not create a second canonical writer.",
|
||||
"The positive new_database_owner_roles delta in this packet is a devctl-v1 database-surface compatibility gate; it does not authorize cross-owner SQL/FK/JOIN or an extra owner unless explicitly stated by this task."
|
||||
],
|
||||
"failure_retry_restart": [
|
||||
"Rollback rule: single-owner mode remains possible; new realm metadata additive",
|
||||
"Retries/restarts must not create duplicate external side effects or a second canonical writer.",
|
||||
"Durable state required by this task must survive process restart; process-local memory is never the only source of truth."
|
||||
],
|
||||
"assumptions": [
|
||||
"Program milestone: M5; test profile: FAST.",
|
||||
"Task branch starts from local gen2-integration; base_repo_revision=gen2-integration must resolve to the untouched integration base while the task branch is active.",
|
||||
"Codex reads DOMOVOY_CONSTITUTION.md, GEN2_TARGET_ARCHITECTURE.md, GEN2_CANONICAL_DATA_MODEL.md, GEN2_MIGRATION_MAP.md, GEN2_MASTER_PLAN.md and GEN2_EXECUTION_CONTRACT.md before non-trivial edits.",
|
||||
"Do not expand scope silently. If a required change falls outside allowed paths or changes ownership, return a typed gap and request a task revision.",
|
||||
"Depends on: GEN2-023."
|
||||
],
|
||||
"acceptance_scenarios": [
|
||||
"GEN2-024-SCN-01: PrincipalDataRealm/owner/shared scopes",
|
||||
"GEN2-024-SCN-02: utterance/source attribution with confidence",
|
||||
"GEN2-024-SCN-03: World person referents do not equal authenticated Principal automatically",
|
||||
"GEN2-024-SCN-04: SourceReliabilityAssessment = source × domain × freshness × history",
|
||||
"GEN2-024-SCN-05: identity ambiguity remains explicit",
|
||||
"GEN2-024-SCN-06: future speaker-recognition result enters as Observation, not authentication truth",
|
||||
"GEN2-024-SCN-07: Context Compiler respects realm/scope isolation",
|
||||
"GEN2-024-EXIT: Домовой может знать семейный контекст, не превращая услышанное имя/голос в полномочия владельца."
|
||||
],
|
||||
"required_tests": [
|
||||
"FAST: go test ./modules/identity/... ./modules/world/... ./modules/epistemics/... ./modules/interaction/... ./adapters/postgres",
|
||||
"FAST: go vet ./modules/identity/... ./modules/world/... ./modules/epistemics/... ./modules/interaction/... ./adapters/postgres",
|
||||
"FAST governance: go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json",
|
||||
"FAST scope: go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json",
|
||||
"FAST independent: go run ./cmd/devctl/independent",
|
||||
"FAST whitespace: git diff --check gen2-integration --"
|
||||
],
|
||||
"terminal_gap_states": [
|
||||
"BLOCKED_BY_SPEC_GAP",
|
||||
"BLOCKED_BY_ARCHITECTURE_GAP",
|
||||
"BLOCKED_BY_MISSING_CAPABILITY",
|
||||
"BLOCKED_BY_CONFLICT",
|
||||
"REQUIRES_MIGRATION_DECISION"
|
||||
]
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
{
|
||||
"task_id": "GEN2-025",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ARCH-GEN2-000",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
"intent": "Закрепить единый путь для text/audio/image/video/device observations, чтобы будущие микрофоны/камеры не потребовали нового ядра.",
|
||||
"requirement_ids": [
|
||||
"REQ-GEN2-025-GOAL",
|
||||
"REQ-GEN2-025-BOUNDARY",
|
||||
"REQ-GEN2-025-GATE"
|
||||
],
|
||||
"allowed_change_scope": {
|
||||
"modules": [
|
||||
"interaction",
|
||||
"capability",
|
||||
"artifact",
|
||||
"world"
|
||||
],
|
||||
"paths": [
|
||||
"modules/interaction/**",
|
||||
"contracts/interaction/**",
|
||||
"modules/capability/**",
|
||||
"contracts/capability/**",
|
||||
"modules/artifact/**",
|
||||
"contracts/artifact/**",
|
||||
"modules/world/**",
|
||||
"contracts/world/**",
|
||||
"adapters/multimodal/**",
|
||||
"cmd/creator-web/**",
|
||||
"tests/**",
|
||||
"dev/ACTIVE_TASK.json",
|
||||
"dev/tasks/GEN2-025-multimodal-and-ambient-ingress-readiness-without-implementing-the-whole-.json"
|
||||
]
|
||||
},
|
||||
"forbidden_change_scope": {
|
||||
"paths": [
|
||||
"security/**",
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
"architecture/**",
|
||||
".gitea/workflows/**",
|
||||
"deploy/**",
|
||||
"database/**",
|
||||
"adapters/models/**"
|
||||
]
|
||||
},
|
||||
"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": [
|
||||
"contracts.interaction@v1",
|
||||
"contracts.capability@gen2",
|
||||
"contracts.artifact@v1",
|
||||
"contracts.world@gen2"
|
||||
],
|
||||
"may_change": [
|
||||
"Only contracts explicitly within GEN2-025 allowed_change_scope; preserve Gen2 owner boundaries and backward compatibility unless this packet says cutover."
|
||||
]
|
||||
},
|
||||
"semantic_owner": "Interaction ingress + Artifact + Capability + World refs. Follow GEN2 target architecture: owner boundaries are authoritative; Memory remains a read/retrieval fabric, World is not truth owner, SELF is not a God orchestrator.",
|
||||
"canonical_state": [
|
||||
"Interaction/Artifact/Epistemics existing responsibilities",
|
||||
"No task may create a second canonical owner for an already-owned mutable semantic concept."
|
||||
],
|
||||
"state_transitions": [
|
||||
"current state -> additive/shadow Gen2 path -> evidence comparison -> controlled cutover -> compatibility retention; writer rule: Interaction/Artifact/Epistemics existing responsibilities"
|
||||
],
|
||||
"side_effects_and_privileges": [
|
||||
"No external mutation may bypass existing Authority -> Commit -> Executor boundaries.",
|
||||
"Do not weaken architecture/security/test guardrails to make the task pass."
|
||||
],
|
||||
"persistence_and_migrations": [
|
||||
"No database schema change is authorized by this packet unless a revised task explicitly grants database/migration scope."
|
||||
],
|
||||
"failure_retry_restart": [
|
||||
"Rollback rule: existing chat/file routes remain compatible",
|
||||
"Retries/restarts must not create duplicate external side effects or a second canonical writer.",
|
||||
"Durable state required by this task must survive process restart; process-local memory is never the only source of truth."
|
||||
],
|
||||
"assumptions": [
|
||||
"Program milestone: M5; test profile: FAST.",
|
||||
"Task branch starts from local gen2-integration; base_repo_revision=gen2-integration must resolve to the untouched integration base while the task branch is active.",
|
||||
"Codex reads DOMOVOY_CONSTITUTION.md, GEN2_TARGET_ARCHITECTURE.md, GEN2_CANONICAL_DATA_MODEL.md, GEN2_MIGRATION_MAP.md, GEN2_MASTER_PLAN.md and GEN2_EXECUTION_CONTRACT.md before non-trivial edits.",
|
||||
"Do not expand scope silently. If a required change falls outside allowed paths or changes ownership, return a typed gap and request a task revision.",
|
||||
"Depends on: GEN2-024."
|
||||
],
|
||||
"acceptance_scenarios": [
|
||||
"GEN2-025-SCN-01: normalise ingress envelope: modality/source/device/location/principal attribution/evidence refs",
|
||||
"GEN2-025-SCN-02: raw large media -> Artifact, derived transcript/vision result -> Observation",
|
||||
"GEN2-025-SCN-03: device/location are World refs",
|
||||
"GEN2-025-SCN-04: speech/OCR/vision results include provenance/model/version/confidence",
|
||||
"GEN2-025-SCN-05: future ambient streams can feed bounded observations/episodes without bypassing Interaction/Epistemics",
|
||||
"GEN2-025-SCN-06: output device choice остаётся будущим capability, не hardcoded in core",
|
||||
"GEN2-025-EXIT: будущая команда «покажи камеру улицы на телевизоре здесь» вписывается через World/Focus/Capability/Authority без смены core model."
|
||||
],
|
||||
"required_tests": [
|
||||
"FAST: go test ./modules/interaction/... ./modules/capability/... ./modules/artifact/... ./modules/world/... ./adapters/multimodal/... ./cmd/creator-web/...",
|
||||
"FAST: go vet ./modules/interaction/... ./modules/capability/... ./modules/artifact/... ./modules/world/... ./adapters/multimodal/... ./cmd/creator-web/...",
|
||||
"FAST governance: go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json",
|
||||
"FAST scope: go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json",
|
||||
"FAST independent: go run ./cmd/devctl/independent",
|
||||
"FAST whitespace: git diff --check gen2-integration --"
|
||||
],
|
||||
"terminal_gap_states": [
|
||||
"BLOCKED_BY_SPEC_GAP",
|
||||
"BLOCKED_BY_ARCHITECTURE_GAP",
|
||||
"BLOCKED_BY_MISSING_CAPABILITY",
|
||||
"BLOCKED_BY_CONFLICT",
|
||||
"REQUIRES_MIGRATION_DECISION"
|
||||
]
|
||||
}
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
{
|
||||
"task_id": "GEN2-026",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ARCH-GEN2-000",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
"intent": "Перевести реальные бытовые сценарии на Gen2 foundation и доказать, что технический протокол не просачивается пользователю.",
|
||||
"requirement_ids": [
|
||||
"REQ-GEN2-026-GOAL",
|
||||
"REQ-GEN2-026-BOUNDARY",
|
||||
"REQ-GEN2-026-GATE"
|
||||
],
|
||||
"allowed_change_scope": {
|
||||
"modules": [
|
||||
"interaction",
|
||||
"situation",
|
||||
"capability",
|
||||
"epistemics",
|
||||
"work",
|
||||
"continuity",
|
||||
"configuration",
|
||||
"self",
|
||||
"world"
|
||||
],
|
||||
"paths": [
|
||||
"cmd/core-runtime/**",
|
||||
"cmd/cognitive-worker/**",
|
||||
"cmd/creator-web/**",
|
||||
"modules/interaction/**",
|
||||
"modules/situation/**",
|
||||
"modules/capability/**",
|
||||
"modules/epistemics/**",
|
||||
"modules/work/**",
|
||||
"modules/continuity/**",
|
||||
"modules/configuration/**",
|
||||
"modules/self/**",
|
||||
"modules/world/**",
|
||||
"runtime/retrieval/**",
|
||||
"adapters/multimodal/**",
|
||||
"tests/**",
|
||||
"dev/ACTIVE_TASK.json",
|
||||
"dev/tasks/GEN2-026-migrate-household-behavior-to-gen2-context-memory-skills-with-local-degr.json"
|
||||
]
|
||||
},
|
||||
"forbidden_change_scope": {
|
||||
"paths": [
|
||||
"security/**",
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
"architecture/**",
|
||||
".gitea/workflows/**",
|
||||
"deploy/**",
|
||||
"database/**",
|
||||
"adapters/models/**"
|
||||
]
|
||||
},
|
||||
"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": [
|
||||
"contracts.interaction@gen2",
|
||||
"contracts.situation@gen2",
|
||||
"contracts.capability@gen2",
|
||||
"contracts.epistemics@gen2",
|
||||
"contracts.work@gen2",
|
||||
"contracts.continuity@v1",
|
||||
"contracts.configuration@gen2",
|
||||
"contracts.self@gen2",
|
||||
"contracts.world@gen2",
|
||||
"contracts.cognitive@gen2"
|
||||
],
|
||||
"may_change": []
|
||||
},
|
||||
"semantic_owner": "Existing owners, integration task. Follow GEN2 target architecture: owner boundaries are authoritative; Memory remains a read/retrieval fabric, World is not truth owner, SELF is not a God orchestrator.",
|
||||
"canonical_state": [
|
||||
"no new canonical owner; integration switches reads/routes to Gen2",
|
||||
"No task may create a second canonical owner for an already-owned mutable semantic concept."
|
||||
],
|
||||
"state_transitions": [
|
||||
"current state -> additive/shadow Gen2 path -> evidence comparison -> controlled cutover -> compatibility retention; writer rule: no new canonical owner; integration switches reads/routes to Gen2"
|
||||
],
|
||||
"side_effects_and_privileges": [
|
||||
"No external mutation may bypass existing Authority -> Commit -> Executor boundaries.",
|
||||
"Do not weaken architecture/security/test guardrails to make the task pass."
|
||||
],
|
||||
"persistence_and_migrations": [
|
||||
"No database schema change is authorized by this packet unless a revised task explicitly grants database/migration scope."
|
||||
],
|
||||
"failure_retry_restart": [
|
||||
"Rollback rule: per-subsystem switches to V1 compatibility remain until M6",
|
||||
"Retries/restarts must not create duplicate external side effects or a second canonical writer.",
|
||||
"Durable state required by this task must survive process restart; process-local memory is never the only source of truth.",
|
||||
"Cutover must keep the previous read/compatibility path available until the task or milestone explicitly proves retirement."
|
||||
],
|
||||
"assumptions": [
|
||||
"Program milestone: M5; test profile: FAST.",
|
||||
"Task branch starts from local gen2-integration; base_repo_revision=gen2-integration must resolve to the untouched integration base while the task branch is active.",
|
||||
"Codex reads DOMOVOY_CONSTITUTION.md, GEN2_TARGET_ARCHITECTURE.md, GEN2_CANONICAL_DATA_MODEL.md, GEN2_MIGRATION_MAP.md, GEN2_MASTER_PLAN.md and GEN2_EXECUTION_CONTRACT.md before non-trivial edits.",
|
||||
"Do not expand scope silently. If a required change falls outside allowed paths or changes ownership, return a typed gap and request a task revision.",
|
||||
"Depends on: GEN2-025."
|
||||
],
|
||||
"acceptance_scenarios": [
|
||||
"GEN2-026-SCN-01: перенести SCN-HH-001..010 на Gen2 paths",
|
||||
"GEN2-026-SCN-02: name/preference continuity through scoped retrieval",
|
||||
"GEN2-026-SCN-03: current data uses freshness/revalidation",
|
||||
"GEN2-026-SCN-04: travel/places follows Focus/location and source evidence",
|
||||
"GEN2-026-SCN-05: multi-action uses Work",
|
||||
"GEN2-026-SCN-06: reminder uses Continuity",
|
||||
"GEN2-026-SCN-07: multimodal uses capability/degraded path",
|
||||
"GEN2-026-SCN-08: creative request avoids unnecessary web",
|
||||
"GEN2-026-SCN-09: partial/unavailable/contradictory results produce honest answer",
|
||||
"GEN2-026-SCN-10: short natural response",
|
||||
"GEN2-026-EXIT: обычный пользователь не замечает internal Gen2 protocols; baseline household behavior preserved/improved."
|
||||
],
|
||||
"required_tests": [
|
||||
"FAST: go test ./cmd/core-runtime/... ./cmd/cognitive-worker/... ./cmd/creator-web/... ./modules/interaction/... ./modules/situation/... ./modules/capability/... ./modules/epistemics/... ./modules/work/... ./modules/continuity/... ./runtime/retrieval/...",
|
||||
"FAST: go vet ./cmd/core-runtime/... ./cmd/cognitive-worker/... ./cmd/creator-web/... ./modules/interaction/... ./modules/situation/... ./modules/capability/... ./modules/epistemics/... ./modules/work/... ./modules/continuity/... ./runtime/retrieval/...",
|
||||
"FAST governance: go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json",
|
||||
"FAST scope: go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json",
|
||||
"FAST independent: go run ./cmd/devctl/independent",
|
||||
"FAST whitespace: git diff --check gen2-integration --"
|
||||
],
|
||||
"terminal_gap_states": [
|
||||
"BLOCKED_BY_SPEC_GAP",
|
||||
"BLOCKED_BY_ARCHITECTURE_GAP",
|
||||
"BLOCKED_BY_MISSING_CAPABILITY",
|
||||
"BLOCKED_BY_CONFLICT",
|
||||
"REQUIRES_MIGRATION_DECISION"
|
||||
]
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user