Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .claude/STATUS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# STATUS — hal

Last updated: 2026-07-24
Last updated: 2026-07-28

## Current Focus

Expand All @@ -16,6 +16,7 @@ Last updated: 2026-07-24
- [ ] **Ops track — 11 runs Archon, 1 brief par repo** — runbook maître `docs/features/ops-runbook.md` (séquencement, commandes de lancement, étapes manuelles). Phase 1 purge+CI : `ops-1-purge-ci.md` dans hal, edifice, bluegreen-marketplace, renaud-marketplace, auth-gateway. Phase 2 : hal `ops-2-deploy-make.md`, bgm `ops-2-release-script.md`, rm `ops-2-release-improve-map.md` + `ops-2-briefing-headless.md`, ag `ops-2-deploy-script.md`. Phase 3 : `ops-3-night-shift.md` (copié dans bluegreen-ops au bootstrap). Les 3 fichiers `ops-phase-*.md` sont des stubs "superseded" à supprimer au commit.

## Done (current sprint)
- [x] **#82 — Edifice tools artifact-callable (branche `archon/task-feat-artifact-callable-edifice-tools`) — 2026-07-28** — 2 nouveaux tools `hal-mcp` : `get_mission_context(mission_id)` renvoie le `context.json` complet **inline** (header + `observations[]` + `notes[]` + `photos[]` metadata, **zéro URL signée**) — shape portée field-for-field depuis `build_context.py` de bluegreen-marketplace ; `get_mission_photo(photo_id, max_width?)` renvoie une photo redimensionnée en base64 JPEG (magick-wasm `@0.0.41`, wasm fetché du CDN au cold start, cible ~300 Ko, image content block). 12 read tools reçoivent `annotations: { readOnlyHint: true }`, `push_mission_context` explicitement `readOnlyHint: false` — débloque le chargement d'un artifact Claude sans cascade de dialogs Allow/Deny (`anthropics/claude-code#57398`, closed not-planned). Fetch de `get_mission_with_assets` extrait dans `fetchMissionRaw()` **sans changement de contrat**. Aucun changement de schéma `edifice_*`. **Offline vert** : `deno check` + suite Deno (68 tests, dont 5 nouveaux : shape inline sans URL, tools/list readOnlyHint, no-clobber single-field, 2 error-paths photo) + `make test` (157 pytest). ⚠️ **Happy-path resize + 3 critères d'acceptation = à vérifier LIVE post-`make deploy`** contre la mission de référence `3f2bd270-fd5f-4d09-a64e-063a66348dc8` (le resize nécessite le wasm de 14,6 Mo, hors suite offline). Débloque **bluegreen-marketplace#50** Phases 2-3 ; bump `plugins/hal/.mcp.json` = follow-up downstream après deploy. Voir cross-repo-log 2026-07-28.
- [x] **#65 — CI migration smoke-test — PR #72 mergée, `main` vert — 2026-07-24** — job `migration-smoke` : charge `supabase/schema.snapshot.sql` (dump réel de prod, 218 Ko, schémas `auth`+`public`+`private`) dans un conteneur éphémère, puis applique les migrations que la branche ajoute. Lancé via Archon `archon-fix-github-issue`. **Le job produit par l'agent ne pouvait pas fonctionner** — 6 causes empilées, aucune visible en relecture, toutes trouvées en exécutant le job en local contre un vrai dump : `postgres:17` n'a ni les rôles cluster Supabase (`role "supabase_admin" does not exist`, ligne 18) ni ses extensions (`type "public.geometry"`, ligne 227) ; se connecter en `postgres` donne `permission denied for schema auth` (ligne 35) ; le schéma `auth` plus ancien de l'image fait que le `CREATE TABLE IF NOT EXISTS` du dump saute `auth.users` (`column "is_sso_user"`, ligne 1866) ; `--schema auth,public` oublie le schéma `private` de prod (ligne 4852). **La 6e est une faute de conception** : « appliquer la migration la plus récente » est impossible — le snapshot *étant* un dump de prod, il la contient déjà, donc `relation ... already exists` à chaque run, indéfiniment. Corrigé en appliquant ce que la branche **ajoute** (`git diff origin/<base>...HEAD`, d'où `fetch-depth: 0`). Image = `public.ecr.aws/supabase/postgres:17.4.1.052`, version **exacte** de prod (`select version()`), la même que celle qu'utilise `supabase db dump` ; postgis/vector/pg_net y résolvent aux versions exactes de prod. **Test négatif exécuté** : un trigger réintroduisant la forme d'avant-fix (`AFTER INSERT ... WHEN (OLD.company_id IS DISTINCT FROM NEW.company_id)`) échoue bien avec `INSERT trigger's WHEN condition cannot reference OLD values` — **l'erreur exacte qui a atteint la prod via PR #60 est attrapée**. 4/4 critères d'acceptation. ⚠️ Le snapshot n'a pu être commité qu'après un détour : un dump de `public` embarquait une clé `service_role` en dur, inlinée dans `edifice_trigger_map_generation()` d'edifice depuis le 2026-05-29 — corrigé côté edifice (Vault puis secret dédié, clés compromises rotées) avant commit. **C'est le smoke-test qui a fait remonter la fuite.**
- [x] **PR #73 — `deno check` réparé sur le codec PNG — 2026-07-24** — `main` était rouge depuis `48f89fd` : la v10 déployée d'`edifice-map-generator`, commitée en `444fa3d`, ne passait pas le type-check (2 × `TS2345`). **Toutes les PR ouvertes héritaient de l'échec**, une PR testant son merge avec `main`. Deux causes de typage : (1) `CompressionStream`/`DecompressionStream` déclarent `writable: WritableStream<BufferSource>`, plus large qu'`Uint8Array` — le helper demandait plus étroit que ce que ces classes fournissent ; (2) depuis **TypeScript 5.7** les tableaux typés sont génériques sur leur buffer, `Uint8Array` nu = `Uint8Array<ArrayBufferLike>` (admet `SharedArrayBuffer`) alors que `BufferSource` exige `ArrayBufferView<ArrayBuffer>`. Corrigé en typant le chemin du codec `Uint8Array<ArrayBuffer>` **plutôt qu'avec un cast** — un `as BufferSource` aurait fait taire le compilateur sans énoncer la contrainte. Diff **types uniquement** : 3 signatures, aucune ligne exécutable, fonction déployée inchangée, aucun redéploiement impliqué. Vérifié avec la toolchain de la CI (image `denoland/deno`).
- [x] **Décision — guard `::uuid` non propagé, #67 fermée — 2026-07-23** — après merge de #62 (PR #70), remontée de tous les chemins d'écriture de `raw_app_meta_data.company_id` : `auth-gateway` `invite/form.tsx:101` et `users/[id]/edit/page.tsx:76` sont des `<select>` peuplés depuis `public.companies` (valeur = UUID lu en base, pas de saisie libre) ; `edifice/webapp/app/api/invite/route.ts:43` réémet le `company_id` du JWT de l'admin invitant, lui-même issu du chemin précédent. **Aucun chemin ne permet une valeur malformée.** Prod : 7 lignes `auth.users`, 4 UUID valides + 3 NULL, 0 malformé. Conséquences : (a) **pas d'issue miroir côté edifice** pour `trg_provision_edifice_org` — le cas est inatteignable, la propager reviendrait à dupliquer un fix mal placé ; (b) **issue #67 fermée `not planned`** — le check ops périodique ne surveillait que le silence introduit par le guard lui-même (un `WHEN` ne peut pas `RAISE`), ce qui contredit la règle « fail fast, loudly / no workarounds » du CLAUDE.md ; (c) **guard #62 conservé** (déjà appliqué en prod, coût négligeable) mais on ne construit rien dessus. Seul vrai manque si validation souhaitée un jour : `auth-gateway` `POST /api/invite` ne valide ni le format ni l'existence de `company_id` — backend, bruyant, à la source. Note annexe : `auth-gateway/src/app/admin/actions.ts:65-70` jette l'erreur du lookup `companies` → `company_slug` peut passer à `null` en silence.
Expand Down
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ documents — and acts from the chat the team already uses. The unit of value is
brain*, not the dashboard; the unit of distribution is the *marketplace skill*, not the
install wizard.

