openvibers/openvibe.codes Commits

Add the opencode harness to the registry and update its tests (#13)

OP OpenVibers committed 5 Oct 2026 · a0219da04a · 4 files, +29 −2

Asked: T16 J3+J9 - OpenVibe.Codes: ONE pull request from current origin/main (git fetch first). 1) server/data/harness-offers.json: add a harness object with id 'opencode', name 'OpenCode', provider 'opencode-go', capabilities {host_access:true, mcp:true, long_autonomy:false, resume:true}, address {kind:'…
Harness job codes-t16-j3-j9-openvibe-codes-one-002347.

Changes

ddiff --git a/README.md b/README.md
iindex 09fd2f3..335a8a6 100644
---- a/README.md
++++ b/README.md
@@@ -34,6 +34,7 @@ A place where a developer outside the network goes from an OpenVibe account to a
| OAuth helper | `/oauth`, `/oauth/test-callback` | Explains authorization code + PKCE (S256) for apps, builds a test authorize URL, and a callback that shows `code`/`state`/`error` and **never exchanges** the code. |
| Webhook tools | `/tools/webhooks` | Verify a delivery as a receiver that requires signature v2 does: `X-OpenVibe-Signature-V2: t=<ts>,v2=<HMAC of "<ts>.<raw body>">` must match and `t` must be within ±300 s of now (the page shows the age and the window, and tells a stale-but-correct signature from a wrong one); `X-OpenVibe-Timestamp` must equal `t`. v2 only: the v1 header (`X-OpenVibe-Signature`) was retired on 2026-09-28 and has no field. In the browser with Web Crypto, or on the server without JavaScript with openvibe-sdk's `verifyDeliveryV2`; constant-time; secret dropped. Generate a sample delivery signed as Events signs it (`X-OpenVibe-Timestamp` and v2, `signDeliveryHeaders`) for any event type the contracts catalog lists. |
| Docs | `/docs/*` | Contracts (field tables, fixtures as examples, raw schema), capability catalog (grantable ones highlighted), event types, SDK reference from its `.d.ts` files, ADRs. Every page states the versions it was generated from. The service registry is read live from Network, with health as Network reports it. |
++| Coding-harness registry | `/docs/harnesses`, `/api/v1/harnesses` | Lists the coding harnesses Codes can route to with their agents (`GET /api/v1/harnesses`) and picks an offer for a task (`POST /api/v1/harnesses/route`). |
| Playgrounds | `/projects/:project/apps/:app/playground` | Media upload (sandbox, `media.object.upload`) and Events publish (`events.app.publish`: types `app.<project_key>.*`, source `app-<app ULID>`), run **as the app** with its own token (from its secret typed for that one request, or a pasted token). Refused — with the missing grant named, and nothing requested or called — unless Network lists the grant as approved. Sandbox apps only. |
| Manifests | `/manifests/validate`, `/projects/:project/apps/:app/releases/new` | Validate `codes.app-manifest@1` and `mods.mod-manifest@1` with openvibe-contracts; create, publish, deprecate and revoke releases. |
| Policy | `/policy/*` | Proposal process and the decision record; compatibility and deprecation policy rendered from ADR-002 and ADR-016 as published; licensing read from package metadata; transparency (what Codes stores and does not); governance — code of conduct ([CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)), contribution guide ([CONTRIBUTING.md](CONTRIBUTING.md)), [contributor ladder](docs/governance/contributor-ladder.md) and [moderation policy](docs/governance/moderation.md) — rendered from those files. All four are **drafts pending owner review**: marked at the top, shown with a notice, not indexed; deleting the draft line publishes one. |
ddiff --git a/server/data/harness-offers.json b/server/data/harness-offers.json
iindex 0f91f42..ae07ba8 100644
---- a/server/data/harness-offers.json
++++ b/server/data/harness-offers.json
@@@ -110,6 +110,31 @@
},
"agents": []
},
++ {
++ "id": "opencode",
++ "name": "OpenCode",
++ "provider": "opencode-go",
++ "capabilities": {
++ "host_access": true,
++ "mcp": true,
++ "long_autonomy": false,
++ "resume": true
++ },
++ "address": {
++ "kind": "cli",
++ "target": "opencode"
++ },
++ "price": {
++ "amount_usd": 0,
++ "unit": "hour"
++ },
++ "limits": {
++ "max_duration_seconds": 7200,
++ "max_concurrent_runs": 2,
++ "max_context_tokens": 128000
++ },
++ "agents": []
++ },
{
"id": "aider",
"name": "Aider",
ddiff --git a/test/harnesses-api.test.js b/test/harnesses-api.test.js
iindex 21cf542..c767dd0 100644
---- a/test/harnesses-api.test.js
++++ b/test/harnesses-api.test.js
@@@ -20,9 +20,10 @@ const rows = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'server', 'da
assert.strictEqual(r.status, 200, r.text.slice(0, 300));
const { harnesses } = r.json();
assert.deepStrictEqual(harnesses.map((h) => h.id), rows.map((row) => row.id));
++ assert.ok(harnesses.some((h) => h.id === 'opencode'), "GET /harnesses includes the 'opencode' harness");
for (const row of rows) {
const h = harnesses.find((x) => x.id === row.id);
-- // Seed rows without an agent yet (command-code, aider) come back with agents: [].
++ // Seed rows without an agent yet (command-code, aider, opencode) come back with agents: [].
assert.deepStrictEqual(h, { ...row, agents: row.agents || [] });
}
});
ddiff --git a/test/harnesses.test.js b/test/harnesses.test.js
iindex 84feb64..6b16f19 100644
---- a/test/harnesses.test.js
++++ b/test/harnesses.test.js
@@@ -13,7 +13,7 @@ const rows = JSON.parse(fs.readFileSync(catalogPath, 'utf8'));
(async () => {
await check('every seed row matches the pinned harness and agent contracts', async () => {
-- assert.strictEqual(rows.length, 5);
++ assert.strictEqual(rows.length, 6);
for (const { agents, ...offer } of rows) {
const harnessCheck = contracts.validate('platform.harness-offer@1', offer);
assert.strictEqual(harnessCheck.valid, true, JSON.stringify(harnessCheck.errors));