mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-15 03:55:09 +08:00
Add exact consumed approval to fenced Goal attempt handoff
This commit is contained in:
parent
8a15c0b573
commit
6ccf8befa0
@ -0,0 +1,99 @@
|
||||
package vip.mate.goal.service;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import vip.mate.agent.context.ChatOrigin;
|
||||
import vip.mate.agent.context.ExecutionAttribution;
|
||||
import vip.mate.exception.MateClawException;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
/** A consumed approval may start one new owner; it never revives its original lease. */
|
||||
@Service
|
||||
public class GoalApprovalRunService {
|
||||
private final JdbcTemplate jdbc;
|
||||
private final ObjectMapper json;
|
||||
private final GoalJsonAcceptanceService acceptance;
|
||||
private final ManagedGoalJsonService artifacts;
|
||||
private final GoalContinuationStore continuations;
|
||||
private final GoalAttemptStore attempts;
|
||||
private final GoalRunCoordinator coordinator;
|
||||
private final GoalService goals;
|
||||
|
||||
public GoalApprovalRunService(JdbcTemplate jdbc, ObjectMapper json, GoalJsonAcceptanceService acceptance,
|
||||
ManagedGoalJsonService artifacts, GoalContinuationStore continuations,
|
||||
GoalAttemptStore attempts, GoalRunCoordinator coordinator, GoalService goals) {
|
||||
this.jdbc=jdbc; this.json=json; this.acceptance=acceptance; this.artifacts=artifacts;
|
||||
this.continuations=continuations; this.attempts=attempts; this.coordinator=coordinator; this.goals=goals;
|
||||
}
|
||||
|
||||
public record ReplayRun(GoalRunCoordinator.ClaimedRun run, ChatOrigin origin) { }
|
||||
|
||||
@Transactional
|
||||
public ReplayRun claim(ChatOrigin requested, String toolCallPayload) {
|
||||
ExecutionAttribution link = requested == null ? null : requested.executionAttribution();
|
||||
if (link == null || link.goalId() == null || link.goalAttemptId() == null
|
||||
|| link.ownerFence() == null || link.approvalId() == null) throw rejected();
|
||||
// Preserve the normal user -> conversation -> Goal lock order.
|
||||
var owners = jdbc.queryForList("SELECT username FROM mate_conversation WHERE conversation_id=? AND deleted=0",
|
||||
String.class, requested.conversationId());
|
||||
if (owners.size()!=1) throw rejected();
|
||||
var scope = acceptance.authorizedGoal(link.goalId(), owners.getFirst(), true);
|
||||
if (!scope.required() || !"active".equals(scope.status())) throw rejected();
|
||||
var candidate = continuations.getForUpdate(link.goalId());
|
||||
if (candidate == null || !"waiting_approval".equals(candidate.state())) throw rejected();
|
||||
var parent = attempts.getForUpdate(link.goalAttemptId());
|
||||
if (parent == null || !Objects.equals(parent.goalId(), link.goalId())
|
||||
|| !Objects.equals(parent.conversationId(), requested.conversationId())
|
||||
|| !Objects.equals(parent.leaseToken(), link.ownerFence()) || !"succeeded".equals(parent.state())) throw rejected();
|
||||
var approvals = jdbc.query("""
|
||||
SELECT conversation_id,agent_id,status,tool_call_payload,chat_origin
|
||||
FROM mate_tool_approval WHERE pending_id=? AND deleted=0 FOR UPDATE
|
||||
""", (r,i) -> new Approval(r.getString("conversation_id"), r.getString("agent_id"),
|
||||
r.getString("status"), r.getString("tool_call_payload"), r.getString("chat_origin")), link.approvalId());
|
||||
if (approvals.size()!=1) throw rejected();
|
||||
var approval = approvals.getFirst();
|
||||
if (!"CONSUMED".equals(approval.status()) || !Objects.equals(toolCallPayload, approval.payload())
|
||||
|| !Objects.equals(requested.conversationId(), approval.conversationId())
|
||||
|| !Objects.equals(String.valueOf(requested.agentId()), approval.agentId())) throw rejected();
|
||||
ChatOrigin persisted;
|
||||
try { persisted = json.readValue(approval.origin(), ChatOrigin.class); }
|
||||
catch (Exception error) { throw rejected(); }
|
||||
if (persisted == null || persisted.executionAttribution() == null
|
||||
|| !Objects.equals(persisted.executionAttribution().goalId(), link.goalId())
|
||||
|| !Objects.equals(persisted.executionAttribution().goalAttemptId(), link.goalAttemptId())
|
||||
|| !Objects.equals(persisted.executionAttribution().ownerFence(), link.ownerFence())
|
||||
|| !Objects.equals(persisted.agentId(), requested.agentId())
|
||||
|| !Objects.equals(persisted.workspaceId(), requested.workspaceId())
|
||||
|| !Objects.equals(persisted.conversationId(), requested.conversationId())) throw rejected();
|
||||
if (!jdbc.queryForList("SELECT attempt_id FROM mate_goal_attempt WHERE approval_pending_id=? FOR UPDATE",
|
||||
String.class, link.approvalId()).isEmpty()) throw rejected();
|
||||
Instant instant = Instant.now();
|
||||
LocalDateTime now = LocalDateTime.ofInstant(instant, java.time.ZoneId.systemDefault());
|
||||
long untilEpoch = instant.getEpochSecond()+60;
|
||||
String token = UUID.randomUUID().toString();
|
||||
if (!continuations.claimApproval(link.goalId(), parent.id(), token, now, untilEpoch)) throw rejected();
|
||||
var claimed = continuations.get(link.goalId());
|
||||
var attempt = attempts.create(link.goalId(), requested.conversationId(), parent.id(), "approval",
|
||||
token, GoalLeaseTime.local(untilEpoch), null, now, untilEpoch);
|
||||
jdbc.update("UPDATE mate_goal_attempt SET approval_pending_id=? WHERE attempt_id=?", link.approvalId(), attempt.id());
|
||||
if (!continuations.bindAttempt(link.goalId(), token, attempt.id(), claimed.revision())) throw rejected();
|
||||
var run = new GoalRunCoordinator.ClaimedRun(candidate, goals.getById(link.goalId()), attempt, claimed.revision()+1);
|
||||
if (!coordinator.markRunning(run, now)) throw rejected();
|
||||
ChatOrigin origin = persisted.withBaseUrl(requested.baseUrl()).withExecutionAttribution(
|
||||
new ExecutionAttribution(link.goalId(), attempt.id(), null, link.approvalId(), token));
|
||||
// Recheck current conversation scope and both new lease rows before committing the claim.
|
||||
ManagedGoalJsonService.verifyLease(artifacts.runtimeGoal(origin));
|
||||
return new ReplayRun(run, origin);
|
||||
}
|
||||
|
||||
private record Approval(String conversationId, String agentId, String status, String payload, String origin) { }
|
||||
private static MateClawException rejected() {
|
||||
return new MateClawException(409, "Approved Goal execution cannot acquire a current owner; resume from current Goal state");
|
||||
}
|
||||
}
|
||||
@ -43,9 +43,17 @@ public class GoalAttemptStore {
|
||||
}
|
||||
|
||||
public GoalAttempt get(String id) {
|
||||
return get(id, false);
|
||||
}
|
||||
|
||||
GoalAttempt getForUpdate(String id) {
|
||||
return get(id, true);
|
||||
}
|
||||
|
||||
private GoalAttempt get(String id, boolean lock) {
|
||||
List<GoalAttempt> rows = jdbc.query("""
|
||||
SELECT * FROM mate_goal_attempt WHERE attempt_id=?
|
||||
""", (rs, row) -> read(rs), id);
|
||||
""" + (lock ? " FOR UPDATE" : ""), (rs, row) -> read(rs), id);
|
||||
return rows.isEmpty() ? null : rows.getFirst();
|
||||
}
|
||||
|
||||
|
||||
@ -67,10 +67,18 @@ public class GoalContinuationStore {
|
||||
}
|
||||
|
||||
public Continuation get(Long goalId) {
|
||||
return get(goalId, false);
|
||||
}
|
||||
|
||||
Continuation getForUpdate(Long goalId) {
|
||||
return get(goalId, true);
|
||||
}
|
||||
|
||||
private Continuation get(Long goalId, boolean lock) {
|
||||
List<Continuation> rows = jdbc.query("""
|
||||
SELECT c.*,g.conversation_id FROM mate_goal_continuation c
|
||||
JOIN mate_agent_goal g ON g.id=c.goal_id WHERE c.goal_id=?
|
||||
""", (rs, row) -> read(rs), goalId);
|
||||
""" + (lock ? " FOR UPDATE" : ""), (rs, row) -> read(rs), goalId);
|
||||
return rows.isEmpty() ? null : rows.getFirst();
|
||||
}
|
||||
|
||||
@ -81,7 +89,7 @@ public class GoalContinuationStore {
|
||||
boolean claim(Long goalId, String token, LocalDateTime now, LocalDateTime until, long nowEpoch, long untilEpoch) {
|
||||
return jdbc.update("""
|
||||
UPDATE mate_goal_continuation SET state='running',lease_owner=?,lease_until=?,lease_until_epoch_second=?,updated_at=?,
|
||||
wake_requested=FALSE,revision=revision+1
|
||||
wake_requested=FALSE,waiting_approval_attempt_id=NULL,revision=revision+1
|
||||
WHERE goal_id=? AND
|
||||
((state IN ('queued','retry') AND next_run_at<=?)
|
||||
OR (state='running' AND lease_until_epoch_second<=?))
|
||||
@ -96,6 +104,17 @@ public class GoalContinuationStore {
|
||||
""", until, GoalLeaseTime.epoch(until), goalId, token) == 1;
|
||||
}
|
||||
|
||||
boolean claimApproval(Long goalId, String parentAttemptId, String token, LocalDateTime now, long untilEpoch) {
|
||||
return jdbc.update("""
|
||||
UPDATE mate_goal_continuation SET state='running',lease_owner=?,lease_until=?,
|
||||
lease_until_epoch_second=?,updated_at=?,wake_requested=FALSE,
|
||||
waiting_approval_attempt_id=NULL,revision=revision+1
|
||||
WHERE goal_id=? AND state='waiting_approval' AND waiting_approval_attempt_id=?
|
||||
AND current_attempt_id IS NULL AND lease_owner IS NULL
|
||||
AND EXISTS(SELECT 1 FROM mate_agent_goal g WHERE g.id=goal_id AND
|
||||
""" + ELIGIBLE + ")", token, GoalLeaseTime.local(untilEpoch), untilEpoch, now, goalId, parentAttemptId) == 1;
|
||||
}
|
||||
|
||||
public boolean bindAttempt(Long goalId, String token, String attemptId, long expectedRevision) {
|
||||
return jdbc.update("""
|
||||
UPDATE mate_goal_continuation SET current_attempt_id=?,revision=revision+1,updated_at=?
|
||||
@ -130,9 +149,10 @@ public class GoalContinuationStore {
|
||||
UPDATE mate_goal_continuation
|
||||
SET state=CASE WHEN ?='waiting_approval' AND wake_requested=TRUE THEN 'queued' ELSE ? END,
|
||||
next_run_at=?,failures=?,reason=?,wake_requested=FALSE,lease_owner=NULL,lease_until=NULL,lease_until_epoch_second=0,
|
||||
waiting_approval_attempt_id=CASE WHEN ?='waiting_approval' THEN current_attempt_id ELSE NULL END,
|
||||
current_attempt_id=NULL,revision=revision+1,updated_at=?
|
||||
WHERE goal_id=? AND lease_owner=? AND current_attempt_id=? AND revision=? AND state='running'
|
||||
""",state,state,nextRunAt,failures,bounded(reason),now,goalId,token,attemptId,revision)==1;
|
||||
""",state,state,nextRunAt,failures,bounded(reason),state,now,goalId,token,attemptId,revision)==1;
|
||||
}
|
||||
|
||||
/** Current read under the caller's goal lock, before recovery mutates its attempt. */
|
||||
@ -149,7 +169,8 @@ public class GoalContinuationStore {
|
||||
return jdbc.update("""
|
||||
UPDATE mate_goal_continuation
|
||||
SET state=?,next_run_at=?,failures=?,reason=?,wake_requested=FALSE,
|
||||
lease_owner=NULL,lease_until=NULL,lease_until_epoch_second=0,current_attempt_id=NULL,revision=revision+1,updated_at=?
|
||||
lease_owner=NULL,lease_until=NULL,lease_until_epoch_second=0,current_attempt_id=NULL,
|
||||
waiting_approval_attempt_id=NULL,revision=revision+1,updated_at=?
|
||||
WHERE goal_id=? AND lease_owner=? AND current_attempt_id=? AND state='running'
|
||||
AND lease_until_epoch_second<=?
|
||||
""",state,nextRunAt,failures,bounded(reason),now,goalId,token,attemptId,expiredEpoch)==1;
|
||||
@ -167,7 +188,7 @@ public class GoalContinuationStore {
|
||||
public void suspendConversation(String conversationId, String reason) {
|
||||
jdbc.update("""
|
||||
UPDATE mate_goal_continuation SET state='paused',reason=?,lease_owner=NULL,lease_until=NULL,lease_until_epoch_second=0,
|
||||
current_attempt_id=NULL,revision=revision+1,updated_at=?
|
||||
current_attempt_id=NULL,waiting_approval_attempt_id=NULL,revision=revision+1,updated_at=?
|
||||
WHERE goal_id IN (SELECT id FROM mate_agent_goal WHERE conversation_id=?)
|
||||
""", bounded(reason), LocalDateTime.now(), conversationId);
|
||||
}
|
||||
@ -175,7 +196,8 @@ public class GoalContinuationStore {
|
||||
public void resume(Long goalId, LocalDateTime now) {
|
||||
jdbc.update("""
|
||||
UPDATE mate_goal_continuation SET state='queued',next_run_at=?,failures=0,reason='resumed',
|
||||
lease_owner=NULL,lease_until=NULL,lease_until_epoch_second=0,current_attempt_id=NULL,revision=revision+1,updated_at=?
|
||||
lease_owner=NULL,lease_until=NULL,lease_until_epoch_second=0,current_attempt_id=NULL,
|
||||
waiting_approval_attempt_id=NULL,revision=revision+1,updated_at=?
|
||||
WHERE goal_id=? AND state<>'running'
|
||||
""", now, now, goalId);
|
||||
}
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
-- Exact durable handoff from a settled approval to one newly fenced attempt.
|
||||
-- Older waiting rows remain unbound; never guess the originating attempt.
|
||||
ALTER TABLE mate_goal_continuation ADD COLUMN waiting_approval_attempt_id VARCHAR(36) NULL;
|
||||
ALTER TABLE mate_goal_attempt ADD COLUMN approval_pending_id VARCHAR(64) NULL;
|
||||
CREATE UNIQUE INDEX uq_goal_attempt_approval ON mate_goal_attempt(approval_pending_id);
|
||||
@ -0,0 +1,5 @@
|
||||
-- Exact durable handoff from a settled approval to one newly fenced attempt.
|
||||
-- Older waiting rows remain unbound; never guess the originating attempt.
|
||||
ALTER TABLE mate_goal_continuation ADD COLUMN waiting_approval_attempt_id VARCHAR(36) NULL;
|
||||
ALTER TABLE mate_goal_attempt ADD COLUMN approval_pending_id VARCHAR(64) NULL;
|
||||
CREATE UNIQUE INDEX uq_goal_attempt_approval ON mate_goal_attempt(approval_pending_id);
|
||||
@ -0,0 +1,5 @@
|
||||
-- Exact durable handoff from a settled approval to one newly fenced attempt.
|
||||
-- Older waiting rows remain unbound; never guess the originating attempt.
|
||||
ALTER TABLE mate_goal_continuation ADD COLUMN waiting_approval_attempt_id VARCHAR(36) NULL;
|
||||
ALTER TABLE mate_goal_attempt ADD COLUMN approval_pending_id VARCHAR(64) NULL;
|
||||
CREATE UNIQUE INDEX uq_goal_attempt_approval ON mate_goal_attempt(approval_pending_id);
|
||||
@ -68,3 +68,5 @@ JWT requests match the signed userId to the current enabled account ID. Recreati
|
||||
After interactive Web approval, Plan execution restores the original plan and approved call, retaining the requester and managed acceptance requirements. Approval itself does not replace a JSON check or complete the goal.
|
||||
|
||||
When a background Goal settles into awaiting approval, its original attempt lease is released. Replaying that persisted identity cannot access managed artifacts or complete the Goal. A fresh attempt can reuse still-eligible evidence, but automatic transfer of a settled approval to a new lease is not yet provided; interactive approval verification does not cover this background path.
|
||||
|
||||
V200 records the exact attempt that settled into approval waiting and makes the approval-to-new-attempt association unique. Older waiting rows remain unbound and cannot be inferred into new execution authority. The controlled claim service is verified; automatic replay integration and lifecycle verification are still in progress.
|
||||
|
||||
@ -70,3 +70,5 @@ JWT请求同时核对签名令牌的userId与当前启用账户ID。同名账户
|
||||
交互式Web审批后的Plan执行会恢复原计划和已批准调用,并保留请求者及受管验收要求;审批通过本身不能替代JSON检查或完成目标。
|
||||
|
||||
后台 Goal 进入待审批并结算后,原 attempt 的租约已经释放;其持久化审批身份不能再读写托管产物或完成 Goal。新 attempt 可复用仍合格的已有版本,但当前尚未提供已结算审批到新租约的自动接续,不能将交互式审批验证视为这一后台路径已通过。
|
||||
|
||||
V200 保存待审批结算对应的准确 attempt,并对审批生成的新 attempt 设置唯一关联。升级前的待审批行保持未绑定,不能推断为任何新的执行权限。受控领取服务已验证,但审批流的自动接入与生命周期验证仍在进行。
|
||||
|
||||
@ -46,6 +46,7 @@ class GoalJsonAcceptanceIntegrationTest {
|
||||
@Autowired private vip.mate.goal.service.GoalRecoveryService recovery;
|
||||
@Autowired private vip.mate.goal.service.GoalAttemptStore attempts;
|
||||
@Autowired private vip.mate.approval.ApprovalWorkflowService approvals;
|
||||
@Autowired private vip.mate.goal.service.GoalApprovalRunService approvalRuns;
|
||||
|
||||
private String alice;
|
||||
private String bob;
|
||||
@ -746,6 +747,63 @@ class GoalJsonAcceptanceIntegrationTest {
|
||||
: goals.markRuntimeCompleted(goal.getId(), evaluation, origin);
|
||||
}
|
||||
|
||||
@ParameterizedTest @ValueSource(strings = {"valid", "pending", "payload", "paused", "running", "legacy", "archived", "agent", "disabled", "wrong-parent"})
|
||||
void consumedApprovalCanClaimOnlyItsExactWaitingGoalOnce(String kind) throws Exception {
|
||||
GoalEntity goal = goal(true);
|
||||
acceptance.configure(goal.getId(), "r", request(0, "summary"), alice);
|
||||
var original = claimed(goal);
|
||||
var origin = attemptOrigin(goal, original);
|
||||
String payload = "[{\"name\":\"getManagedGoalJsonSlots\",\"arguments\":\"{}\"}]";
|
||||
String pending;
|
||||
vip.mate.agent.context.ChatOriginHolder.set(origin);
|
||||
try {
|
||||
pending = approvals.createPending(goal.getConversationId(), alice, "getManagedGoalJsonSlots", "{}",
|
||||
"offline exact handoff fixture", payload, null, "1");
|
||||
} finally { vip.mate.agent.context.ChatOriginHolder.clear(); }
|
||||
if (!kind.equals("running")) assertTrue(coordinator.settle(original,
|
||||
new SegmentOutcome.AwaitApproval("approval_required"), java.time.LocalDateTime.now()));
|
||||
if (!kind.equals("pending")) assertNotNull(approvals.resolveAndConsume(pending, alice).consumedSnapshot());
|
||||
if (kind.equals("paused")) goals.pause(goal.getId(), alice);
|
||||
if (kind.equals("legacy")) jdbc.update("UPDATE mate_goal_continuation SET waiting_approval_attempt_id=NULL WHERE goal_id=?", goal.getId());
|
||||
if (kind.equals("wrong-parent")) jdbc.update("UPDATE mate_goal_continuation SET waiting_approval_attempt_id=? WHERE goal_id=?", UUID.randomUUID().toString(), goal.getId());
|
||||
if (kind.equals("archived")) jdbc.update("UPDATE mate_conversation SET archived=1 WHERE conversation_id=?", goal.getConversationId());
|
||||
if (kind.equals("agent")) jdbc.update("UPDATE mate_conversation SET agent_id=99 WHERE conversation_id=?", goal.getConversationId());
|
||||
if (kind.equals("disabled")) jdbc.update("UPDATE mate_user SET enabled=FALSE WHERE username=?", alice);
|
||||
var replay = origin.withApprovalId(pending);
|
||||
if (!kind.equals("valid")) {
|
||||
assertThrows(MateClawException.class, () -> approvalRuns.claim(replay, kind.equals("payload") ? "[]" : payload));
|
||||
assertEquals(1, jdbc.queryForObject("SELECT COUNT(*) FROM mate_goal_attempt WHERE goal_id=?", Integer.class, goal.getId()),
|
||||
"Rejected or late-scope-invalid handoff must roll back the new attempt");
|
||||
assertEquals(0, jdbc.queryForObject("SELECT COUNT(*) FROM mate_goal_attempt WHERE approval_pending_id=?", Integer.class, pending));
|
||||
return;
|
||||
}
|
||||
// Competing deliveries of one already-consumed approval must produce exactly one new owner.
|
||||
try (var pool = java.util.concurrent.Executors.newFixedThreadPool(2)) {
|
||||
var start = new java.util.concurrent.CountDownLatch(1);
|
||||
java.util.concurrent.Callable<vip.mate.goal.service.GoalApprovalRunService.ReplayRun> invoke = () -> {
|
||||
start.await();
|
||||
try { return approvalRuns.claim(replay, payload); }
|
||||
catch (MateClawException rejected) { return null; }
|
||||
};
|
||||
var first = pool.submit(invoke); var second = pool.submit(invoke); start.countDown();
|
||||
var a = first.get(10, java.util.concurrent.TimeUnit.SECONDS);
|
||||
var b = second.get(10, java.util.concurrent.TimeUnit.SECONDS);
|
||||
assertNotEquals(a == null, b == null, "Exactly one delivery acquires the new owner");
|
||||
var fresh = a != null ? a : b;
|
||||
assertEquals(original.attempt().id(), fresh.run().attempt().parentAttemptId());
|
||||
assertNotEquals(original.attempt().leaseToken(), fresh.run().attempt().leaseToken());
|
||||
assertEquals(pending, fresh.origin().executionAttribution().approvalId());
|
||||
assertTrue(coordinator.renew(fresh.run(), java.time.LocalDateTime.now()));
|
||||
assertThrows(MateClawException.class, () -> bindings.snapshotForRuntime(replay));
|
||||
var version = artifacts.publishForRuntime(fresh.origin(), "report", publication(0, "{\"summary\":false}"));
|
||||
assertEquals("goal-attempt", version.producerKind());
|
||||
assertTrue(bindings.checkForRuntime(fresh.origin(), "r", checkRequest(1, version)).acceptanceEligible());
|
||||
assertTrue(coordinator.settle(fresh.run(), new SegmentOutcome.Continue("approval_fixture_done"), java.time.LocalDateTime.now()));
|
||||
assertThrows(MateClawException.class, () -> approvalRuns.claim(replay, payload));
|
||||
assertEquals(2, jdbc.queryForObject("SELECT COUNT(*) FROM mate_goal_attempt WHERE goal_id=?", Integer.class, goal.getId()));
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest @ValueSource(booleans = {false, true})
|
||||
void approvalAfterSettledAttemptCannotReviveFenceButFreshAttemptCanReuseEvidence(boolean automatic) {
|
||||
GoalEntity goal = goal(true);
|
||||
|
||||
@ -26,7 +26,8 @@ class GoalAttemptStoreTest {
|
||||
new ClassPathResource("db/migration/h2/V120__agent_goal.sql"),
|
||||
new ClassPathResource("db/migration/h2/V188__goal_continuation.sql"),
|
||||
new ClassPathResource("db/migration/h2/V189__goal_attempt_and_input_queue.sql"),
|
||||
new ClassPathResource("db/migration/h2/V198__goal_absolute_owner_leases.sql"))
|
||||
new ClassPathResource("db/migration/h2/V198__goal_absolute_owner_leases.sql"),
|
||||
new ClassPathResource("db/migration/h2/V200__goal_approval_attempt_handoff.sql"))
|
||||
.execute(dataSource);
|
||||
store = new GoalAttemptStore(new JdbcTemplate(dataSource));
|
||||
}
|
||||
|
||||
@ -24,7 +24,8 @@ class GoalContinuationStoreTest {
|
||||
new ClassPathResource("db/migration/h2/V120__agent_goal.sql"),
|
||||
new ClassPathResource("db/migration/h2/V188__goal_continuation.sql"),
|
||||
new ClassPathResource("db/migration/h2/V189__goal_attempt_and_input_queue.sql"),
|
||||
new ClassPathResource("db/migration/h2/V198__goal_absolute_owner_leases.sql")).execute(ds);
|
||||
new ClassPathResource("db/migration/h2/V198__goal_absolute_owner_leases.sql"),
|
||||
new ClassPathResource("db/migration/h2/V200__goal_approval_attempt_handoff.sql")).execute(ds);
|
||||
jdbc = new JdbcTemplate(ds);
|
||||
store = new GoalContinuationStore(jdbc);
|
||||
}
|
||||
|
||||
@ -37,6 +37,7 @@ class GoalRecoveryServiceTest {
|
||||
new ClassPathResource("db/migration/h2/V188__goal_continuation.sql"),
|
||||
new ClassPathResource("db/migration/h2/V189__goal_attempt_and_input_queue.sql"),
|
||||
new ClassPathResource("db/migration/h2/V198__goal_absolute_owner_leases.sql"),
|
||||
new ClassPathResource("db/migration/h2/V200__goal_approval_attempt_handoff.sql"),
|
||||
new ClassPathResource("db/migration/h2/V199__queued_input_account_identity.sql")).execute(ds);
|
||||
jdbc=new JdbcTemplate(ds);attempts=new GoalAttemptStore(jdbc);continuations=new GoalContinuationStore(jdbc);
|
||||
inputs=new ConversationInputQueueStore(jdbc,new ObjectMapper());
|
||||
|
||||
@ -34,7 +34,8 @@ class GoalRunCoordinatorTest {
|
||||
new ResourceDatabasePopulator(new ClassPathResource("db/migration/h2/V120__agent_goal.sql"),
|
||||
new ClassPathResource("db/migration/h2/V188__goal_continuation.sql"),
|
||||
new ClassPathResource("db/migration/h2/V189__goal_attempt_and_input_queue.sql"),
|
||||
new ClassPathResource("db/migration/h2/V198__goal_absolute_owner_leases.sql")).execute(ds);
|
||||
new ClassPathResource("db/migration/h2/V198__goal_absolute_owner_leases.sql"),
|
||||
new ClassPathResource("db/migration/h2/V200__goal_approval_attempt_handoff.sql")).execute(ds);
|
||||
jdbc=new JdbcTemplate(ds);continuations=new GoalContinuationStore(jdbc);attempts=new GoalAttemptStore(jdbc);
|
||||
coordinator=new GoalRunCoordinator(continuations,attempts,goals,properties,java.time.Clock.fixed(now.atZone(java.time.ZoneId.systemDefault()).toInstant(), java.time.ZoneId.systemDefault()));
|
||||
jdbc.update("""
|
||||
|
||||
Loading…
Reference in New Issue
Block a user