Skip to content

Commit ef760cd

Browse files
GiniGini
authored andcommitted
Prove protected Claude artifact and skill flows
1 parent 572b67b commit ef760cd

10 files changed

Lines changed: 75 additions & 36 deletions

HANDOVER.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# ONEVibe — Agent Handover Document
22

33
> **Date**: 2026-07-17
4-
> **Status**: Local phases 1–3 are implemented and the professional UI has crossed the Zustand and ordinary-collection TanStack Query boundaries. Governed MCP declarations, bounded MCP health probing, a tested opt-in MCP capability facade, truthful demo skill status, a GitHub-catalog skill marketplace boundary, feature-gated Better Auth, authenticated owner scoping, a local organization-membership scaffold, and a reviewed Drizzle/Postgres import proof (including organization/member staging) are present. LiteLLM-only enforcement remains mandatory for every harness and every environment: data sovereignty, centralized routing, cost control, and model optimization are product requirements. The running application is still SQLite-backed; protected Claude/provider acceptance, production auth, the Postgres repository/runtime switch, deployment, cloud sandbox attestation, MCP secret brokering, and external MCP health/attestation remain open.
4+
> **Status**: Local phases 1–3 are implemented and the professional UI has crossed the Zustand and ordinary-collection TanStack Query boundaries. Governed MCP declarations, bounded MCP health probing, a tested opt-in MCP capability facade, truthful demo skill status, a GitHub-catalog skill marketplace with protected Claude/LiteLLM materialization evidence, feature-gated Better Auth, authenticated owner scoping, a local organization-membership scaffold, and a reviewed Drizzle/Postgres import proof (including organization/member staging) are present. LiteLLM-only enforcement remains mandatory for every harness and every environment: data sovereignty, centralized routing, cost control, and model optimization are product requirements. The running application is still SQLite-backed; production auth, the Postgres repository/runtime switch, deployment, cloud sandbox attestation, MCP secret brokering, and external MCP health/attestation remain open.
55
> **For**: The next agent (or human) picking this up cold.
66
> **Read this entire document before touching any code.**
77
@@ -56,7 +56,7 @@ The abstraction that enforces this: `server/runtime-adapter.ts` — the `Runtime
5656
6. **Organization membership is not yet a data-plane grant** — local authenticated owners can create organizations and owners can add/remove members, but task/project/runtime access remains owner-scoped until the Postgres-backed organization policy and repository switch are accepted
5757
7. **The active task remains an intentional state boundary** — durable SSE replay and the active snapshot are still owned by `useTask`; active-task mutations use Query mutation lifecycle/pending state and reconcile server-derived caches without creating a second client authority
5858
7. **Local metadata writes are now crash-safe** — task/project/schedule/version JSON is written through same-directory temporary files and flush-before-rename; this does not replace the still-open Postgres/object-storage promotion path
59-
8. **Remaining extension/release gaps**protected provider-backed marketplace materialization, production MCP secret brokering/external health attestation, and browser evidence remain open in `TODO.md`; the production dependency audit gate is now clean under the reviewed esbuild override
59+
8. **Remaining extension/release gaps** — production MCP secret brokering/external health attestation and browser evidence remain open in `TODO.md`; the production dependency audit gate is now clean under the reviewed esbuild override
6060

6161
### How to run it locally
6262

@@ -209,7 +209,7 @@ Full task list: `TODO.md`. Summary:
209209
(See `plan/05-ui-overhaul.md` for details)
210210

211211
### Phase 6 — MCP + extensions
212-
**4 tasks. Target: users add MCP servers; verified skill marketplace installation works. The local MCP declaration, bounded health probe, and marketplace install/remove slices exist; authenticated ownership, secret brokering, external health attestation, and protected provider-backed marketplace materialization remain open.**
212+
**4 tasks. Target: users add MCP servers; verified skill marketplace installation works. The local MCP declaration, bounded health probe, marketplace install/remove slices, live GitHub catalog verification, and protected Claude/LiteLLM skill materialization exist; authenticated ownership, secret brokering, and external health attestation remain open.**
213213
(See `plan/06-mcp-extensions.md` for details)
214214

215215
---

TODO.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ Reference: `plan/05-ui-overhaul.md`
100100
Reference: `plan/06-mcp-extensions.md`
101101

102102
- [x] **P6-01** Add MCP config management — local SQLite-backed UI to add/remove/test MCP servers; store in `runtime_mcp_configs` (not on-disk JSON); inject secret-free declarations into adapters that support `'tool_use'`. Authenticated ownership and a production secret broker remain part of P4.
103-
- [ ] **P6-02** Add skill marketplace — GitHub-backed catalog, SHA-256 verified install/remove UI, owner-scoped persistence, bounded built-in/marketplace selection validation, truthful demo/provider event contracts, deterministic restart/materialization proof, and a live pushed-GitHub catalog/content harness are implemented; keep open until a protected Claude/LiteLLM run proves marketplace content materialization.
103+
- [x] **P6-02** Add skill marketplace — GitHub-backed catalog, SHA-256 verified install/remove UI, owner-scoped persistence, bounded built-in/marketplace selection validation, truthful demo/provider event contracts, deterministic restart/materialization proof, live pushed-GitHub catalog/content verification, and protected Claude/LiteLLM materialization are complete. The protected evidence is host-process only; production org authorization and ONEComputer/microVM isolation remain separate gates.
104104
- [x] **P6-03** Add two-tool MCP facade — opt-in server-owned stdio facade exposes `search_capabilities` + `execute_capability`, bounds output/time/process environment, and executes only IDs returned by the same catalog; production secret brokering, external health/attestation, authenticated organization ownership, and protected provider acceptance remain open
105105
- [x] **P6-04** Add agent context diagnostics — authenticated `/api/diagnostics` and a Computers status panel now report the LiteLLM model boundary, session scope, persistence driver/contract, runtime readiness, sandbox boundary, and owner-scoped MCP health/tool-catalog checks without returning credentials, prompts, or provider payloads. This is local operational visibility, not production attestation.
106106

docs/IMPLEMENTATION-LOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -823,7 +823,7 @@ baseline harness in CI.
823823
## 2026-07-17 — skills E2E truthfulness and identifier fixes
824824

825825
- Fixed the task skill schema to accept the repository’s stable built-in snake_case identifiers (`document`, `security_review`, and the other built-in packs) while preserving bounded marketplace IDs. The previous schema rejected valid built-in selections before execution.
826-
- Corrected `scripts/skills-e2e.ts` to distinguish truthful demo (`Skill packs recorded for simulation`, `not_executed_demo`) from provider (`Versioned skill packs selected`, `provider_turn_workspace`) evidence. The harness now inspects internal skill bytes only through a stopped local `TaskStore`; the public files route remains prohibited from exposing `.claude/skills`.
826+
- Corrected `scripts/skills-e2e.ts` to distinguish truthful demo (`Skill packs recorded for simulation`, `not_executed_demo`) from provider (`Versioned skill packs selected`, `adapter_owned`) evidence. The harness now inspects internal skill bytes only through a stopped local `TaskStore`; the public files route remains prohibited from exposing `.claude/skills`. The first protected relay run exposed and closed a stale harness expectation for `provider_turn_workspace`; the runtime contract was already intentionally `adapter_owned`.
827827
- Passing evidence: `npm run e2e:skills` (deterministic local-demo materialization, immutable manifest across restart, permission invariant, selected-only files), `npm run e2e:skill-marketplace` (loopback catalog install/remove), and `npm run check` (52 files / 259 tests). No protected Claude/LiteLLM materialization claim is made without configured relay evidence.
828828

829829
## 2026-07-17 — document and website local creation recheck
@@ -843,6 +843,12 @@ baseline harness in CI.
843843
- Added a GitHub Actions `postgres-schema` job that starts PostgreSQL 18, runs `npm run db:migrate`, and validates the reviewed migration manifest with `npm run db:check`.
844844
- Boundary: this proves schema/migration compatibility only. The API still fails closed when `DATABASE_URL` would select Postgres because the TaskStore repository/runtime adapter, production import, and application idempotency switch remain open.
845845

846+
## 2026-07-17 — protected Claude/LiteLLM skill materialization and SDK path compatibility
847+
848+
- Fixed a real Claude SDK compatibility defect: native file tools can canonicalize a relative path against the parent Node process before `PreToolUse`, causing valid task-workspace `Read`/`Write` calls to appear outside the workspace. Added a bounded normalization path that remaps only process-cwd-relative canonical paths back into the task workspace and rejects all other paths; the normalized input is passed back to both `canUseTool` and `PreToolUse`.
849+
- Protected evidence through the host-only LiteLLM relay: `npm run e2e:skills` completed with `claudeProviderReady: true`, `materializationMode: claude_sdk`, selected `document` and `security_review` packs, immutable manifest across restart, selected-only internal files, permission invariant, valid evidence chain, no external writes, and no logged secrets. No ONEComputer/microVM isolation claim is made.
850+
- Corrected the provider harness expectation from the stale `provider_turn_workspace` value to the canonical `adapter_owned` contract. The first relay run exposed this mismatch; the runtime contract itself was already explicit and tested.
851+
846852
## 2026-07-17 — live GitHub skill catalog acceptance harness
847853

848854
- Added `npm run e2e:skill-marketplace-github`, which fetches the pushed default catalog from `raw.githubusercontent.com`, downloads every pinned `SKILL.md`, rechecks the SHA-256 digest and frontmatter contract, and reports only IDs, versions, byte counts, and digests.

docs/LINEAR-BOARD.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ The Zustand state migration is tracked in [ONE-259](https://linear.app/onecomput
4646

4747
The TanStack Query migration is tracked in [ONE-260](https://linear.app/onecomputer/issue/ONE-260/uxp1-migrate-server-backed-collections-to-tanstack-query). It is Done for the local boundary: the root QueryClient and all ordinary server-backed collection queries are implemented, and active-task stop/retry/follow-up/branch/share/guidance/project/tag actions use mutation hooks with explicit pending/error behavior and authoritative cache/snapshot reconciliation. The durable `useTask` SSE authority remains intentionally separate from generic Query state.
4848

49-
The skills/navigation workstream is tracked in [ONE-243](https://linear.app/onecomputer/issue/ONE-243/uxp1-rebuild-thread-list-skills-command-palette-and-project-context). Its marketplace slice has a GitHub-backed catalog, SHA-256-verified content download, owner-scoped SQLite installation state, install/remove API/UI, built-in snake_case selection validation, deterministic restart/materialization proof, truthful demo/provider materialization boundaries, and a live pushed-GitHub catalog/content harness (`npm run e2e:skill-marketplace-github`). It remains open only for protected Claude/LiteLLM materialization evidence and the wider production authorization boundary.
49+
The skills/navigation workstream is tracked in [ONE-243](https://linear.app/onecomputer/issue/ONE-243/uxp1-rebuild-thread-list-skills-command-palette-and-project-context). Its marketplace slice has a GitHub-backed catalog, SHA-256-verified content download, owner-scoped SQLite installation state, install/remove API/UI, built-in snake_case selection validation, deterministic restart/materialization proof, truthful demo/provider materialization boundaries, a live pushed-GitHub catalog/content harness (`npm run e2e:skill-marketplace-github`), and protected Claude/LiteLLM materialization evidence (`npm run e2e:skills`). The provider evidence is host-process only; production authorization remains a separate P4/P6 boundary.
5050

5151
The LiteLLM-routed Codex-compatible harness is tracked in [ONE-248](https://linear.app/onecomputer/issue/ONE-248/bep1-add-litellm-routed-codex-compatible-runtime). Commit `947835b` adds bounded workspace tools and truthful non-sandboxed capability metadata; live relay acceptance and isolated execution remain open.
5252

docs/LIVE-E2E-ENGINEERING-LOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@
22

33
This is the durable failure-and-evidence log for the backend POC. It records observed facts and fixes so future agents do not repeat the same experiments.
44

5+
## 2026-07-17 — protected LiteLLM provider gate and SDK workspace-path compatibility
6+
7+
- The host-only LiteLLM relay advertised the configured `claude-sonnet-5` alias and received the Claude-compatible `/v1/messages` traffic. No direct first-party Anthropic endpoint or credential was used.
8+
- `npm run e2e:chat` passed through LiteLLM: chat `task_efedfbc7faf944` produced two durable turns, 8 live SSE frames and 39 replay frames, artifact task `task_a982a5e4f8e343` produced a Markdown artifact and bounded Bash evidence, the API restart recovered history, the failure/retry probe recovered to `completed`, and all evidence chains validated. Boundary: `executionBoundary=host_process`.
9+
- The first protected skills run exposed a stale harness expectation (`provider_turn_workspace` vs the canonical `adapter_owned` event contract). After correcting the harness, `npm run e2e:skills` passed with `task_6c89fa28f7364c`, selected `document` and `security_review` packs, immutable manifest/restart proof, selected-only internal files, permission invariance, valid evidence, and no external writes.
10+
- A real provider document run exposed the underlying SDK path issue: native file hooks received relative paths canonicalized against the parent process. The bounded normalization fix maps only process-cwd-relative paths back into the task workspace and rejects everything else. The follow-up document run completed all required document artifacts through LiteLLM.
11+
- `npm run e2e:claude-slides` passed against a temporary LiteLLM-configured API: 8 slides, `deck.pptx` (34,990 bytes), `deck.pdf` (7,546 bytes), and valid evidence. The harness now asserts that `.claude/skills/*` remains hidden from the public file route rather than attempting to read runtime internals through that route.
12+
- None of these host-process runs prove ONEComputer, microVM, OpenVTC/VTI, or production egress enforcement. Browser evidence remains unavailable because the in-app browser runtime reported no available browser.
13+
514
## 2026-07-16 — handover baseline and failure-path slice
615

716
- The current checkout is `699fe22` with the handover roadmap as the governing plan. Baseline `npm run check` passed with 37 test files and 207 tests before the slice.

docs/ONEVIBE-SKILL-MARKETPLACE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,4 @@ POST /api/skills/install { "skillId": "meeting-brief" }
2323
DELETE /api/skills/:skillId
2424
```
2525

26-
The local acceptance proof is `npm run e2e:skill-marketplace`. It uses a loopback GitHub-shaped catalog fixture to prove install, digest verification, task selection, truthful demo evidence, and removal. `npm run e2e:skill-marketplace-github` verifies the pushed default catalog and every pinned content digest through GitHub's raw-content host without credentials. Neither harness claims protected Claude/LiteLLM provider execution; that remains a separate gate.
26+
The local acceptance proof is `npm run e2e:skill-marketplace`. It uses a loopback GitHub-shaped catalog fixture to prove install, digest verification, task selection, truthful demo evidence, and removal. `npm run e2e:skill-marketplace-github` verifies the pushed default catalog and every pinned content digest through GitHub's raw-content host without credentials. `npm run e2e:skills` can be run with the server-only LiteLLM relay environment to prove protected Claude SDK materialization, immutable restart persistence, selected-only files, and evidence-chain validity. That provider evidence is host-process only; it does not claim ONEComputer or microVM isolation.

scripts/claude-sdk-slides-e2e.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ const download = async (taskId: string, filePath: string) => {
3535
return new Uint8Array(await response.arrayBuffer())
3636
}
3737

38-
const readTextFile = async (taskId: string, filePath: string) => {
39-
const result = await request<{ content: string }>(`/api/tasks/${taskId}/file?path=${encodeURIComponent(filePath)}`)
40-
return result.content
38+
const assertInternalFileHidden = async (taskId: string, filePath: string) => {
39+
const response = await fetch(`${baseUrl}/api/tasks/${taskId}/file?path=${encodeURIComponent(filePath)}`)
40+
if (response.status !== 404) throw new Error(`Internal runtime file ${filePath} must remain hidden from the public file route`)
4141
}
4242

4343
const main = async () => {
@@ -58,10 +58,7 @@ const main = async () => {
5858
if (!materialized || materialized.payload.permissionChange !== false) throw new Error('Claude skill materialization evidence is missing or widened permissions')
5959
const materializedIds = Array.isArray(materialized.payload.skills) ? materialized.payload.skills.map((skill) => typeof skill === 'object' && skill !== null && 'id' in skill ? String(skill.id) : '') : []
6060
if (JSON.stringify(materializedIds) !== JSON.stringify(['slides', 'security_review'])) throw new Error('Claude materialization evidence did not match the selected skill set')
61-
for (const skill of ['slides', 'security_review']) {
62-
const content = await readTextFile(task.id, `.claude/skills/${skill}/SKILL.md`)
63-
if (!content.includes(`name: ${skill}`)) throw new Error(`Materialized ${skill} pack is not readable in the task workspace`)
64-
}
61+
for (const skill of ['slides', 'security_review']) await assertInternalFileHidden(task.id, `.claude/skills/${skill}/SKILL.md`)
6562
const renderCall = task.events.find((event) => event.type === 'tool_call_started' && event.label === 'mcp__onevibe__render_slide_deck')
6663
if (!renderCall) throw new Error('Claude did not invoke the governed slide renderer')
6764
const [pptx, pdf, outline] = await Promise.all([
@@ -71,7 +68,7 @@ const main = async () => {
7168
if (String.fromCharCode(...pdf.subarray(0, 5)) !== '%PDF-') throw new Error('PDF export has an invalid signature')
7269
const slides = JSON.parse(outline.content) as unknown[]
7370
if (slides.length !== 8) throw new Error(`Expected eight structured slides, found ${slides.length}`)
74-
const manifest = JSON.parse(await readTextFile(task.id, 'artifact-manifest.json')) as { outputs?: Array<{ path: string; size: number; sha256: string }> }
71+
const manifest = JSON.parse((await request<{ content: string }>(`/api/tasks/${task.id}/file?path=artifact-manifest.json`)).content) as { outputs?: Array<{ path: string; size: number; sha256: string }> }
7572
const manifestOutputs = manifest.outputs ?? []
7673
for (const filePath of ['deck.pptx', 'deck.pdf', 'outline.json', 'speaker-notes.md']) {
7774
const entry = manifestOutputs.find((output) => output.path === filePath)

0 commit comments

Comments
 (0)