Skip to content

feat(portal): rename save_artifact/manage_artifact to save_asset/manage_asset (#1029)#1031

Merged
cjimti merged 1 commit into
mainfrom
feat/1029-rename-asset-tools
Jul 23, 2026
Merged

feat(portal): rename save_artifact/manage_artifact to save_asset/manage_asset (#1029)#1031
cjimti merged 1 commit into
mainfrom
feat/1029-rename-asset-tools

Conversation

@cjimti

@cjimti cjimti commented Jul 23, 2026

Copy link
Copy Markdown
Member

What this is

The portal toolkit's object is an asset everywhere in the system except the two tool names. The tables are portal_assets, portal_asset_versions, portal_asset_thumbnails. The tool result field is asset_id. The REST API, the portal UI, and the docs all say asset. Only the tools said artifact, so save_artifact returned an asset_id.

The tools are now save_asset and manage_asset.

before after
save_artifact save_asset
manage_artifact manage_asset

Hard rename, no aliases

The old names are not registered. MCP tools are discovered at tools/list time rather than pinned by a versioned client contract, so registering the same capability twice adds discovery surface, pollutes the platform_find_tools embedding corpus, and presents one capability as two, without buying anything a client actually needs.

scripts/retired-tools.txt now carries both old names, so make doc-check hard-fails if either reappears anywhere in docs/, the bench doc tree, or README.md.

Provenance harvesting

MCPProvenanceMiddleware decides when to harvest the accumulated tool-call chain onto a saved asset by matching the called tool name against a configured list. That list was a hardcoded []string{"save_artifact"} in pkg/platform/platform.go. If the string and the registered name disagree, nothing fails: provenance capture just stops.

The list now comes from the toolkit's own constant, re-exported through internal/platform/portalstore so pkg/platform does not need a new import edge into pkg/toolkits/portal. TestSaveToolNameIsRegistered asserts the constant against a real tools/list response over an in-memory session, rather than against Tools() (which returns the same constant and so could never fail).

Persona tool lists

Persona tools.allow / tools.deny are glob lists matched against live tool names, and in at least one deployment personas are database-backed rather than file-backed. Migration 000087 rewrites the exact old names in persona_definitions.tools_allow and tools_deny, preserving array order and leaving glob entries such as save_* alone (they still match the new names).

The deny direction is why the migration exists. A stale allow entry stops granting the tool, which is visible immediately. A stale deny entry stops denying it, which fails open and is visible to nobody.

Two things a deployment must update by hand:

  • Persona allow/deny lists held in a YAML config file rather than the database.
  • Saved prompt bodies that name the tools in their text.

Explicitly out of scope

portal.s3_prefix keeps its "artifacts/" default. It addresses stored blobs, not the tool surface.

The tools were not renamed to create_file / manage_file. An asset is versioned with revert, thumbnailed in light and dark, provenance-harvested, collection-organized, feedback-threaded, and publicly shareable; "file" communicates none of that and collides with s3_put_object, which writes actual objects.

Assets and resources were not merged into one tool. They differ in origin (agent-generated vs human-uploaded), direction (session output vs session input), versioning, provenance, MCP exposure, and write path. A merged tool would need a kind discriminator under which roughly half the actions are invalid for either value.

Also in this change

Verification

make verify green.

Live-tested against the make dev-up stack (PostgreSQL + SeaweedFS, real HTTP MCP transport, API-key auth, the full assembled middleware chain):

  • tools/list advertises save_asset and manage_asset, advertises neither old name, and no portal tool's name, title, description, or input schema contains the word "artifact".
  • save_asset created a portal_assets row and returned its asset_id.
  • Provenance harvest works end to end: after a search call, the stored asset carried provenance.tool_calls of length 1 with tool_name = search. Not a unit test of the harvest function, a real call through the real chain.
  • manage_asset action=get read the asset back with matching name, content type, and size.
  • Audit rows for both tools resolved toolkit_kind = portal, so registry lookup routes the new names correctly.
  • Migration 000087 applied to a probe persona row rewrote both tools_allow and tools_deny, left trino_* untouched, and preserved ordering.

Two automated guards were added rather than left to review discipline: TestToolsListVocabulary (the advertised surface never says "artifact") and TestSaveToolNameIsRegistered (the harvest constant matches a registered tool).

Closes #1029

…ge_asset (#1029)

The portal toolkit's object is an asset everywhere else in the system: the
tables are portal_assets and portal_asset_versions, the tool result field is
asset_id, the REST API and portal UI say asset. The two tool names were the
only place it was called an artifact, so save_artifact returned an asset_id.

The tools are now save_asset and manage_asset. This is a hard rename: the old
names are not registered. MCP tools are discovered at tools/list time, so a
second registration of the same capability under a retired name is discovery
surface with no compensating benefit.

Provenance harvesting takes the save tool's name from the toolkit constant
(re-exported through portalstore) rather than a literal, so the harvest list
and the registered name cannot drift; a mismatch there stops provenance
capture silently. A guard test asserts the constant against a real tools/list
response.

Migration 000087 rewrites the exact old names in database-backed persona
tools_allow and tools_deny. The deny direction is why the migration exists: a
stale deny entry stops denying and fails open. Glob entries such as save_*
still match the new names and are left alone.

Deployments must update two things by hand: persona allow/deny lists held in a
YAML config file, and saved prompt bodies that name the tools in their text.

The old names are recorded in scripts/retired-tools.txt, so doc-check now hard
-fails if either reappears in the documentation.

Adjacent corrections this exposed: comments in thread_actions.go and
validation_handler.go attributed the feedback actions to the asset tool, but
#618 moved them to manage_feedback; the pending-feedback error path reported
"failed to gather your assets" when the collection gather failed.

portal.s3_prefix keeps its "artifacts/" value. It addresses stored blobs, not
the tool surface.

Closes #1029
@codecov

codecov Bot commented Jul 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.80328% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.38%. Comparing base (6dc4f2b) to head (d28c4b0).

Files with missing lines Patch % Lines
pkg/platform/platform.go 0.00% 2 Missing ⚠️
pkg/toolkits/portal/toolkit.go 95.34% 1 Missing and 1 partial ⚠️
pkg/toolkits/portal/thread_actions.go 75.00% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1031   +/-   ##
=======================================
  Coverage   89.38%   89.38%           
=======================================
  Files         475      475           
  Lines       52040    52028   -12     
=======================================
- Hits        46514    46505    -9     
+ Misses       3667     3665    -2     
+ Partials     1859     1858    -1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@cjimti
cjimti merged commit fb5d153 into main Jul 23, 2026
10 checks passed
@cjimti
cjimti deleted the feat/1029-rename-asset-tools branch July 23, 2026 05:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

portal: rename save_artifact/manage_artifact to save_asset/manage_asset

1 participant