[Phase 6.1] Artifact System #70

Closed
opened 2026-04-02 20:52:36 +00:00 by Mark · 1 comment
Owner

Overview

Save generated content to accessible artifacts instead of letting results die in ledger.

Problem Statement

Current flow:

Intent → LLM → Result stored in ledger → Result inaccessible

Generated articles, code, documents exist only in SQLite. Users can't:

  • Download generated content
  • View artifacts in a browser
  • Push to git repos
  • Version control outputs
  • Share with others

Proposed Solution

6.1.1: Artifact Storage

class ArtifactStore:
    """Manages artifact lifecycle: save, retrieve, list, delete."""
    
    def save(self, content: str, format: str, metadata: dict) -> ArtifactID:
        """Save to artifacts/<project>/<intent_id>/<timestamp>.<ext>"""
        
    def save_binary(self, data: bytes, format: str) -> ArtifactID:
        """Save images, PDFs, datasets"""
        
    def get(self, artifact_id: str) -> Artifact | None:
        """Retrieve artifact by ID"""
        
    def list_for_intent(self, intent_id: str) -> list[Artifact]:
        """All artifacts from one intent"""
        
    def list_for_project(self, project: str) -> list[Artifact]:
        """All artifacts for a project"""
        
    def delete(self, artifact_id: str) -> bool:
        """Remove artifact"""

6.1.2: Artifact Metadata Schema

artifact:
  id: str  # UUID
  intent_id: str  # Source intent
  project: str  # Project namespace
  format: str  # md, html, pdf, png, json, py
  created_at: datetime
  size_bytes: int
  checksum: str  # SHA256
  
  # Optional
  git_repo: str  # If pushed to git
  git_commit: str  # Commit SHA
  tags: list[str]  # User-defined tags

6.1.3: Storage Backends

Backend Use Case Priority
Local filesystem Development, single-node P0
Git repo Version-controlled outputs P1
S3/MinIO Cloud storage, distributed P2

6.1.4: API Endpoints

endpoints:
  POST /v1/artifacts:
    body: {content, format, project, intent_id}
    returns: {artifact_id, path}
    
  GET /v1/artifacts/<id>:
    returns: {artifact metadata + content}
    
  GET /v1/artifacts/<id>/raw:
    returns: Raw file download
    
  GET /v1/artifacts?project=X&intent=Y:
    returns: List of artifacts
    
  DELETE /v1/artifacts/<id>:
    returns: {deleted: true}
    
  POST /v1/artifacts/<id>/push:
    body: {git_repo, branch}
    returns: {git_commit}

Deliverables

  • substrate/artifact_store.py - ArtifactStore class
  • artifacts/ directory structure
  • API endpoints in runtime/http_entrypoint.py
  • Integration with intent result flow
  • Unit tests
  • Integration tests

Dependencies

  • None (standalone)

Estimated Duration

1 week

Success Criteria

  • Generated content saved to artifacts/
  • Artifacts retrievable via API
  • Artifacts downloadable as files
  • Metadata searchable (project, intent, format)
  • Intent results linked to artifacts
  • Parent: #69 (Phase 6 Meta)
