# Investibook Generic Workspace Attachment Plan

## Current Design

- AO owns Jira access, dry-run/live visibility filtering, reply-safe comments,
  and attachment selection in its local run workspace.
- Cloud performance cases select Investibook as primary. Wetech OP remains an
  AO-owned diagnosis path using `wetech-op-grafana`.
- The old AO client calls `/api/workspaces/from-entry` with a Jira key, causing
  Investibook to fetch Jira again and violate AO's canonical dry-run boundary.
- Investibook already has a workspace attachment chain:
  `POST /api/workspace/{id}/image-attachments` -> attachment metadata/status ->
  `/api/chat/stream?attachment_ids=...`. It currently accepts only raster
  images and may pre-parse them.
- A separate `/files` write API would bypass attachment metadata and Chat
  integration while exposing a broader workspace-filesystem write surface.

## Risks

- Re-fetching Jira creates two intake sources and can expose future state to a
  historical dry-run.
- Reusing a remote Workspace across AO runs reuses its Agent conversation.
- Expanding file types without size, filename, and content metadata checks can
  turn the attachment endpoint into an unsafe arbitrary upload surface.
- Image parsing must remain backward compatible for existing UI callers while
  AO needs request-level raw passthrough.
- Raw support attachments and Agent output must not leak into trace payloads.

## Target Contract

### Canonical source of truth

- Jira and the AO run workspace are the only intake sources.
- AO sends no `issue_key`, `alert_id`, Jira source locator, or `/from-entry`
  request to Investibook.
- Investibook stores AO-selected attachment bytes and lets the workspace Agent
  use them; it does not fetch Jira or preprocess AO uploads.
- `input/diagnostics/manifest.json` remains AO's normalized record of the
  remote Workspace and result.

### Generic workspace attachment chain

1. AO creates a generic Workspace with `POST /api/workspaces`. Its opaque ID is
   derived from the AO run ID and scope, never the Jira key.
2. AO uploads generated `context.md` plus every available canonical attachment
   through the existing attachment implementation.
3. The canonical API name becomes
   `POST /api/workspace/{workspace_id}/attachments`; the existing
   `/image-attachments` route remains a compatibility alias backed by the same
   handler, database records, storage, and status model.
4. Multipart input is `file` plus optional `parse`. Existing image callers keep
   the configured image-parser default. AO sends `parse=false`, so images and
   all non-image files become `ready` without preprocessing.
5. Non-image uploads accept arbitrary MIME types and preserve raw bytes. Their
   attachment kind is `file`; images remain `image`.
6. AO calls `/api/chat/stream` with a short instruction, `workspace_id`, and
   the returned comma-separated `attachment_ids`. Chat injects file metadata
   and workspace-relative paths; the Agent reads the uploaded files itself.
7. AO consumes SSE until `complete`; error, premature EOF, timeout, empty output,
   or upload failure becomes an explicit failed primary diagnostic manifest.

### Isolation

- Each AO run gets a distinct Investibook Workspace and Agent session.
- Repeating an HTTP request within one AO run produces the same opaque Workspace
  ID; a new retry/run uses a different ID.
- Only attachment IDs returned by the current upload set are passed to Chat.

## Invariants

- There is one attachment storage/metadata/Chat path, not parallel `/files` and
  `/image-attachments` write implementations.
- `/image-attachments` remains backward compatible for current image clients.
- `parse=false` never calls the vision parser or creates a parser summary.
- Non-image files are never decoded, extracted, OCRed, or inlined by the backend.
- Upload filenames cannot escape their attachment-owned directory; raw bytes
  are unchanged and bounded by the configured upload limit.
- AO never sends Jira keys as Investibook control fields or generated metadata.
- Failed Investibook primary evidence never silently falls back to Clinic.
- Wetech OP does not use this Investibook path.

## Not Doing

- No writable generic Workspace filesystem `/files` endpoint.
- No `/from-entry`, Investibook Jira source preparation, or comment filtering
  for AO.
- No frontend rename/removal of the existing image route in this slice.
- No customer-visible Jira reply, transition, production operation, AO deploy,
  or production cutover.

## Implementation Steps

1. Generalize Investibook attachment validation/storage and add the canonical
   `/attachments` alias without duplicating handler logic.
2. Add request-level `parse` control; preserve existing image defaults and make
   non-images raw passthrough.
3. Extend Chat attachment prompt construction to expose non-image relative
   paths without inlining or parsing content.
4. Replace AO's `/from-entry` and `/files` assumptions with generic Workspace,
   attachment upload, `attachment_ids`, and Chat SSE.
5. Build context only from AO's canonical local workspace and support PDF,
   Office, image, and text attachment materialization.
6. Keep traces metadata-only and preserve Investibook-first/Clinic-supplemental
   plus Wetech OP routing.
7. Update tests and remove obsolete diagnostics polling deployment settings.

## Verification Checklist

- [x] Investibook: old image upload and parser-default tests remain green.
- [x] Investibook: `parse=false` image is raw/ready with no parser call.
- [x] Investibook: text, PDF, Office, archive, and opaque binary round-trip unchanged.
- [x] Investibook: invalid/empty/oversized upload and unauthorized access fail.
- [x] Investibook: canonical and legacy route names use the same implementation.
- [x] Investibook Chat: attachment metadata supplies readable workspace paths;
  non-images are not inlined or parsed.
- [x] AO: requests use `/api/workspaces`, `/attachments`, and `/api/chat/stream` with
  returned `attachment_ids`; no `/from-entry`, `issue_key`, or `alert_id`.
- [x] AO: dry-run consumes only canonical filtered context and attachment files.
- [x] AO: SSE success/error/EOF, upload failure, empty output, result bounds, and
  run-scoped Workspace isolation are covered.
- [x] AO: `npm run typecheck` and full `npm test` pass (235 tests).
- [x] Initial implementation and contract verification completed before rollout.

## Rollout Plan (approved 2026-07-14)

- Keep `/from-entry` as a compatibility API. It predates the earlier hardening
  change and AO has no runtime dependency on it after this change.
- Merge and deploy Investibook first because AO staging targets
  `https://investibook.com`.
- Sync the AO staging app-delivery template before publishing the AO image.
- Deploy AO to staging only. Production remains
  `AUTO_ONCALL_AUTOMATION_ROLE=disabled` and
  `AUTO_ONCALL_POLL_ENABLED=false`.
- Verify the production Investibook attachment OpenAPI/runtime response, then
  verify the AO staging image, rendered env, readiness, recent worker/poller
  logs, and queued/running job counts.
