Skip to content
Merged
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 34 additions & 17 deletions src/agent_author.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1263,7 +1263,7 @@ mod tests {
&declaration("worker", "h", None, "catalog"),
);

let added = add_resource(
let added = add_resource_with_selector(
root,
"h.worker",
"h",
Expand All @@ -1272,13 +1272,14 @@ mod tests {
"github-issue://example/project/123",
"release work item",
None,
None,
)
.unwrap();
assert_eq!(added.result, AuthorOutcome::Changed);
assert_eq!(added.identity, "h.worker");
assert_eq!(added.inactive_reason, None);

add_resource(
add_resource_with_selector(
root,
"h.worker",
"h",
Expand All @@ -1287,13 +1288,14 @@ mod tests {
"worktree://github.com/example/project/change",
"primary checkout",
None,
None,
)
.unwrap();
let two_bindings = fs::read_to_string(&path).unwrap();

// An identical request proves the binding rather than rewriting the declaration.
assert_eq!(
add_resource(
add_resource_with_selector(
root,
"h.worker",
"h",
Expand All @@ -1302,6 +1304,7 @@ mod tests {
"github-issue://example/project/123",
"release work item",
None,
None,
)
.unwrap()
.result,
Expand All @@ -1311,7 +1314,7 @@ mod tests {

// Re-declaring an existing name updates it in place, keeping its position and siblings.
assert_eq!(
add_resource(
add_resource_with_selector(
root,
"h.worker",
"h",
Expand All @@ -1320,6 +1323,7 @@ mod tests {
"github-issue://example/project/456",
"follow-up work item",
Some("superseded by the follow-up"),
None,
)
.unwrap()
.result,
Expand All @@ -1345,7 +1349,7 @@ mod tests {

// The request declares the complete binding, so an omitted inactive-reason clears it.
assert_eq!(
add_resource(
add_resource_with_selector(
root,
"h.worker",
"h",
Expand All @@ -1354,6 +1358,7 @@ mod tests {
"github-issue://example/project/456",
"follow-up work item",
None,
None,
)
.unwrap()
.result,
Expand All @@ -1379,7 +1384,7 @@ mod tests {

// Hand-authored property order and spacing are proven, not re-rendered.
assert_eq!(
add_resource(
add_resource_with_selector(
root,
"h.worker",
"h",
Expand All @@ -1388,6 +1393,7 @@ mod tests {
"github-issue://example/project/123",
"release work item",
None,
None,
)
.unwrap()
.result,
Expand All @@ -1405,7 +1411,7 @@ mod tests {
"h/worker/agent.kdl",
&declaration("worker", "h", None, "catalog"),
);
add_resource(
add_resource_with_selector(
root,
"h.worker",
"h",
Expand All @@ -1414,9 +1420,10 @@ mod tests {
"github-issue://example/project/123",
"release work item",
None,
None,
)
.unwrap();
add_resource(
add_resource_with_selector(
root,
"h.worker",
"h",
Expand All @@ -1425,6 +1432,7 @@ mod tests {
"worktree://github.com/example/project/change",
"primary checkout",
None,
None,
)
.unwrap();

Expand Down Expand Up @@ -1461,7 +1469,7 @@ mod tests {
"h/worker/agent.kdl",
&declaration("worker", "h", None, "catalog"),
);
add_resource(
add_resource_with_selector(
root,
"h.worker",
"h",
Expand All @@ -1470,9 +1478,10 @@ mod tests {
"github-issue://example/project/123",
"release work item",
Some("merged and retained for traceability"),
None,
)
.unwrap();
add_resource(
add_resource_with_selector(
root,
"h.worker",
"h",
Expand All @@ -1481,6 +1490,7 @@ mod tests {
"worktree://github.com/example/project/change",
"primary checkout",
None,
None,
)
.unwrap();
let before = fs::read_to_string(&path).unwrap();
Expand Down Expand Up @@ -1561,7 +1571,7 @@ mod tests {
);
let untouched = fs::read_to_string(&nix_owned).unwrap();

add_resource(
add_resource_with_selector(
root,
"h.child",
"h",
Expand All @@ -1570,11 +1580,12 @@ mod tests {
"github-issue://example/project/1",
"supervised work item",
None,
None,
)
.unwrap();

assert_eq!(
add_resource(
add_resource_with_selector(
root,
"h.sibling",
"h",
Expand All @@ -1583,6 +1594,7 @@ mod tests {
"github-issue://example/project/1",
"reaching across the fleet",
None,
None,
)
.unwrap_err()
.code(),
Expand All @@ -1595,7 +1607,7 @@ mod tests {
"resource-not-authorized"
);
assert_eq!(
add_resource(
add_resource_with_selector(
root,
"h.nix",
"h",
Expand All @@ -1604,6 +1616,7 @@ mod tests {
"github-issue://example/project/1",
"Nix owns this declaration",
None,
None,
)
.unwrap_err()
.code(),
Expand All @@ -1625,7 +1638,7 @@ mod tests {
Some(""),
),
] {
let error = add_resource(
let error = add_resource_with_selector(
root,
"h.child",
"h",
Expand All @@ -1634,6 +1647,7 @@ mod tests {
uri,
reason,
inactive_reason,
None,
)
.unwrap_err();
assert_eq!(error.code(), "invalid-resource", "{name}: {error}");
Expand All @@ -1647,7 +1661,7 @@ mod tests {
);

// #345 widened the envelope: a catalog-relative carrier path is a valid binding uri.
add_resource(
add_resource_with_selector(
root,
"h.child",
"h",
Expand All @@ -1656,6 +1670,7 @@ mod tests {
"carriers/goal.md",
"Catalog-relative carrier.",
None,
None,
)
.expect("a catalog-relative carrier path is admitted");
assert_eq!(fs::read_to_string(&nix_owned).unwrap(), untouched);
Expand All @@ -1672,7 +1687,7 @@ mod tests {
);
let exact = "vendor+Thing://Authority.Example/Exact%20Identity?Query=A%2Fb#Frag%20Ment";

add_resource(
add_resource_with_selector(
root,
"h.worker",
"h",
Expand All @@ -1681,6 +1696,7 @@ mod tests {
exact,
"exact vendor identity",
None,
None,
)
.unwrap();
assert_eq!(bound(root, "worker", "subject").uri(), exact);
Expand All @@ -1691,7 +1707,7 @@ mod tests {

// A byte-identical re-declaration is a proven no-op, not a rewrite.
assert_eq!(
add_resource(
add_resource_with_selector(
root,
"h.worker",
"h",
Expand All @@ -1700,6 +1716,7 @@ mod tests {
exact,
"exact vendor identity",
None,
None,
)
.unwrap()
.result,
Expand Down
24 changes: 0 additions & 24 deletions src/agent_author/resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,30 +39,6 @@ pub struct ResourceRenameReceipt {
///
/// st2 preserves the binding for readers; it resolves nothing and grants nothing. `uri` is the
/// exact absolute identity and is stored byte for byte with no normalization.
#[allow(clippy::too_many_arguments)]
pub fn add_resource(
catalog_root: &Path,
selector: &str,
this_host: &str,
actor: Option<&str>,
name: &str,
uri: &str,
reason: &str,
inactive_reason: Option<&str>,
) -> Result<ResourceAddReceipt, AuthorError> {
add_resource_with_selector(
catalog_root,
selector,
this_host,
actor,
name,
uri,
reason,
inactive_reason,
None,
)
}

#[allow(clippy::too_many_arguments)]
pub fn add_resource_with_selector(
catalog_root: &Path,
Expand Down
10 changes: 6 additions & 4 deletions src/codex_app_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ pub enum CodexObservedState {

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum CodexHoldReason {
pub(crate) enum CodexHoldReason {
ActiveWithoutTurn,
ConflictingTurn,
Review,
Expand All @@ -249,7 +249,7 @@ pub enum CodexHoldReason {

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum CodexTerminalError {
pub(crate) enum CodexTerminalError {
SystemError,
ProviderAuthRejected,
}
Expand Down Expand Up @@ -3002,7 +3002,8 @@ fn atomic_json(path: &Path, value: &impl Serialize) -> Result<()> {
result
}

pub fn load_current_binding(
#[cfg(test)]
fn load_current_binding(
path: &Path,
runtime: &CodexRuntime,
) -> Result<Option<CodexThreadBinding>> {
Expand All @@ -3025,7 +3026,8 @@ pub fn load_current_binding(
Ok(Some(binding))
}

pub fn load_current_control_state(
#[cfg(test)]
fn load_current_control_state(
path: &Path,
runtime: &CodexRuntime,
binding: &CodexThreadBinding,
Expand Down
19 changes: 6 additions & 13 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,13 +122,6 @@ fn lock_now(context_dir: &Path) -> anyhow::Result<fs::File> {
.context("acquire now.md writer lock")
}

/// Append one decision to the log. `decision` and `why` must be single non-empty lines (the log is a
/// scannable list; multi-line reasoning belongs in a doc). Renders `- <ISO> <decision>. why: <why>.`
/// into a fresh `decisions/<unix-ms>-<rand6>.md`. Returns the entry's filename.
pub fn append_decision(context_dir: &Path, decision: &str, why: &str) -> anyhow::Result<String> {
append_decision_to_dir(&decisions_dir(context_dir), decision, why)
}

pub fn append_decision_to_dir(dir: &Path, decision: &str, why: &str) -> anyhow::Result<String> {
let decision = decision.trim();
let why = why.trim();
Expand Down Expand Up @@ -285,14 +278,14 @@ mod tests {
fn append_decisions_are_ordered_bullets() {
let tmp = tempfile::tempdir().unwrap();
let dir = context_dir(tmp.path());
append_decision(
&dir,
append_decision_to_dir(
&decisions_dir(&dir),
"use hook-enforced perms",
"never prompts an autonomous pty",
)
.unwrap();
std::thread::sleep(std::time::Duration::from_millis(2));
append_decision(&dir, "defer shims", "scope enforcement is follow-on").unwrap();
append_decision_to_dir(&decisions_dir(&dir), "defer shims", "scope enforcement is follow-on").unwrap();

let dec = read(&dir, View::Decisions);
let lines: Vec<&str> = dec.lines().collect();
Expand All @@ -315,8 +308,8 @@ mod tests {
fn append_rejects_empty_or_multiline() {
let tmp = tempfile::tempdir().unwrap();
let dir = context_dir(tmp.path());
assert!(append_decision(&dir, "", "why").is_err());
assert!(append_decision(&dir, "d", "").is_err());
assert!(append_decision(&dir, "line1\nline2", "why").is_err());
assert!(append_decision_to_dir(&decisions_dir(&dir), "", "why").is_err());
assert!(append_decision_to_dir(&decisions_dir(&dir), "d", "").is_err());
assert!(append_decision_to_dir(&decisions_dir(&dir), "line1\nline2", "why").is_err());
}
}
Loading
Loading