fix(reborn): harden capability approval lifecycle - #3111
Conversation
Follow-up to the capability host base slice.\n\n[skip-regression-check]
6c13dc5 to
b523db2
Compare
There was a problem hiding this comment.
Code Review
This pull request adds support for asynchronous process spawn approvals, aligning it with the existing dispatch approval flow. Key updates include refactoring the approval resolution logic, modifying the capability host to handle pending spawn requests and run-state transitions, and implementing lease revocation on invocation failure. Additionally, filesystem-based stores now ignore missing files during directory scans. Feedback recommends centralizing the duplicated file-reading logic within the run-state stores into a helper function to improve maintainability and consistency.
| let bytes = match self.filesystem.read_file(&entry.path).await { | ||
| Ok(bytes) => bytes, | ||
| Err(error) if is_not_found(&error) => continue, | ||
| Err(error) => return Err(error.into()), | ||
| }; |
There was a problem hiding this comment.
This logic to gracefully handle NotFound errors is duplicated in FilesystemApprovalRequestStore::records_for_scope (lines 826-830). To improve maintainability and ensure consistent behavior, consider extracting this into a centralized helper function that handles errors gracefully by returning None, as per repository guidelines for resource parsing. Additionally, use the let-else pattern to skip missing files without aborting the scan.
Example helper:
async fn read_file_tolerant<F: RootFilesystem>(
filesystem: &F,
path: &VirtualPath,
) -> Result<Option<Vec<u8>>, RunStateError> {
match filesystem.read_file(path).await {
Ok(bytes) => Ok(Some(bytes)),
Err(error) if is_not_found(&error) => Ok(None),
Err(error) => Err(error.into()),
}
}Example usage:
let Some(bytes) = read_file_tolerant(self.filesystem, &entry.path).await? else {
continue;
};References
- Centralize parsing logic for resources that can fail into a single function that handles errors gracefully (e.g., by returning
None) to ensure consistent behavior across all call sites. - Use
let Some(...) = ... else { continue; }instead of the?operator when scanning resources to skip items that don't match the expected format without prematurely aborting the entire scan.
Follow-up to the capability host base slice.\n\n[skip-regression-check]
Summary
Follow-up to #3071 after the capability-host base PR landed. Hardens the approval/run-state/lease lifecycle around the review findings:
authorize_spawn_with_trustreturnsRequireApproval.CapabilityHost::resume_spawn_jsonso approved spawn requests can claim the matching lease, start the process, consume the lease, and complete run state.correlation_idin addition to action/requester/fingerprint before persisting or resuming approvals.Notes
ironclaw_capabilities.approve_dispatchnow delegates through a shared action-specific helper, andapprove_spawnhandlesAction::SpawnCapability.skip-regression-checklabel and[skip-regression-check]commit marker are applied because the workflow false-negatives on crate-local Rust test changes, even though this PR adds multiple regression tests.Verification
All passed locally.