Skip to content

Store session automation artifacts with their sessions - #1824

Closed
Aaronontheweb wants to merge 11 commits into
devfrom
spec/session-owned-automation-artifacts
Closed

Store session automation artifacts with their sessions#1824
Aaronontheweb wants to merge 11 commits into
devfrom
spec/session-owned-automation-artifacts

Conversation

@Aaronontheweb

@Aaronontheweb Aaronontheweb commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Store new current-session reminder definitions and history in the source session directory.
  • Store new background job definitions and logs in the source session directory.
  • Keep channel and sessionless reminder files in the daemon reminder directory.
  • Preserve current paths for existing reminder and job files.
  • Update existing files in place without an automatic migration.
  • Reject duplicate IDs and session owner mismatches.
  • Reject generic file and shell writes to session automation artifacts.
  • Reject writes through symbolic link aliases.

Compatibility

Existing scheduler payloads remain ID-only.

Existing reminder definitions, histories, job definitions, and logs do not move.

The managers keep their current ownership, limits, retry rules, passivation rules, and delivery routes.

The default agent reminder list shows active reminders.
The operator CLI list includes disabled and terminal reminders.

Validation

  • Netclaw.Actors.Tests: 2,940 passed.
  • Netclaw.Security.Tests: 685 passed.
  • Netclaw.Daemon.Tests: 951 passed.
  • Slopwatch found zero issues.
  • The file header check passed.
  • Strict OpenSpec validation passed.
  • git diff --check passed.
  • The final adversarial review found no issues.

The eval suite did not run.
This change does not modify an eval-sensitive surface.

Closes #1820

@Aaronontheweb Aaronontheweb added cleanup Code quality improvements and tech debt reduction reliability Retries, resilience, graceful degradation reminders Reminder scheduling, execution, and history tools Issues related to agent tools: file_read, web_search, shell_execute, image processing, etc. labels Aug 8, 2026
public string GetOutputLogPath(BackgroundJobId id) =>
Path.Combine(GetOutputDirectory(id), "output.log");
public string GetOutputLogPath(BackgroundJobId id, SessionId sessionId) =>
Path.Combine(GetOutputDirectory(id, sessionId), "output.log");
Comment thread src/Netclaw.Actors/Jobs/BackgroundJobDefinitionStore.cs Fixed
Comment thread src/Netclaw.Actors/Jobs/BackgroundJobDefinitionStore.cs Fixed
Comment thread src/Netclaw.Actors/Jobs/BackgroundJobDefinitionStore.cs Fixed
Comment thread src/Netclaw.Actors/Jobs/BackgroundJobDefinitionStore.cs Fixed
Comment thread src/Netclaw.Actors/Protocol/SessionDirectoryHelper.cs Fixed
Comment thread src/Netclaw.Actors/Reminders/ReminderHistoryStore.cs Fixed
Comment thread src/Netclaw.Actors/Reminders/ReminderDefinitionStore.cs Fixed
Comment thread src/Netclaw.Actors/Reminders/ReminderDefinitionStore.cs Fixed
Comment thread src/Netclaw.Actors/Reminders/ReminderDefinitionStore.cs Fixed
Comment thread src/Netclaw.Actors/Reminders/ReminderDefinitionStore.cs Fixed
Comment thread src/Netclaw.Actors/Reminders/ReminderDefinitionStore.cs Fixed
Comment thread src/Netclaw.Actors/Reminders/ReminderDefinitionStore.cs Fixed
Comment thread src/Netclaw.Actors/Reminders/ReminderDefinitionStore.cs Fixed
Comment thread src/Netclaw.Actors/Reminders/ReminderDefinitionStore.cs Fixed
Comment thread src/Netclaw.Actors/Reminders/ReminderDefinitionStore.cs Fixed
Comment on lines +234 to +238
foreach (var path in FindDefinitionPaths(id))
{
if (TryReadValidDefinition(path) is { } stored && stored.Definition.Id == id)
definitions.Add(stored);
}
Comment on lines +237 to +241
foreach (var path in FindDefinitionPaths(id))
{
if (TryReadValidDefinition(path) is { } stored && stored.Definition.Id == id)
definitions.Add(stored);
}
Comment on lines +249 to +256
foreach (var jobDirectory in EnumerateSessionJobDirectories())
{
var path = GetContainedPath(jobDirectory, fileName, id);
if (IsSafeSessionPath(path, out var reason))
AddIfPresent(paths, path);
else
_logger.LogError("Session job definition {Path} is unsafe: {Reason}", path, reason);
}
Comment on lines +252 to +259
foreach (var reminderDirectory in EnumerateSessionReminderDirectories())
{
var path = GetContainedPath(reminderDirectory, fileName, id);
if (IsSafeSessionPath(path, out var reason))
AddIfPresent(paths, path);
else
_logger.LogError("Session reminder definition {Path} is unsafe: {Reason}", path, reason);
}
Comment thread src/Netclaw.Actors.Tests/Tools/AutonomousZoneClampTests.cs Fixed
{
var directory = GetOutputDirectoryPath(GetStorageDirectory(id, sessionId), id);
EnsureSafeOutputPath(directory);
return Path.Combine(directory, "output.log");
Comment thread src/Netclaw.Actors/Jobs/BackgroundJobDefinitionStore.cs Fixed
Comment thread src/Netclaw.Actors/Reminders/ReminderDefinitionStore.cs Fixed
Comment thread src/Netclaw.Actors/Reminders/ReminderDefinitionStore.cs Fixed
Comment on lines +105 to +115
foreach (var autonomous in new[] { true, false })
{
var allowed = policy.TryResolveWritePath(
artifactPath,
Ctx(TrustAudience.Personal, autonomous),
out _,
out var error);

Assert.False(allowed);
Assert.Contains("automation artifacts", error, StringComparison.OrdinalIgnoreCase);
}
Comment thread src/Netclaw.Actors/Tools/ScopedFileAccessPolicy.cs Fixed

@Aaronontheweb Aaronontheweb left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some comments

_paths = paths;
_logger = logger ?? NullLogger<ReminderDefinitionStore>.Instance;
Directory.CreateDirectory(_directory);
Directory.CreateDirectory(_paths.RemindersDirectory);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

}
}

private IEnumerable<string> EnumerateSessionReminderDirectories()

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like we should only do this in instances where we have a stored reminder in the database somewhere. Because otherwise, we might be enumerating thousands of directories potentially.


private bool TryGetSavePath(ReminderDefinition definition, out string path, out string error)
{
var definitions = FindValidDefinitions(definition.Id);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can there possibly be multiple sessions for the same reminder definition? And why pick the first one?


foreach (var sessionDirectory in Directory.EnumerateDirectories(_paths.SessionsDirectory))
{
var jobDirectory = Path.Combine(sessionDirectory, SessionDirectoryHelper.JobsSubdirectory);
@Aaronontheweb

Copy link
Copy Markdown
Collaborator Author

This change is too broad for the next release. We will ship the bounded background-job cleanup in #1821 and revisit session-owned automation storage separately.

@Aaronontheweb
Aaronontheweb deleted the spec/session-owned-automation-artifacts branch August 9, 2026 01:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cleanup Code quality improvements and tech debt reduction reliability Retries, resilience, graceful degradation reminders Reminder scheduling, execution, and history tools Issues related to agent tools: file_read, web_search, shell_execute, image processing, etc.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Clean up completed background jobs and one-shot reminders that are no longer needed

2 participants