**Status**: live in production. `hal-mcp` v0.2.1 deployed. 27 MCP tools across CRM, pipeline,
**Status**: live in production. `hal-mcp` v0.2.1 deployed. 30 MCP tools across CRM, pipeline,
tasks/sprints, document vault, identity, and the Edifice field workflow. CRM, vault, and the
Edifice pull→push loop all run on Supabase `zgkvbjqlvebttbnkklpo`.

Expand Down Expand Up @@ -112,7 +112,10 @@ the firm's brain it needs for the question at hand.

## MCP tools (hal-mcp v0.2.1)

27 tools. Reads are `list_*`/`get_*`; everything else mutates.
30 tools. Reads are `list_*`/`get_*`; everything else mutates. Read tools carry
`annotations: { readOnlyHint: true }` so a URL-less, permission-gated consumer (a Claude
artifact) can call them on load without a permission dialog; `push_mission_context` is
explicitly `readOnlyHint: false`.

**Identity**
- `whoami` — read-only probe: returns `user_email`, the user's `workspaces[]` (slug + role +
Expand Down Expand Up @@ -142,6 +145,8 @@ the firm's brain it needs for the question at hand.
**Edifice (conditional on `edifice.enabled`)**
- `list_edifice_missions` · `read_edifice_mission`
- `get_mission_with_assets` — notes + annotations + photo signed URLs
- `get_mission_context` — full context.json shape inline, zero signed URLs (URL-less consumers)
- `get_mission_photo` — single photo as resized base64 JPEG (image content block)
- `push_mission_context` — writes observations, building context, and optionally `photos[]` (crop_region + YOLO annotations); returns `updated`, `photos_updated`, `annotations_upserted`

