Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
25 changes: 21 additions & 4 deletions docs/orgtrack-pm-protocol/schemas/routine.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,11 @@
"properties": {
"type": {
"type": "string",
"enum": ["manual", "schedule", "provider_event"]
"enum": ["manual", "schedule", "one_time", "provider_event"]
},
"cron": { "type": "string" },
"timezone": { "type": "string" },
"at": { "type": "string", "format": "date-time" },
"provider": { "type": "string" },
"eventKind": { "type": "string" },
"filter": {
Expand All @@ -126,25 +127,41 @@
},
"concurrencyPolicy": {
"type": "string",
"enum": ["coalesce", "skip", "queue"],
"enum": ["coalesce", "skip", "queue", "always"],
"default": "skip",
"description": "Behavior when the previous Run is not terminal. coalesce/skip record an AuditEvent without creating a Run; queue creates a pending Run."
"description": "Behavior when the previous Run is not terminal. coalesce/skip record a durable activation outcome, queue records a durable activation that is promoted once the active Run settles, and always creates another Run."
},
"catchUp": {
"type": "string",
"enum": ["none", "fire_once"],
"enum": ["none", "fire_once", "run_all_limited"],
"default": "none",
"description": "Compensation for schedule fires missed while the ORG2 host process was not running."
},
"maxCatchUpRuns": {
"type": "integer",
"minimum": 1,
"description": "Maximum missed activations replayed when catchUp is run_all_limited."
}
},
"allOf": [
{
"if": { "properties": { "type": { "const": "schedule" } } },
"then": { "required": ["cron", "timezone"] }
},
{
"if": { "properties": { "type": { "const": "one_time" } } },
"then": { "required": ["at"] }
},
{
"if": { "properties": { "type": { "const": "provider_event" } } },
"then": { "required": ["provider", "eventKind"] }
},
{
"if": {
"required": ["catchUp"],
"properties": { "catchUp": { "const": "run_all_limited" } }
},
"then": { "required": ["maxCatchUpRuns"] }
}
],
"additionalProperties": false
Expand Down
1 change: 1 addition & 0 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,9 @@ tauri-plugin-updater = "=2.9.0"

[dev-dependencies]

# Parse ephemeral Codex profile layers in runner security/compatibility tests.
toml = "0.8"

# `test-util` only for the test build: it is what lets a test drive Tokio's
# clock (`#[tokio::test(start_paused = true)]`) rather than really sleeping out
# a multi-second timeout. Deliberately absent from the production features.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ use crate::coordination::agent_org_payload_limits as limits;
/// - `(recipient_agent_id, read_at, created_at)` — coordinator / legacy drain query.
/// - `(org_run_id, created_at)` — bounded debug / E2E history pages.
/// - `(request_id)` — RPC correlation lookups.
/// - `(org_run_id, sender_agent_id, client_message_id)` — idempotent user sends.
pub fn init_schema(conn: &Connection) -> SqliteResult<()> {
create_agent_inbox_table(conn)?;
ensure_agent_inbox_column(conn, "causation_inbox_id", "INTEGER")?;
ensure_agent_inbox_column(conn, "display_text", "TEXT")?;
ensure_agent_inbox_column(conn, "client_message_id", "TEXT")?;
let schema = format!(
"CREATE TABLE IF NOT EXISTS agent_inbox_materializations (
inbox_id INTEGER PRIMARY KEY,
Expand Down Expand Up @@ -84,6 +86,9 @@ pub fn init_schema(conn: &Connection) -> SqliteResult<()> {
DROP INDEX IF EXISTS idx_agent_inbox_run_task_assignment_v2;
CREATE INDEX IF NOT EXISTS idx_agent_inbox_request_id
ON agent_inbox(request_id);
CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_inbox_user_message_once
ON agent_inbox(org_run_id, sender_agent_id, client_message_id)
WHERE client_message_id IS NOT NULL;
DROP INDEX IF EXISTS idx_agent_inbox_causation_once;
CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_inbox_causation_recipient_once
ON agent_inbox(
Expand Down Expand Up @@ -165,7 +170,8 @@ fn create_agent_inbox_table(conn: &Connection) -> SqliteResult<()> {
created_at TEXT NOT NULL,
read_at TEXT,
causation_inbox_id INTEGER,
display_text TEXT
display_text TEXT,
client_message_id TEXT
);",
)
}
Expand Down Expand Up @@ -195,6 +201,7 @@ mod tests {
.expect("create legacy inbox table");

init_schema(&conn).expect("upgrade legacy inbox schema");
init_schema(&conn).expect("re-initialize upgraded inbox schema");

let mut stmt = conn
.prepare("PRAGMA table_info(agent_inbox)")
Expand All @@ -206,5 +213,18 @@ mod tests {
.expect("collect inbox columns");
assert!(columns.iter().any(|column| column == "causation_inbox_id"));
assert!(columns.iter().any(|column| column == "display_text"));
assert!(columns.iter().any(|column| column == "client_message_id"));
let has_idempotency_index: bool = conn
.query_row(
"SELECT EXISTS(
SELECT 1 FROM sqlite_master
WHERE type='index'
AND name='idx_agent_inbox_user_message_once'
)",
[],
|row| row.get(0),
)
.expect("inspect Group Chat idempotency index");
assert!(has_idempotency_index);
}
}
Loading
Loading