## Overview Save generated content to accessible artifacts instead of letting results die in ledger. ## Problem Statement Current flow: ``` Intent → LLM → Result stored in ledger → Result inaccessible ``` Generated articles, code, documents exist only in SQLite. Users can't: - Download generated content - View artifacts in a browser - Push to git repos - Version control outputs - Share with others ## Proposed Solution ### 6.1.1: Artifact Storage ```python class ArtifactStore: """Manages artifact lifecycle: save, retrieve, list, delete.""" def save(self, content: str, format: str, metadata: dict) -> ArtifactID: """Save to artifacts/<project>/<intent_id>/<timestamp>.<ext>""" def save_binary(self, data: bytes, format: str) -> ArtifactID: """Save images, PDFs, datasets""" def get(self, artifact_id: str) -> Artifact | None: """Retrieve artifact by ID""" def list_for_intent(self, intent_id: str) -> list[Artifact]: """All artifacts from one intent""" def list_for_project(self, project: str) -> list[Artifact]: """All artifacts for a project""" def delete(self, artifact_id: str) -> bool: """Remove artifact""" ``` ### 6.1.2: Artifact Metadata Schema ```yaml artifact: id: str # UUID intent_id: str # Source intent project: str # Project namespace format: str # md, html, pdf, png, json, py created_at: datetime size_bytes: int checksum: str # SHA256 # Optional git_repo: str # If pushed to git git_commit: str # Commit SHA tags: list[str] # User-defined tags ``` ### 6.1.3: Storage Backends | Backend | Use Case | Priority | |---------|----------|----------| | Local filesystem | Development, single-node | P0 | | Git repo | Version-controlled outputs | P1 | | S3/MinIO | Cloud storage, distributed | P2 | ### 6.1.4: API Endpoints ```yaml endpoints: POST /v1/artifacts: body: {content, format, project, intent_id} returns: {artifact_id, path} GET /v1/artifacts/<id>: returns: {artifact metadata + content} GET /v1/artifacts/<id>/raw: returns: Raw file download GET /v1/artifacts?project=X&intent=Y: returns: List of artifacts DELETE /v1/artifacts/<id>: returns: {deleted: true} POST /v1/artifacts/<id>/push: body: {git_repo, branch} returns: {git_commit} ``` ## Deliverables - [ ] `substrate/artifact_store.py` - ArtifactStore class - [ ] `artifacts/` directory structure - [ ] API endpoints in `runtime/http_entrypoint.py` - [ ] Integration with intent result flow - [ ] Unit tests - [ ] Integration tests ## Dependencies - None (standalone) ## Estimated Duration 1 week ## Success Criteria - [ ] Generated content saved to `artifacts/` - [ ] Artifacts retrievable via API - [ ] Artifacts downloadable as files - [ ] Metadata searchable (project, intent, format) - [ ] Intent results linked to artifacts ## Related - Parent: #69 (Phase 6 Meta)
Author
Owner

Overseer report — session terminated (10min idle)

State at termination:

  • Session: gone (tmux session not found)
  • Working tree dirty, NOT committed:
    • runtime/http_entrypoint.py: +74 lines — new POST /v1/artifacts/<id>/push route (git push endpoint)
    • tests/unit/test_artifact_store.py: new untracked file, 30 tests, 3 failing:
      1. TestFormatConfig::test_known_aliases — get_format_config("yaml") != get_format_config("yml") (no alias map in substrate/artifact_store.py:88)
      2. TestSaveBinary::test_read_binary_returns_bytes — binary round-trip broken
      3. TestEmbeddingIntegration::test_update_embedding_with_service — embedding update returns False

Not committed. No labels changed. Issue left OPEN with quickfix,triage-approved.

Not marking blocked:needs-attention — these are real quickfix-scope bugs in the issue, not blockers awaiting human input. Recommend re-running quickfix to pick up the WIP and finish the 3 fixes.

**Overseer report — session terminated (10min idle)** State at termination: - Session: gone (tmux session not found) - Working tree dirty, NOT committed: - `runtime/http_entrypoint.py`: +74 lines — new `POST /v1/artifacts/<id>/push` route (git push endpoint) - `tests/unit/test_artifact_store.py`: new untracked file, 30 tests, **3 failing**: 1. `TestFormatConfig::test_known_aliases` — `get_format_config("yaml")` != `get_format_config("yml")` (no alias map in `substrate/artifact_store.py:88`) 2. `TestSaveBinary::test_read_binary_returns_bytes` — binary round-trip broken 3. `TestEmbeddingIntegration::test_update_embedding_with_service` — embedding update returns False Not committed. No labels changed. Issue left OPEN with `quickfix`,`triage-approved`. Not marking `blocked:needs-attention` — these are real quickfix-scope bugs in the issue, not blockers awaiting human input. Recommend re-running quickfix to pick up the WIP and finish the 3 fixes.
Mark 2026-06-16 13:27:17 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Mark/mesh-seed#70
No description provided.