---
Expand Down
10 changes: 10 additions & 0 deletions docs/cross-repo-log.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@ Keep entries ≤ 10 lines. Link outward; don't restate.

---

## 2026-07-28 — Artifact-callable Edifice tools (get_mission_context, get_mission_photo, readOnlyHint)

- **Repo**: hal
- **Commit / PR**: `archon/task-feat-artifact-callable-edifice-tools` (issue #82)
- **What shipped**: two new `hal-mcp` tools — `get_mission_context(mission_id)` returns the full `context.json`-shaped payload inline (header + `observations[]` + `notes[]` + `photos[]` metadata, **zero signed URLs**), and `get_mission_photo(photo_id, max_width?)` returns one photo as a resized base64 JPEG (magick-wasm, ~300 KB target). 12 read tools gained `annotations: { readOnlyHint: true }`; `push_mission_context` is now explicitly `readOnlyHint: false`. `get_mission_with_assets`'s fetch logic was extracted to a shared `fetchMissionRaw()` — its contract is unchanged. New npm dep `@imagemagick/magick-wasm@0.0.41` (WASM, fetched from CDN at cold start). No `edifice_*` schema change.
- **Impact on the other repo**: unblocks **bluegreen-marketplace#50** (Edifice front as a Claude artifact), Phases 2–3. Once this Edge Function is deployed, the downstream repo must **bump `plugins/hal/.mcp.json`'s version** so consumers pick up the new tools — that bump lives in bluegreen-marketplace and is not part of this PR. `build_context.py` in bluegreen-marketplace is the source shape `get_mission_context` was ported from; keep them in sync if either changes.
- **Follow-up**: deploy `hal-mcp` (`make deploy`), then live-verify the 3 acceptance criteria against reference mission `3f2bd270-fd5f-4d09-a64e-063a66348dc8`; then bump the downstream `.mcp.json` version.

---

## 2026-07-24 — service_role key removed from the map trigger; edge function auth decoupled

- **Repo**: edifice (`87252d14c`) + hal (`444fa3d`, `48f89fd`, deployed)
Expand Down
14 changes: 10 additions & 4 deletions docs/prd.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,16 +122,18 @@ columns on `halcrm_missions`.

### 3.2. The interface — hal-mcp

One Supabase Edge Function (`hal-mcp`, Deno, `@supabase/server`). 27 tools. **Reads via
One Supabase Edge Function (`hal-mcp`, Deno, `@supabase/server`). 30 tools. **Reads via
`list_*`/`get_*`, writes via mutation tools** — there is no fat-context blob; the agent pulls
exactly what a question needs.
exactly what a question needs. Read tools carry `annotations: { readOnlyHint: true }` so a
URL-less, permission-gated consumer (a Claude artifact) can call them on load without a
permission dialog; `push_mission_context` is explicitly `readOnlyHint: false`.

- **Identity**: `whoami` (read-only probe — `user_email`, `workspaces[]`, server-resolved `default_workspace_slug`; doubles as connectivity check)
- **CRM**: `create_company`, `list_companies`, `create_contact`, `list_contacts`, `log_interaction`
- **Pipeline**: `list_stages`, `list_projects`, `create_project`, `update_project`, `update_project_stage`
- **Tasks/sprints**: `create_task`, `list_tasks`, `update_task` (partial; no status, no sprint), `update_task_status`, `create_sprint`, `list_sprints` (status filter), `update_sprint` (rename, status, dates), `assign_task_to_sprint`
- **Vault**: `save_document`, `list_documents`, `get_document`, `get_document_file`
- **Edifice** (conditional): `list_edifice_missions`, `read_edifice_mission`, `get_mission_with_assets`, `push_mission_context`
- **Edifice** (conditional): `list_edifice_missions`, `read_edifice_mission`, `get_mission_with_assets`, `get_mission_context` (full context.json inline, no signed URLs — for URL-less consumers), `get_mission_photo` (single photo as resized base64 JPEG), `push_mission_context`

**Task & sprint model** — three single-responsibility write verbs, so a skill always knows
which tool to reach for. A task has independent fields and changing one never touches another:
Expand Down Expand Up @@ -210,8 +212,12 @@ workspace see everything; fine-grained permissions are out of scope.

### 6.1. Shipped (production)

- `hal-mcp` v0.2.1 — 27 tools, OAuth + API-key auth, RLS isolation. `whoami` +
- `hal-mcp` v0.2.1 — 30 tools, OAuth + API-key auth, RLS isolation. `whoami` +
`workspace_members.is_default` for server-side default-workspace resolution.
- **Artifact-callable Edifice tools (#82)** — `get_mission_context` (inline context.json
shape, zero signed URLs) + `get_mission_photo` (resized base64 JPEG) for consumers that
can't follow a URL (Claude artifacts). `readOnlyHint: true`/`false` annotations on all
13 relevant tools unblock artifact loading without an Allow/Deny dialog cascade.
- **Task/sprint completeness (LastDev Loop 1)** — `update_task` (partial),
`list_sprints` (resolve current via `status="actuel"`), `priority` + `external_ref` on
tasks. `halcrm_tasks.document` renamed to `external_ref`. v38 froze the surface; v0.2.1 adds
Expand Down
3 changes: 2 additions & 1 deletion supabase/functions/hal-mcp/deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"@modelcontextprotocol/sdk": "npm:@modelcontextprotocol/sdk@1.25.3",
"@supabase/server/core": "npm:@supabase/server@1.1.0/core",
"zod": "npm:zod@3.25.28",
"@supabase/supabase-js": "npm:@supabase/supabase-js@2.47.10"
"@supabase/supabase-js": "npm:@supabase/supabase-js@2.47.10",
"@imagemagick/magick-wasm": "npm:@imagemagick/magick-wasm@0.0.41"
}
}
5 changes: 5 additions & 0 deletions supabase/functions/hal-mcp/deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading