Compare commits
131
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
@@ -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.
|
||||
|
||||
@@ -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" {
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
func TestSpecializedInformationReplyIsNotOverwrittenByResearch(t *testing.T) {
|
||||
for _, kind := range []interaction.InformationRequestKind{
|
||||
interaction.InformationRequestMemoryInventory,
|
||||
interaction.InformationRequestTimeDifference,
|
||||
interaction.InformationRequestCapabilityInventory,
|
||||
interaction.InformationRequestCognitiveModelInventory,
|
||||
interaction.InformationRequestCognitiveChannelStatus,
|
||||
@@ -19,6 +18,9 @@ func TestSpecializedInformationReplyIsNotOverwrittenByResearch(t *testing.T) {
|
||||
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")
|
||||
}
|
||||
|
||||
+355
-36
@@ -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,14 +654,14 @@ func main() {
|
||||
cognitiveAvailable := true
|
||||
semanticInterpretationRequested := shouldRequestSemanticInterpretation(semantic)
|
||||
semanticReflexAudit := shouldAuditDeterministicReflex(semantic, string(utterance.ID), now)
|
||||
// A selected specialized media route already has an executable semantic
|
||||
// outcome. Do not block it on the general cognitive channel: image
|
||||
// generation must reach its own adapter even when cognition is degraded.
|
||||
if hasChatRoute(routePlan, chatRouteMedia) {
|
||||
// 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 != "" {
|
||||
if identityReply != "" || memoryInstructionReply != "" {
|
||||
semanticReflexAudit = false
|
||||
}
|
||||
if semanticInterpretationRequested || semanticReflexAudit {
|
||||
@@ -748,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)
|
||||
@@ -759,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
|
||||
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))
|
||||
@@ -778,14 +810,35 @@ func main() {
|
||||
replies = append(replies, "По запросу «"+part+"»: "+clarification)
|
||||
continue
|
||||
}
|
||||
searchQuery := interactiondomain.TravelSearchQuery(part, now)
|
||||
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: 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
|
||||
}
|
||||
@@ -813,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
|
||||
@@ -920,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,
|
||||
@@ -930,10 +1000,16 @@ func main() {
|
||||
ConfidenceMillis: semantic.Interpretation.ConfidenceMillis,
|
||||
})
|
||||
cancelReview()
|
||||
stableSemanticReply := mediaAttempted || 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")
|
||||
@@ -955,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
|
||||
@@ -1590,7 +1671,7 @@ func main() {
|
||||
}
|
||||
|
||||
func shouldRunGeneralResearch(kind interaction.InformationRequestKind) bool {
|
||||
return kind == ""
|
||||
return kind == "" || kind == interaction.InformationRequestTimeDifference
|
||||
}
|
||||
|
||||
func creatorSessionHandler(bootstrapToken, creatorUsername, creatorPassword string, sessionKey []byte, now func() time.Time) http.HandlerFunc {
|
||||
@@ -1713,35 +1794,54 @@ 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
|
||||
}
|
||||
if isInternetStatusQuery(query) {
|
||||
return "Интернет работает: Домовой получил ответ от внешнего источника.", true
|
||||
}
|
||||
if nearestFlightQuery(query) && !containsFlightTimeEvidence(strings.Join(lines, " ")) {
|
||||
return "Нашёл расписание по маршруту, но точное время ближайшего вылета в доступных фрагментах не указано. Откройте источник и выберите ближайший рейс:\n\n" + strings.Join(lines, "\n\n"), true
|
||||
}
|
||||
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 trainScheduleQuery(query) && !containsScheduleTimeEvidence(strings.Join(lines, " ")) {
|
||||
return "Нашёл страницу расписания по маршруту, но в доступных фрагментах нет точного времени отправления:\n\n" + strings.Join(lines, "\n\n"), true
|
||||
}
|
||||
return reply, 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 {
|
||||
@@ -1749,6 +1849,170 @@ func nearestFlightQuery(query string) bool {
|
||||
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)
|
||||
}
|
||||
@@ -1773,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 {
|
||||
@@ -1788,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 != "" {
|
||||
@@ -1830,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)
|
||||
@@ -2007,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"
|
||||
)
|
||||
|
||||
@@ -34,6 +36,158 @@ func TestNearestFlightEvidenceDoesNotClaimMissingDepartureTime(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
@@ -67,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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,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"))
|
||||
}))
|
||||
}
|
||||
@@ -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"}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -25,53 +25,66 @@ 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
|
||||
}
|
||||
|
||||
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"
|
||||
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 +116,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-024-multi-person-realms-identity-boundaries-and-contextual-source-trust.json","task_id":"GEN2-024","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"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
{
|
||||
"task_id": "GEN2-027",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ARCH-GEN2-000",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
"intent": "Перевести production на Gen2 по контролируемому canary, сохранив мгновенный rollback к последнему good release.",
|
||||
"requirement_ids": [
|
||||
"REQ-GEN2-027-GOAL",
|
||||
"REQ-GEN2-027-BOUNDARY",
|
||||
"REQ-GEN2-027-GATE"
|
||||
],
|
||||
"allowed_change_scope": {
|
||||
"modules": [
|
||||
"configuration"
|
||||
],
|
||||
"paths": [
|
||||
"deploy/**",
|
||||
"runtime/supervisor/**",
|
||||
"runtime/health/**",
|
||||
"modules/configuration/**",
|
||||
"contracts/configuration/**",
|
||||
"docs/**",
|
||||
"tests/**",
|
||||
"dev/ACTIVE_TASK.json",
|
||||
"dev/tasks/GEN2-027-production-canary-and-controlled-gen2-traffic-transition.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.configuration@gen2"
|
||||
],
|
||||
"may_change": [
|
||||
"Only contracts explicitly within GEN2-027 allowed_change_scope; preserve Gen2 owner boundaries and backward compatibility unless this packet says cutover."
|
||||
]
|
||||
},
|
||||
"semantic_owner": "Operations/Supervisor. 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 release state switches via Supervisor; semantic writers already cut over by prior tasks",
|
||||
"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: runtime release state switches via Supervisor; semantic writers already cut over by prior tasks"
|
||||
],
|
||||
"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": [
|
||||
"No database schema change is authorized by this packet unless a revised task explicitly grants database/migration scope."
|
||||
],
|
||||
"failure_retry_restart": [
|
||||
"Rollback rule: one-command/automatic revert to last-known-good release, schema remains backward 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.",
|
||||
"Cutover must keep the previous read/compatibility path available until the task or milestone explicitly proves retirement."
|
||||
],
|
||||
"assumptions": [
|
||||
"Program milestone: M5; 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-026."
|
||||
],
|
||||
"acceptance_scenarios": [
|
||||
"GEN2-027-SCN-01: build candidate from accepted milestone commit",
|
||||
"GEN2-027-SCN-02: deploy alongside or controlled replacement according to existing platform constraints",
|
||||
"GEN2-027-SCN-03: health + DB migration compatibility",
|
||||
"GEN2-027-SCN-04: bounded traffic/canary",
|
||||
"GEN2-027-SCN-05: compare errors/latency/unknown/recovery",
|
||||
"GEN2-027-SCN-06: household smoke",
|
||||
"GEN2-027-SCN-07: rollback drill",
|
||||
"GEN2-027-SCN-08: promote only после evidence",
|
||||
"GEN2-027-SCN-09: записать production composition snapshot в SELF/Learning Ledger where appropriate",
|
||||
"GEN2-027-EXIT: Gen2 handles production household traffic and rollback proven."
|
||||
],
|
||||
"required_tests": [
|
||||
"FAST: go test ./runtime/supervisor/... ./runtime/health/... ./modules/configuration/...",
|
||||
"FAST: go vet ./runtime/supervisor/... ./runtime/health/... ./modules/configuration/...",
|
||||
"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 M5: household + multimodal + degraded-mode + restart/recovery + production canary/rollback smoke on docker.cin.su."
|
||||
],
|
||||
"terminal_gap_states": [
|
||||
"BLOCKED_BY_SPEC_GAP",
|
||||
"BLOCKED_BY_ARCHITECTURE_GAP",
|
||||
"BLOCKED_BY_MISSING_CAPABILITY",
|
||||
"BLOCKED_BY_CONFLICT",
|
||||
"REQUIRES_MIGRATION_DECISION"
|
||||
]
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
{
|
||||
"task_id": "GEN2-028",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ARCH-GEN2-000",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
"intent": "Доказать, что legacy paths больше не являются активными writers/read dependencies.",
|
||||
"requirement_ids": [
|
||||
"REQ-GEN2-028-GOAL",
|
||||
"REQ-GEN2-028-BOUNDARY",
|
||||
"REQ-GEN2-028-GATE"
|
||||
],
|
||||
"allowed_change_scope": {
|
||||
"modules": [],
|
||||
"paths": [
|
||||
"modules/**",
|
||||
"contracts/**",
|
||||
"cmd/**",
|
||||
"adapters/**",
|
||||
"runtime/**",
|
||||
"platform/**",
|
||||
"docs/**",
|
||||
"tests/**",
|
||||
"dev/ACTIVE_TASK.json",
|
||||
"dev/tasks/GEN2-028-inventory-v1-compatibility-usage-and-prove-retirement-readiness.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": [],
|
||||
"may_change": [
|
||||
"Only contracts explicitly within GEN2-028 allowed_change_scope; preserve Gen2 owner boundaries and backward compatibility unless this packet says cutover."
|
||||
]
|
||||
},
|
||||
"semantic_owner": "Migration/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": [
|
||||
"no change",
|
||||
"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: no change"
|
||||
],
|
||||
"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: not applicable; this task is proof 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: M6; 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-027."
|
||||
],
|
||||
"acceptance_scenarios": [
|
||||
"GEN2-028-SCN-01: caller inventory",
|
||||
"GEN2-028-SCN-02: writer inventory",
|
||||
"GEN2-028-SCN-03: feature-switch telemetry",
|
||||
"GEN2-028-SCN-04: PersonalMemory fallback usage",
|
||||
"GEN2-028-SCN-05: ActiveProjectFocus compatibility usage",
|
||||
"GEN2-028-SCN-06: truth_bundles read usage",
|
||||
"GEN2-028-SCN-07: ordinary ENV fallback usage",
|
||||
"GEN2-028-SCN-08: V1 Skill view usage",
|
||||
"GEN2-028-SCN-09: rollback observation window evidence",
|
||||
"GEN2-028-SCN-10: сформировать explicit retirement list, каждый пункт с rollback consequence",
|
||||
"GEN2-028-EXIT: для каждого legacy path есть доказательство ACTIVE / SAFE_TO_RETIRE / KEEP_LONGER."
|
||||
],
|
||||
"required_tests": [
|
||||
"FAST: go test ./cmd/... ./modules/... ./runtime/...",
|
||||
"FAST: go vet ./cmd/... ./modules/... ./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"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
{
|
||||
"task_id": "GEN2-029",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ARCH-GEN2-000",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
"intent": "Удалить архитектурные костыли V1, которые мешают дальнейшему развитию, но сохранить raw/canonical historical evidence.",
|
||||
"requirement_ids": [
|
||||
"REQ-GEN2-029-GOAL",
|
||||
"REQ-GEN2-029-BOUNDARY",
|
||||
"REQ-GEN2-029-GATE"
|
||||
],
|
||||
"allowed_change_scope": {
|
||||
"modules": [],
|
||||
"paths": [
|
||||
"modules/**",
|
||||
"contracts/**",
|
||||
"cmd/**",
|
||||
"adapters/**",
|
||||
"runtime/**",
|
||||
"platform/**",
|
||||
"database/migrations/*.sql",
|
||||
"docs/**",
|
||||
"tests/**",
|
||||
"dev/ACTIVE_TASK.json",
|
||||
"dev/tasks/GEN2-029-retire-proven-v1-paths-without-deleting-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": [],
|
||||
"may_change": [
|
||||
"Only contracts explicitly within GEN2-029 allowed_change_scope; preserve Gen2 owner boundaries and backward compatibility unless this packet says cutover."
|
||||
]
|
||||
},
|
||||
"semantic_owner": "respective 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": [
|
||||
"Gen2 writers already active; V1 writers permanently disabled/removed",
|
||||
"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: Gen2 writers already active; V1 writers permanently disabled/removed"
|
||||
],
|
||||
"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: code rollback possible to pre-retirement tag only if schema remains readable; destructive DB deletion requires explicit backup/export and approval",
|
||||
"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: M6; test profile: FULL_RETIREMENT.",
|
||||
"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-028."
|
||||
],
|
||||
"acceptance_scenarios": [
|
||||
"GEN2-029-SCN-01: retire PersonalMemory ContextText read path",
|
||||
"GEN2-029-SCN-02: retire project-only Focus source of truth",
|
||||
"GEN2-029-SCN-03: retire truth_bundles as active canonical path while retaining historical compatibility/export as needed",
|
||||
"GEN2-029-SCN-04: remove non-bootstrap ordinary ENV reads already migrated",
|
||||
"GEN2-029-SCN-05: retire implicit reflex substitutes superseded by explicit Reflex",
|
||||
"GEN2-029-SCN-06: remove dead feature switches only after observation",
|
||||
"GEN2-029-SCN-07: не удалять human utterances, observations, experiences, commits, outcomes, audit lineage",
|
||||
"GEN2-029-EXIT: нет двух архитектурных поколений в active runtime; historical evidence доступно."
|
||||
],
|
||||
"required_tests": [
|
||||
"FAST: go test ./cmd/... ./modules/... ./runtime/...",
|
||||
"FAST: go vet ./cmd/... ./modules/... ./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 --",
|
||||
"FULL: go test ./...",
|
||||
"FULL: go vet ./...",
|
||||
"FULL retirement gate: complete repository + recovery + migration/export verification BEFORE any irreversible V1 schema/data retirement."
|
||||
],
|
||||
"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-030",
|
||||
"task_revision": 1,
|
||||
"architecture_revision": "ARCH-GEN2-000",
|
||||
"base_repo_revision": "gen2-integration",
|
||||
"risk_tier": "E",
|
||||
"intent": "Зафиксировать Gen2 как новый рабочий baseline и отделить фундамент от будущих capabilities.",
|
||||
"requirement_ids": [
|
||||
"REQ-GEN2-030-GOAL",
|
||||
"REQ-GEN2-030-BOUNDARY",
|
||||
"REQ-GEN2-030-GATE"
|
||||
],
|
||||
"allowed_change_scope": {
|
||||
"modules": [],
|
||||
"paths": [
|
||||
"docs/**",
|
||||
"architecture/**",
|
||||
"development/**",
|
||||
"dev/**",
|
||||
"modules/**",
|
||||
"contracts/**",
|
||||
"cmd/**",
|
||||
"adapters/**",
|
||||
"runtime/**",
|
||||
"platform/**",
|
||||
"tests/**"
|
||||
]
|
||||
},
|
||||
"forbidden_change_scope": {
|
||||
"paths": [
|
||||
"security/**",
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
".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": [],
|
||||
"may_change": [
|
||||
"Only contracts explicitly within GEN2-030 allowed_change_scope; preserve Gen2 owner boundaries and backward compatibility unless this packet says cutover."
|
||||
]
|
||||
},
|
||||
"semantic_owner": "Architecture/Governance + all owners evidence. 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 semantic cutover",
|
||||
"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: no semantic cutover"
|
||||
],
|
||||
"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: previous M5 production tag remains last-known-good until Gen2 acceptance signed",
|
||||
"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: M6; test profile: FULL_FINAL.",
|
||||
"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-029."
|
||||
],
|
||||
"acceptance_scenarios": [
|
||||
"GEN2-030-SCN-01: FULL acceptance gate once",
|
||||
"GEN2-030-SCN-02: production smoke/restart/rollback confirmation",
|
||||
"GEN2-030-SCN-03: architecture registry reflects actual code",
|
||||
"GEN2-030-SCN-04: canonical data model vs actual schema reconciliation",
|
||||
"GEN2-030-SCN-05: Personal Benchmark baseline",
|
||||
"GEN2-030-SCN-06: Learning Ledger initial history",
|
||||
"GEN2-030-SCN-07: known gaps/future capabilities queue",
|
||||
"GEN2-030-SCN-08: новый stable tag `domovoy-gen2-baseline-*`",
|
||||
"GEN2-030-SCN-09: обновить PROJECT_STATUS/active task",
|
||||
"GEN2-030-EXIT: Gen2 is documented, production-proven, rollbackable and ready for capability growth/self-learning iterations."
|
||||
],
|
||||
"required_tests": [
|
||||
"FAST: go test ./cmd/... ./modules/... ./runtime/...",
|
||||
"FAST: go vet ./cmd/... ./modules/... ./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 --",
|
||||
"FULL: go test ./...",
|
||||
"FULL: go vet ./...",
|
||||
"FULL final: complete Gen2 acceptance, household scenarios, recovery, architecture, migration compatibility and production smoke."
|
||||
],
|
||||
"terminal_gap_states": [
|
||||
"BLOCKED_BY_SPEC_GAP",
|
||||
"BLOCKED_BY_ARCHITECTURE_GAP",
|
||||
"BLOCKED_BY_MISSING_CAPABILITY",
|
||||
"BLOCKED_BY_CONFLICT",
|
||||
"REQUIRES_MIGRATION_DECISION"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,367 @@
|
||||
# DOMOVOY Gen2 — Execution Contract for Codex
|
||||
|
||||
## 1. Назначение
|
||||
|
||||
Этот документ определяет, **как выполнять** `PRE-GEN2-000`, `ARCH-GEN2-000` и `GEN2-001…GEN2-030`.
|
||||
|
||||
Он не заменяет:
|
||||
- `DOMOVOY_CONSTITUTION.md`;
|
||||
- `GEN2_TARGET_ARCHITECTURE.md`;
|
||||
- `GEN2_CANONICAL_DATA_MODEL.md`;
|
||||
- `GEN2_MIGRATION_MAP.md`;
|
||||
- `GEN2_MASTER_PLAN.md`.
|
||||
|
||||
При конфликте действуют существующий `AGENTS.md` и canonical architecture authority order. Если конфликт нельзя снять однозначно — `BLOCKED_BY_SPEC_GAP` / `BLOCKED_BY_ARCHITECTURE_GAP`, а не обход.
|
||||
|
||||
## 2. Почему `ARCH-GEN2-000`, а не `GEN2-000`
|
||||
|
||||
Текущий `devctl` принимает `architecture_revision` только как:
|
||||
- принятый `ADR-*`; или
|
||||
- существующий architecture task с префиксом `ARCH-*`.
|
||||
|
||||
Поэтому Master Plan task `GEN2-000 — Adopt Gen2 ...` исполняется как:
|
||||
|
||||
`ARCH-GEN2-000`
|
||||
|
||||
Смысл задачи не изменён. Все последующие `GEN2-*` используют:
|
||||
|
||||
`"architecture_revision": "ARCH-GEN2-000"`
|
||||
|
||||
## 3. Нулевой принцип
|
||||
|
||||
**Цель — построить Gen2, а не бесконечно проверять тесты.**
|
||||
|
||||
Тесты дают достаточное доказательство сохранения критических свойств. Они не являются самостоятельным продуктом.
|
||||
|
||||
Не добавлять тест только потому, что можно добавить ещё один edge case.
|
||||
|
||||
## 4. PRE-GEN2: сначала сохранить всё текущее
|
||||
|
||||
До любых Gen2 semantic/schema изменений выполнить `PRE-GEN2-000`.
|
||||
|
||||
### 4.1 Инвентаризация
|
||||
|
||||
Проверить:
|
||||
- `git status`;
|
||||
- все worktrees;
|
||||
- все локальные ветки;
|
||||
- ветки, опережающие `main`;
|
||||
- staged / unstaged / untracked files;
|
||||
- незапушенные commits;
|
||||
- merge/rebase/cherry-pick state.
|
||||
|
||||
Ничего не удалять и не делать `reset --hard` до классификации каждого изменения.
|
||||
|
||||
### 4.2 Preservation
|
||||
|
||||
Для каждого актуального открытого изменения:
|
||||
1. понять происхождение;
|
||||
2. исключить secrets/generated/runtime garbage;
|
||||
3. сохранить актуальную работу commit-ом;
|
||||
4. не переписывать опубликованную историю;
|
||||
5. не терять конфликтующую ветку ради удобного merge.
|
||||
|
||||
### 4.3 Merge
|
||||
|
||||
После preservation:
|
||||
1. обновить локальный `main`;
|
||||
2. контролируемо слить всю актуальную работу;
|
||||
3. разрешить конфликты по смыслу, сохраняя существующее поведение;
|
||||
4. выполнить только PRE FAST gate;
|
||||
5. push `main`.
|
||||
|
||||
### 4.4 Baseline
|
||||
|
||||
После успешного push:
|
||||
- записать SHA как `GEN2_BASE_COMMIT`;
|
||||
- поставить baseline tag `domovoy-pre-gen2`;
|
||||
- сохранить baseline report;
|
||||
- создать ветку `gen2-integration` от baseline;
|
||||
- push `gen2-integration`.
|
||||
|
||||
Если tag уже существует, не перезаписывать его молча: использовать duplicate-safe имя с датой/коротким SHA и записать фактическое имя в report.
|
||||
|
||||
## 5. Как установить execution package
|
||||
|
||||
До `PRE-GEN2-000` в репозиторий копируются **только**:
|
||||
- `bootstrap/dev/tasks/PRE-GEN2-000-*.json` -> `dev/tasks/`;
|
||||
- `bootstrap/dev/ACTIVE_TASK.json` -> `dev/ACTIVE_TASK.json`.
|
||||
|
||||
Остальные Gen2 документы/task packets не должны случайно попасть в V1 baseline как «реализация Gen2».
|
||||
|
||||
После PRE baseline:
|
||||
1. checkout `gen2-integration`;
|
||||
2. создать branch для `ARCH-GEN2-000`;
|
||||
3. скопировать содержимое `program/docs/gen2/` в согласованные canonical repo paths;
|
||||
4. скопировать `program/dev/tasks/*.json` в `dev/tasks/`;
|
||||
5. заменить `dev/ACTIVE_TASK.json` содержимым `program/dev/ACTIVE_TASK.arch-gen2-000.json`;
|
||||
6. выполнить `ARCH-GEN2-000`.
|
||||
|
||||
## 6. Branch model
|
||||
|
||||
После PRE каждая задача выполняется отдельно.
|
||||
|
||||
### 6.1 Перед task
|
||||
|
||||
```text
|
||||
checkout gen2-integration
|
||||
pull/verify expected head
|
||||
create branch gen2/<task-id>-<short-slug>
|
||||
set dev/ACTIVE_TASK.json -> selected packet
|
||||
run preflight
|
||||
```
|
||||
|
||||
Все post-PRE task packets имеют:
|
||||
|
||||
`"base_repo_revision": "gen2-integration"`
|
||||
|
||||
Это намеренно.
|
||||
|
||||
Пока Codex работает в task branch, локальная ветка `gen2-integration` остаётся на исходной точке задачи. Поэтому `check-diff` видит **только diff текущего packet-а**, а не всю программу Gen2.
|
||||
|
||||
### 6.2 После FAST gate
|
||||
|
||||
1. `check-diff`;
|
||||
2. `git diff --check`;
|
||||
3. commit task branch;
|
||||
4. merge task branch -> `gen2-integration`;
|
||||
5. push `gen2-integration`;
|
||||
6. удалить task branch только после проверки merge.
|
||||
|
||||
Не сливать незавершённый packet в integration ради «сохранения прогресса»: для этого используются commits в task branch.
|
||||
|
||||
## 7. Milestone merge в main
|
||||
|
||||
`main` обновляется по реперным точкам, а не после каждой мелкой задачи.
|
||||
|
||||
Milestone exit:
|
||||
1. выполнить соответствующий `MILESTONE` или `FULL` gate на `gen2-integration`;
|
||||
2. только если gate зелёный — merge `gen2-integration` -> `main`;
|
||||
3. push `main`;
|
||||
4. поставить milestone tag;
|
||||
5. fast-forward `gen2-integration` до нового `main` merge commit;
|
||||
6. push `gen2-integration`.
|
||||
|
||||
Рекомендуемые tags:
|
||||
- `domovoy-gen2-m0`
|
||||
- `domovoy-gen2-m1`
|
||||
- …
|
||||
- `domovoy-gen2-m6`
|
||||
|
||||
Не перезаписывать существующий tag.
|
||||
|
||||
## 8. Test profiles
|
||||
|
||||
### 8.1 FAST — основной цикл
|
||||
|
||||
FAST выполняется на каждом task.
|
||||
|
||||
Состав:
|
||||
- targeted `go test` только изменённого owner/runtime;
|
||||
- targeted `go vet`;
|
||||
- `devctl preflight`;
|
||||
- `devctl check-diff`;
|
||||
- `devctl/independent`;
|
||||
- `git diff --check`;
|
||||
- один-два regression checks для фактического риска task-а.
|
||||
|
||||
**FAST не включает `go test ./...` по умолчанию.**
|
||||
|
||||
Если targeted command невозможно корректно сформировать из-за package topology, сначала объяснить почему; не переходить автоматически на полный suite без причины.
|
||||
|
||||
### 8.2 MILESTONE
|
||||
|
||||
MILESTONE запускается после завершения целого архитектурного органа.
|
||||
|
||||
Только:
|
||||
- затронутые owners;
|
||||
- соседние contracts;
|
||||
- migration/backfill sample;
|
||||
- ключевые cross-owner regressions;
|
||||
- restart/recovery там, где это часть milestone.
|
||||
|
||||
### 8.3 FULL
|
||||
|
||||
FULL запускается только там, где packet прямо содержит `FULL:` evidence.
|
||||
|
||||
Базовые FULL points:
|
||||
- `GEN2-016` — M3;
|
||||
- `GEN2-027` — M5;
|
||||
- `GEN2-029` — до необратимого V1 retirement;
|
||||
- `GEN2-030` — финальный acceptance.
|
||||
|
||||
Дополнительный FULL допустим только при:
|
||||
- критической глобальной schema migration;
|
||||
- изменении Authority/Commit invariants;
|
||||
- серьёзном production rollback concern;
|
||||
- явном требовании владельца.
|
||||
|
||||
### 8.4 Не повторять свежий FULL без причины
|
||||
|
||||
Если предыдущий FULL:
|
||||
- выполнен на том же milestone;
|
||||
- после него не менялась соответствующая глобальная поверхность;
|
||||
- нет нового риска, который он должен покрыть,
|
||||
|
||||
не запускать его повторно «для уверенности».
|
||||
|
||||
## 9. STOP TEST EXPANSION
|
||||
|
||||
Codex обязан остановить расширение тестов и вернуться к цели task-а, если одновременно верно:
|
||||
|
||||
1. функциональная/архитектурная цель ещё не достигнута;
|
||||
2. тестовая работа стала существенно больше самой реализации;
|
||||
3. новые тесты проверяют уже защищённый invariant или косметические вариации;
|
||||
4. нет нового существенного риска, ради которого они нужны.
|
||||
|
||||
В этом случае:
|
||||
- оставить минимально достаточные tests;
|
||||
- записать потенциальные дополнительные cases в findings;
|
||||
- продолжить реализацию.
|
||||
|
||||
## 10. Architecture and migration law
|
||||
|
||||
### 10.1 Один canonical writer
|
||||
|
||||
Никогда не иметь два canonical writers одного mutable semantic state.
|
||||
|
||||
Разрешено:
|
||||
- backfill;
|
||||
- shadow projection;
|
||||
- dual-read;
|
||||
- compare;
|
||||
- read fallback.
|
||||
|
||||
Не разрешено:
|
||||
- два равноправных canonical write paths «на время миграции».
|
||||
|
||||
### 10.2 Historical migrations
|
||||
|
||||
Не редактировать V1 migrations `001–045`.
|
||||
|
||||
Gen2 создаёт только новые next-numbered migrations.
|
||||
|
||||
Если порядковый номер изменился из-за параллельной работы, использовать следующий свободный — смысл migration определяется task, а не заранее зарезервированным числом.
|
||||
|
||||
### 10.3 Особенность текущего devctl
|
||||
|
||||
Текущий validator трактует **любое** изменение `database/**` как требующее положительного `new_database_owner_roles`, а любое изменение `deploy/**` — положительного `new_privileges`.
|
||||
|
||||
Поэтому packets на этих поверхностях содержат `1` как compatibility gate.
|
||||
|
||||
Это **не означает**, что task автоматически разрешает:
|
||||
- новый semantic owner;
|
||||
- новый DB role;
|
||||
- cross-owner FK/JOIN/write;
|
||||
- дополнительную внешнюю привилегию.
|
||||
|
||||
Разрешено только то, что явно написано в packet + Gen2 architecture docs.
|
||||
|
||||
`ARCH-GEN2-000` может улучшить этот validator, но должен сохранить обратную совместимость с уже созданными packets.
|
||||
|
||||
## 11. Scope discipline
|
||||
|
||||
Task packet — верхняя граница изменения.
|
||||
|
||||
Если Codex обнаружил, что нужно изменить файл вне scope:
|
||||
1. не менять файл;
|
||||
2. установить, это implementation need или architecture gap;
|
||||
3. вернуть соответствующий `BLOCKED_*`, либо подготовить revision task packet;
|
||||
4. после явного расширения scope продолжить.
|
||||
|
||||
Не расширять task до соседнего рефакторинга только потому, что он «логично рядом».
|
||||
|
||||
## 12. Read before write
|
||||
|
||||
Перед нетривиальным изменением:
|
||||
1. прочитать Gen2 canonical docs;
|
||||
2. прочитать текущий task packet;
|
||||
3. найти существующую реализацию/owner;
|
||||
4. проверить migrations/contracts/call path;
|
||||
5. только затем менять код.
|
||||
|
||||
Если существующий механизм уже решает задачу — расширить его, а не создавать параллельный.
|
||||
|
||||
## 13. Data migration rules
|
||||
|
||||
Для каждого cutover:
|
||||
1. additive schema;
|
||||
2. backfill с lineage;
|
||||
3. shadow/read comparison;
|
||||
4. controlled reader switch;
|
||||
5. writer switch только после доказательства;
|
||||
6. observation window;
|
||||
7. V1 compatibility path остаётся до retirement task.
|
||||
|
||||
Rollback не должен уничтожать уже накопленные Gen2 evidence.
|
||||
|
||||
## 14. Production
|
||||
|
||||
Production mutation допускается только packet-ами, где это явно разрешено.
|
||||
|
||||
Основной production transition — `GEN2-027`.
|
||||
|
||||
Перед promotion:
|
||||
- health;
|
||||
- migration compatibility;
|
||||
- bounded canary;
|
||||
- household smoke;
|
||||
- rollback drill.
|
||||
|
||||
Если canary неубедителен — rollback считается нормальным результатом, а не провалом программы.
|
||||
|
||||
## 15. Self-evolution
|
||||
|
||||
До `GEN2-022` SELF/Skill/Reflex не получает права самовольно менять production code.
|
||||
|
||||
После `GEN2-022`:
|
||||
- proposal != promotion;
|
||||
- creator != evaluator;
|
||||
- candidate строится отдельно;
|
||||
- tests/shadow/canary;
|
||||
- Supervisor держит last-known-good;
|
||||
- owner intent/authority остаётся выше внутренней оптимизации.
|
||||
|
||||
## 16. Completion report каждого task
|
||||
|
||||
Codex обязан закончить packet коротким отчётом:
|
||||
|
||||
- task id/revision;
|
||||
- base SHA;
|
||||
- branch/commit SHA;
|
||||
- changed paths/modules;
|
||||
- contracts/schema/events/dependencies/privileges touched;
|
||||
- writer before/after;
|
||||
- migration/backfill/cutover state;
|
||||
- FAST/MILESTONE/FULL tests **фактически** выполненные;
|
||||
- tests сознательно не выполненные и почему;
|
||||
- rollback point;
|
||||
- findings not changed;
|
||||
- gaps/limitations;
|
||||
- следующий task, который разрешено активировать.
|
||||
|
||||
Не писать `PASS` для теста, который не запускался.
|
||||
|
||||
## 17. Terminal states
|
||||
|
||||
Допустимые остановки:
|
||||
- `BLOCKED_BY_SPEC_GAP`
|
||||
- `BLOCKED_BY_ARCHITECTURE_GAP`
|
||||
- `BLOCKED_BY_MISSING_CAPABILITY`
|
||||
- `BLOCKED_BY_CONFLICT`
|
||||
- `REQUIRES_MIGRATION_DECISION`
|
||||
|
||||
Typed stop лучше скрытого архитектурного обхода.
|
||||
|
||||
## 18. Главный критерий программы
|
||||
|
||||
Успех task-а — не «все возможные тесты зелёные».
|
||||
|
||||
Успех:
|
||||
|
||||
1. заявленная Gen2 capability/architecture increment реально создана;
|
||||
2. canonical ownership не размыт;
|
||||
3. предыдущий критический behavior не сломан;
|
||||
4. есть достаточное evidence;
|
||||
5. rollback понятен;
|
||||
6. следующий packet может строиться на результате без архитектурного долга, который мы сознательно откладываем.
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user