Classify artifact references in one place - #2479
Merged
Merged
Conversation
Artifact reference groups were derived twice: agent task run results matched exact kinds, and the public reference projection matched broader predicates. The rules had drifted, so the same bundle could group differently depending on which projection a consumer read. Add one classification module both projections compose their groups from. The difference between them is preserved and named: typed classification accepts only declared kinds because it feeds the workspace delta, whose patches a caller may apply, while discovery classification also infers from paths so partially typed bundles stay listable. Fix log extension matching while consolidating. The shared path helper compares trailing path segments, so the .log and .jsonl checks never matched a real filename.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #2432. A bounded slice: one canonical artifact reference classification, not the full artifact kernel.
Problem
Artifact reference groups were derived twice in TypeScript, with rules that had already drifted:
agent-task-run-result.ts:116artifact-references.ts:197artifact_bundlesartifact-bundle/codebox-artifact-bundlechanged_fileschanged-files+ path matchpatchespatch+ path matchtranscripts=== codebox-transcriptkind.includes("transcript")logsincludes("log")+.log/.jsonlThe same bundle could group differently depending on which projection a consumer read.
Change
Add
artifact-ref-classification.ts. Both projections now compose their group sets from shared predicates, so what makes a reference a patch, a transcript, or a log is decided once. Each surface still owns the group shape it publishes.The difference between them is a trust boundary, not drift
This is the important part, and it is why the predicates take an explicit mode.
normalizeAgentTaskRunResultfeeds the workspace delta, and a delta patch is something a caller may apply. It must therefore accept only explicitly declared kinds: a file that merely sits atfiles/patch.diffmust never earn that trust.publicArtifactRefGroupsis a read-only discovery projection, where inferring from paths keeps partially typed bundles listable.My first attempt collapsed both into the lenient rule and weakened that boundary.
tests/agent-task-contracts.test.ts:277caught it:So the modes are now named and documented in the module rather than left as an accidental difference between two copies.
tests/artifact-ref-classification.test.tslocks the distinction from both directions so a future consolidation cannot quietly erase it.Latent bug fixed
The shared path helper compares trailing path segments (
path === suffix || endsWith("/" + suffix)). The log checks passed extensions to it, sopathEndsWith(path, ".jsonl")only ever matched a file literally named.jsonl. Real filenames likefiles/run.jsonlnever classified as logs. Added a separate extension helper. This slightly widens the discoverylogsgroup, which is the documented intent of that predicate.Verification
npm run buildpasses.npm run checkpasses: 311 commands, including the PHP smoke lane.tests/artifact-ref-classification.test.tsasserts typed mode rejects path-inferred patches and changed files, discovery mode accepts them, declared kinds work in both, and the boundary holds end to end throughworkspaceDeltaFromAgentTaskRunResultandpublicArtifactRefGroups.Follow-up found while investigating
A third implementation of this projection lives in PHP.
class-wp-codebox-host-run-result-normalizer.phprebuilds the wholewp-codebox/agent-task-run-result/v1result by hand, because the plugin shells out torecipe-runand re-derives the agent-task result host-side. Details in a comment on #2432. That is a plugin architecture question tied to #1827 and should be scheduled deliberately rather than folded into this slice.AI assistance