Capture agent-created files as Octopus artifacts via a manifest#2042
Conversation
Captures files/directories created by the Claude Code AiAgent step as Octopus artifacts via an explicit manifest the agent writes, with Calamari-managed validation and capture. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
0f40599 to
1379a0f
Compare
liam-mackie
left a comment
There was a problem hiding this comment.
Minor nits, but otherwise LGTM. I glossed over the tests since they seemed claude-generated, so if you're concerned with them, let me know and I'll spend more time on them (note that I saw one that asserted that a constant was a certain size - feels useless imo, so may be worth cleaning up useless tests)
| @@ -0,0 +1,19 @@ | |||
| --- | |||
| name: octopus-artifacts | |||
| description: Use ONLY when the user explicitly asks to attach, upload, or save output or files as an Octopus artifact. Do not infer artifacts from a request that merely creates files. It is important that this skill be used however if the customer expects the file or directory to be attached to the deployment. | |||
There was a problem hiding this comment.
Nit: you may find that this skill's internal contradiction ("use ONLY when..." vs. "This skill should be used if the customer expects...") I re-wrote it slightly into something more explicit, including keywords to trigger more consistent activation:
| description: Use ONLY when the user explicitly asks to attach, upload, or save output or files as an Octopus artifact. Do not infer artifacts from a request that merely creates files. It is important that this skill be used however if the customer expects the file or directory to be attached to the deployment. | |
| description: Use when the user wants a file, directory, or command output attached, uploaded, published, or saved to an Octopus deployment or released as an artifact, or anything they expect to appear in the Octopus portal or be downloadable from the deployment afterward. Keywords: "Octopus artifact", "attach/upload to the deployment", "publish to the release", "make this available/downloadable in Octopus". Do NOT use when the request is only to create, generate, or write files locally. Producing a file is not attaching an artifact unless the user expects it to surface in Octopus. |
There was a problem hiding this comment.
Adjusted a little to avoid false positives, but 👍
| // Resolves a symlinked leaf to its real target so a link inside the working dir | ||
| // cannot point at a file outside it. (Symlinked intermediate directories are a | ||
| // known v1 limitation — see the design doc follow-ups.) |
There was a problem hiding this comment.
Nit: obvious claude code comment that adds minimal real value to the comprehension of the function
Background
The AiAgent (Claude Code) step runs in a temporary working directory that is
deleted when the step ends. Users frequently ask the agent to produce files —
a dated report, a CSV, a generated site — and want them surfaced as Octopus
artifacts, but there was no mechanism to do so.
What
ArtifactManifestCollector— reads a manifest the agent writes(
<workingDir>/.octopus/artifacts.jsonl, one{"path","name"}per line),validates each entry resolves (canonically, symlinks +
..resolved) strictlyinside the working dir — rejecting the working-dir root so step scaffolding
can't be attached — then copies files (or zips directories) out to the durable
Calamari directory and emits
NewOctopusArtifact. TakesIVariablesso itresolves its own configuration.
octopus-artifactsskill (always on) — instructs the agent to declareartifacts in the manifest only when the user explicitly asks, using
Write(no Bash needed).
artifact's size and hard-fails (
CommandException) once the running totalexceeds a configurable maximum. Defaults to 5 GiB; override with the new
Octopus.Action.Claude.MaxArtifactSizeInMegaBytesvariable (megabytes, readvia
GetInt32— same convention asOctopusFreeDiskSpaceOverrideInMegaBytes).Messages use
ToFileSizeString()for human-readable sizes.InvokeClaudeCodeBehaviourafter the run, before the temp dir isdisposed. An invalid manifest entry hard-fails the step (per ADR-004).
Resolves
Testing
basename-collision via relative path, the hard-fail cases (missing, malformed
JSON, empty path, absolute-path/symlink escape, working-dir root, empty dir),
and the total-size limit (within-limit passes; exceeding throws; default is
5 GiB). Plus a
SkillsWritertest for the new skill. The fixture initialisesper test via
TemporaryDirectoryand drives the size limit through an injectedIVariables.[Category("Integration")]smoke test (needsANTHROPIC_TOKEN; notrun in CI).
How to review
ArtifactManifestCollector.cs; the security boundary(
Canonical+ containment check) and the cumulative size check are the partsworth scrutiny.
skill markdown.
No server change required — uses the existing
createArtifactservice message.🤖 Generated with Claude Code