Be Civic — Cowork Plugin Specification (formerly v1-spec; renamed 2026-05-18 ahead of multi-harness expansion)

Canonical system specifications for the Be Civic project.

Be Civic — Cowork Plugin Specification

What this document is. The scope contract for the Be Civic Cowork plugin — the first concrete harness Be Civic ships. The Cowork plugin is the canonical onboarding path per architecture.md §3 principle 13 (plugin-as-bootstrap, 2026-05-17 amendment). This document covers acceptance criteria, plugin contents, Cowork-host-specific onboarding flow, alpha-operational consent posture, filesystem layout under the Cowork host, and build sequence.

What this document is NOT. The architecture spec. Universal architecture lives in the harness-agnostic sub-spec docs (start at README.md). The harness-agnostic onboarding flow (gate classification, confirmation gate, branded form delivery, folder-mount-after-submit) lives in architecture.md §24.3 / §24.6. This document pins each universal step to its Cowork primitive (mcp__visualize__show_widget, sendPrompt, mcp__cowork__request_cowork_directory) and adds Cowork-bundle-specific contents and on-disk file layout.

Multi-harness posture. As of 2026-05-18 the previous v1-spec.md was renamed cowork-plugin.md ahead of multi-harness expansion. Sibling harness specs (ChatGPT app, etc.) will get their own <harness>.md documents when those surfaces are locked. Procedure canonicals (bc-docs/skills/<id>/canonical.md) and the harness-agnostic flow are deliberately shared; the Cowork-specific surface mechanics live here.

Tag convention. Throughout this document, subsections carry an implicit [Cowork] (durable Cowork-harness spec content) or [Alpha-operational] (alpha-and-beta operational reality, revisited at post-alpha cutover) classification. Where ambiguous, the relevant tag is noted inline.

Procedure scope at v1. A single procedure: Belgian nationality declaration via art. 12bis (5-year residence path), filed at the commune État Civil. Three legalisation sub-skills cover origin-country routing (apostille-foreign-document-hague, consular-legalisation-foreign-document, eu-2016-1191-multilingual-form); a meta-skill (meta-no-skill-fallback) handles procedures outside the corpus. This is the worked example. All other Belgian admin procedures are out of v1 scope.

Customer scope. A single tester: friend referred 2026-05-11. Adult, EU or non-EU national, 5+ years registered residence in Belgium, considering nationality declaration. Pays €20/month for Claude Desktop with Cowork tab. Comfortable installing a plugin and pointing the agent at a folder she chose; not a developer.

1. Acceptance criteria

v1 is "shipped" when all of the following are true for the friend tester:

  1. She has installed the Be Civic plugin from the Cowork Plugins directory in Claude Desktop (or — per §1.1 fallback below — from the marketplace-by-GitHub-URL path or the zip-upload fallback). The plugin install places the harness skill, peer skills, drafter sub-agents, starter state templates, and schemas under the Cowork plugin runtime automatically; no zip download or manual folder selection is required at install time. (Folder selection happens later during onboarding, via mcp__cowork__request_cowork_directory, per §3.3.)
  2. She has connected mcp.becivic.be via Settings > Integrations > MCP (optional — v1 delivers full guidance with MCP absent via the §6 fallback chain).
  3. She has opened a session in the Cowork tab. The be-civic gate skill auto-activated on her opener and routed her through bc-onboarding.
  4. The harness has elicited her routing context via the branded onboarding form (per §3.2) and written it to <user-picked-parent>/BeCivic/profile.json (per §2.9 layout). The form populated the V1 profile fields per schemas/profile.schema.json: region, commune_nis5, administration_language, conversation_language (free text per the universal-amendment D27), civic_status, nationality_status, residency_status, plus the new V1 fields has_id_card (per D23; renamed from has_eID — depends on the universal proposal landing the rename in the live schema), browser_driving_preference (per D8), and the consent block with alpha_bundle / signed_at / version (per D28; see §3.8). The harness never asked for her name, date of birth, document numbers, NISS, or full address. The form rendered via mcp__visualize__show_widget with HTML returned from mcp__becivic__read_skill --with_form (per protocol.md §23.2; depends on the universal proposal landing the with_form parameter on the wire) with pre-population from her opener (per D33); on MCP failure, the plugin's static fallback HTML for her locale rendered instead (per §3.2 fallback paths).
  5. The harness has produced a situated nationality-application guidance document — citing the statutory hook (Code de la nationalité belge, art. 12bis), listing the required documents for her commune and language, and ending with a "verify with your commune" checklist.
  6. The harness has resumed in a second session and correctly identified her active procedure (active_procedures: ["nationality-application"]) without re-asking the routing questions. The Cowork-host returning-user detection per §3 fired the mini header (D54) at session start.
  7. At least one concern has been submitted from the session via the MCP mcp__becivic__submit_concern tool — or HTTP fallback to POST /api/concerns if MCP was not configured — and is visible in the Be Civic staging surface. Submission was gated on profile.consent.alpha_bundle: true per §3.8.
  8. She can delete <user-picked-parent>/BeCivic/ (via her OS file manager) and the next session starts fresh with no residual state. Plugin uninstall via Cowork's plugin manager is also supported and produces the same fresh-start behaviour. The harness MUST NOT prevent or create friction around deletion; deletion is the user's unilateral act per architecture.md §3 principle 11. The harness MUST NOT write state outside <user-picked-parent>/BeCivic/ or ${CLAUDE_PLUGIN_ROOT} (the plugin's read-only / writable plugin-data location).
  9. The harness called mcp__becivic__get_graph (or GET /api/skill-graph if no MCP) at session start, received the v1 graph (nationality target + three legalisation sub-skills + meta-skill), and routed her request through it. When her origin-country routing requires a legalisation sub-skill (apostille / consular-legalisation / EU 2016/1191), the harness loads the appropriate sub-skill via the requires edge without asking her to re-route manually.
  10. The friend's conversation language was detected and offered as a pre-fill in the form's conversation_language field; her form rendered in the V1 locale matching her UI language (one of EN, FR, NL, DE, AR, UK per D35). If her conversation language is none of those, the form rendered in English with an in-chat nudge in her language (per G24 and §3.2 locale-fallback rule).

Acceptance criterion 5 is the success signal: the friend produces a real document she can take to her commune. Criteria 1–4 and 6–10 verify the protocol contract.

1.1 Install path fallbacks

The plugin supports three install paths, in preference order:

  1. Cowork Plugins directory — when the Be Civic plugin is listed on the official Anthropic plugin registry, Customize → Browse plugins → Anthropic. Not yet available pre-launch.
  2. Personal marketplace from GitHubCustomize → Browse plugins → Personal → + → Add marketplace from GitHub, then paste the plugin repo URL. Auto-updates on every push. This is the v1 ship path.
  3. Zip-upload fallbackCustomize → My Uploads, upload a release zip from the plugin repo's Releases page. For testers behind proxies or network restrictions that block GitHub.

For Claude Code CLI: /plugin marketplace add <repo> then /plugin install be-civic. The CLI install path is supported for development dogfooding; it is not the customer-facing v1 surface.

2. In scope (build this)

2.1 Plugin bundle contents

The Cowork plugin bundle is the canonical delivery artefact. File layout:

be-civic-plugin/
├── .claude-plugin/
│   ├── plugin.json                       # plugin manifest (name, version, description, author)
│   └── marketplace.json                  # marketplace manifest (for personal marketplace install)
├── CLAUDE.md                             # OMITTED at plugin root — the harness CLAUDE.md is one-per-BeCivic-root,
│                                         # written by bc-onboarding into <user-picked-parent>/BeCivic/CLAUDE.md.
│                                         # ONE shared-root copy services every procedure under that root (§2.9).
│                                         # Source template lives at skills/bc-onboarding/references/harness-CLAUDE.md.
├── README.md                             # operator-facing repo readme; install instructions, repo status
├── .mcp.json                             # MCP server registration (becivic at https://mcp.becivic.be/mcp)
├── skills/
│   ├── be-civic/                         # gate skill — checks for project folder, routes to bc-onboarding if absent
│   │   ├── SKILL.md
│   │   └── references/
│   │       └── intro-header.html         # branded intro card for mid-session pickup (D54 mini header)
│   ├── bc-onboarding/                    # first-contact onboarding skill (owns first-contact only per D34)
│   │   ├── SKILL.md
│   │   └── references/
│   │       ├── onboarding.html           # branded HTML widget (Pattern B; rendered via mcp__visualize__show_widget)
│   │       ├── harness-CLAUDE.md         # template written into <user-picked-parent>/BeCivic/<project>/ at folder-mount
│   │       ├── onboarding.en.html        # static section-1-only fallback per D44 (forward commitment — see §C reconciliation doc)
│   │       ├── onboarding.fr.html        # forward commitment
│   │       ├── onboarding.nl.html        # forward commitment
│   │       ├── onboarding.de.html        # forward commitment
│   │       ├── onboarding.ar.html        # forward commitment — RTL
│   │       ├── onboarding.uk.html        # forward commitment
│   │       └── project-init/             # templates copied into <user-picked-parent>/BeCivic/<project>/ at mount
│   │           ├── profile.json          # starter state — all routing fields null
│   │           └── MEMORY.md             # empty narrative-memory index
│   ├── bc-discovery/                     # zero-match routing — drives to meta-no-skill-fallback or invites a skill draft
│   │   └── SKILL.md
│   ├── bc-document-handler/              # document intake, scrub-layer-1 invocation, document-content-discard rule
│   │   └── SKILL.md
│   ├── bc-path-traversal/                # walks <Path> tags, honours source priority and fallback_only
│   │   └── SKILL.md
│   ├── bc-session-close/                 # session-end roll-up: review observations, submit, ack
│   │   └── SKILL.md
│   └── bc-dossier-compilation/           # dossier assembly across procedure lifecycle (initial/refresh/final modes)
│       ├── SKILL.md
│       └── templates/                    # HTML templates: cover, checklist, divider, placeholder, filled forms
├── scripts/                              # plugin-side Python (preamble, scrub, dossier renderer module)
│   ├── preamble.py
│   ├── scrub-layer1.py
│   ├── ...
│   └── be_civic_dossier/                 # dossier renderer module — imported by user's render.py
│       ├── __init__.py
│       ├── dossier.py                    # Dossier container class
│       └── layouts/                      # 6 presentation-class transformers
├── vendor/                               # vendored pure-Python deps — no pip install required
│   ├── fpdf2/                            # new PDF page generation
│   ├── pypdf/                            # PDF merge + rotate + watermark overlay
│   ├── fonts/                            # bundled fonts (Inter, Source Sans Pro, Noto Sans Arabic, Noto Sans Cyrillic)
│   └── UPGRADING.md                      # vendor library upgrade procedure
├── agents/                               # subagents spawned by bc-session-close for heavy authoring
│   ├── bc-path-drafter.md                # drafts new path entries from session research-notes
│   └── bc-skill-drafter.md               # drafts new skills from end-to-end session walkthroughs
├── schemas/
│   ├── profile.schema.json               # for harness self-validation (V1 schema)
│   └── observation.v3.schema.json        # legacy observation envelope; superseded by per-type concern/amendment/draft schemas
├── data/
│   ├── scrub-rules.json                  # local Layer-1 regex scrub rules; refreshed at session start when reachable
│   ├── privacy-attachment.md             # alpha-specific terms (D49); forward commitment — see §C reconciliation doc
│   ├── privacy-snippet.md                # canonical privacy answer for meta-question shape (D47); forward commitment
│   └── mini-header-strings.md            # 8-10 rotating call-out variants (D13); forward commitment
├── scripts/
│   ├── preamble.py                       # session-start orchestrator: SESSION_ID, USER_DATA_DIR, PENDING_STATE, capability flags
│   ├── scrub-layer1.py                   # local regex scrub before any submission
│   ├── scan-orphan-buffers.py            # scans for stranded observation-buffer files from prior sessions
│   ├── scan-pending-state.py             # detects pending submissions awaiting user review
│   └── detect-browser-capability.py      # probes for Chrome / Chrome MCP / Be Civic MCP at session start
├── hooks/                                # consent-flow hook templates (post-alpha) — ships empty in v1 alpha
│   └── .gitkeep
├── .github/workflows/
│   └── plugin-validate.yml               # CI: schema validation, plugin.json shape, SKILL.md frontmatter strictness
├── .gitignore
└── LICENSE                               # MIT

Key plugin-format facts.

  • ${CLAUDE_PLUGIN_ROOT} is set by the Cowork plugin runtime at session start and resolves to the plugin's installed (read-only) folder root. ${CLAUDE_PLUGIN_DATA} is the writable plugin-data location surviving plugin updates; preamble.py falls back to ${CLAUDE_PLUGIN_ROOT} (script-parent) when both env vars are absent (Cowork-Project legacy mode).
  • The harness CLAUDE.md is NOT at the plugin root. It lives at skills/bc-onboarding/references/harness-CLAUDE.md as a template, and gets copied to <user-picked-parent>/BeCivic/CLAUDE.md (the shared BeCivic root — one CLAUDE.md services every procedure under it, per §2.9) at folder-mount time per §3.3. This is because Cowork's CLAUDE.md auto-load convention walks ancestors and picks up the BeCivic-root file when the user opens any procedure subfolder.
  • The plugin does not ship a local snapshot of procedure skill bodies. Procedure content (nationality, apostille, etc.) is MCP-delivered live from mcp.becivic.be (or HTTPS / WebFetch fallback per §6 of the harness CLAUDE.md). The plugin ships only meta-skills (be-civic gate, bc-onboarding, bc-discovery, bc-document-handler, bc-path-traversal, bc-session-close, bc-dossier-compilation) — the agentic behaviour layer. The procedure layer is served from becivic.be.
  • Forward commitments (the six locale fallback HTML files, privacy-attachment.md, privacy-snippet.md, mini-header-strings.md) are scheduled deliverables tracked in §5 build sequence. They are not yet present on disk; the reconciliation doc tracks them under "Specced but not built."

2.2 MCP server + api Worker additions

Existing mcp.becivic.be already exposes the per-type submission tools, find_skill, read_skill, get_graph, and path-directory tools. v1 adds the following surfaces:

1. Bootstrap (MCP-only):

  • mcp__becivic__bootstrap() — returns a one-shot orientation payload: the current /agents boot-loader text, the manifest, and the URL for the latest plugin install link. Called by agents on first contact when they want a deterministic alternative to WebFetch becivic.be/agents.

2. Skill-graph (MCP + HTTP parity):

Per §24.2 (see architecture.md; revised 2026-05-11 PM), the skill routing graph is served live from D1, not as a static file. v1 ships both surfaces:

  • mcp__becivic__get_graph(filters?) — MCP tool. Filterable by applies_to, status, customer_locale, profile_match. Returns nodes + edges per the response-shape schema in the schemas amendment.
  • GET https://becivic.be/api/skill-graph — HTTP parity endpoint. Same JSON response. Filters as query-string parameters. Required for harnesses without MCP and for the HTTPS fallback rung.

Both surfaces share the same D1 query and serializer. Edge caching: Cache-Control: public, max-age=60, s-maxage=60.

In v1 the graph has five nodes — nationality-application (target procedure, status beta at ship), apostille-foreign-document-hague, consular-legalisation-foreign-document, eu-2016-1191-multilingual-form (status alpha), and meta-no-skill-fallback (status beta). Edges encode requires relationships from nationality-application to each of the three sub-skills.

3. Extended read_skill with onboarding form (forward dependency on universal proposal):

The universal onboarding-rebuild proposal extends read_skill with with_form, app, locale, mode, pre_selected, and profile_snapshot parameters, and adds a sibling get_onboarding_form tool. Both return server-rendered branded HTML for the Cowork-host mcp__visualize__show_widget widget surface. This is a forward commitment — depends on the 2026-05-18-onboarding-rebuild.md universal proposal landing. Until it lands, v1 falls back to the static onboarding.html widget already in the plugin bundle (§3.2 details).

4. Cowork bindings for row_list form inputs with deferred-capture modes (W25, 2026-05-19):

The row_list form input type (defined in schemas.md "Form-input types") supports three capture modes; the type and its sentinel-payload contract are catalogue-backend concerns spec'd in schemas.md + protocol.md §23.2. Cowork's host-specific bindings:

  • Widget rendering. All three modes render via mcp__visualize__show_widget — the standard Cowork host primitive already used by the V1 branded onboarding form.
  • Folder-drop mode uses the Cowork-mounted project folder (via mcp__cowork__request_cowork_directory per §3.3). The path shown to the user resolves to <user-picked-parent>/BeCivic/<procedure-id>/inputs/<input-name>/. The agent reads dropped images from this path via the workspace bash / file tools after the user signals completion.
  • Post-submit hydration is owned by bc-onboarding (per its peer-skill paragraph in §2.3): when the form submit returns __status: "pending" on a row_list field, bc-onboarding's Step 7.1 probing reads the sentinel, runs the mode-appropriate hydration (poll folder + vision-extract for folder_drop; chat elicitation for chat), and re-renders the same widget pre-populated for user confirmation before writing the final value to the target store.

Locked design: ../docs/agent-ux/row-list-input-type-design.md.

D1 schema migration: the skills catalogue table gains columns applies_to (enum), tags (JSON array), prerequisites (JSON array of skill_ids), related (JSON array of skill_ids), profile_requirements (JSON array of profile.json field names). Backfill: one row each for the five v1 skills. Migration via bc-docs/migrations/<NNNN>-add-skill-graph-columns.sql.

2.3 Harness skill (be-civic gate + bc-onboarding peer + harness CLAUDE.md)

The two-layer runtime defined in §24.1 (see architecture.md), Cowork-plugin-shape:

  • skills/be-civic/SKILL.md is the gate. Its only job is to detect whether the current cwd is inside a Be Civic project folder (marker file at .be-civic/marker) and route accordingly. If yes → confirm and exit (CLAUDE.md inside the project is already driving). If no and the user has an admin query → invoke bc-onboarding in new-project mode. If no and no specific query → softer opening, offer to set up.
  • skills/bc-onboarding/SKILL.md owns first-contact only (per D34). Two modes: new-project (full setup-and-onboard sequence in this conversation, ending with handover to "re-open this folder in a new chat") and first-contact (running inside an existing project folder where CLAUDE.md detected PROFILE_JSON: absent). (A prior migrate-from-v1 mode was stripped 2026-05-18 — no users exist to migrate.)
  • skills/bc-onboarding/references/harness-CLAUDE.md is the harness body — Iron Law, situation-assessment-first, decision tree, profile/memory rules, MCP/HTTPS/WebFetch fallback chain, inline tag handling (<VV> / <Ref> / <Path> / <Skill> / <Observations> / <Risk>), observation handling, document handling, path traversal entry, dossier compilation entry, session close. This file is copied to <user-picked-parent>/BeCivic/CLAUDE.md (a single shared-root copy per §2.9) at folder-mount time so Cowork loads it as Project Instructions for any session opened from that BeCivic root or any procedure subfolder under it.
  • Peer skills (bc-discovery, bc-document-handler, bc-path-traversal, bc-session-close, bc-dossier-compilation) — each is a focused mode the harness invokes via the Skill-tool route. They live under the plugin bundle so they're loadable on any Be Civic project folder without per-project install. Per-skill purpose (one paragraph each):
    • bc-discovery — handles the no-verified-skill and no-verified-path cases. Has two modes: skill mode fires on graph zero-match for the customer's intent (or when bc-path-traversal returns unknown-skill-fallback); path mode fires when bc-path-traversal returns a structured miss signal (unknown-path-id or all-sources-failed-with-alternative). Frames the beat as "discovery mode" with the customer (not "no-skill fallback" — that's internal language), walks them through the procedure with research-as-we-go discipline, applies source-quality rules (Belgian statute / federal regulation / regional decree as citation-grade), and produces a durable research-notes-*.md file that the drafter sub-agents consume at session close. Helps today's customer through their procedure while building the artefact the next customer with the same intent will hit verified.
    • bc-document-handler — handles document presentation at the moment it happens (paste, upload, attachment, photo, or verbatim field description). Applies the always-on rules from harness CLAUDE.md §7 — take only the routing fields the active procedure needs, archive the original to the customer's machine under documents/<procedure-id>/, never write document bodies to routing stores (profile.json / MEMORY.md), maintain a cross-procedure index so subsequent procedures know which documents are already present. Inline by design: runs the per-drop extract-archive-confirm dialogue and abort handling without context-switching back to the procedure skill for every document.
    • bc-path-traversal — owns the §6.12 Path Directory traversal contract for any inline <span class="dsl dsl-path"><a href="https://becivic.be/paths/…" target="_blank" rel="noopener">Path: …</a></span> tag in a procedure body, any frontmatter requires_paths: entry reached at filing or document step, or any customer-initiated path request. Loads the catalogue, applies the three invariants (source-priority order, eligibility-predicate filtering against profile, fallback_only discipline with offline always last), executes per-source attempts with consent gates and handoff text, files a validation submission per attempt (confirm on success, reject with rationale on failure), and degrades to bc-discovery in path mode on exhaustion. Honours browser_driving_preference for Chrome MCP handoff vs AskUserQuestion vs markdown-link routing.
    • bc-session-close — handles end-of-session rollup. Two invocations: full close (procedure terminal step, explicit customer close, session timeout) runs the 9-step sequence — summary, save state, per-item observation review with the customer using customer-facing language ("list" / "notes", never "buffer"), drafter handoff for ready research-notes (auto-spawns bc-path-drafter and bc-skill-drafter sub-agents), contributions double-filtered, submission, next-time framing, cleanup, goodbye; resume-submit (invoked from the pending-state surface at session open) skips review/cleanup beats and jumps to submission for items the customer chose "handle now" on a deferred item from a prior session. Each per-item review respects the 24-hour cancel window contract.
    • bc-dossier-compilation — handles dossier assembly across the lifecycle of an application_dossier-class procedure. Three modes: initial (fires after bc-document-handler archives the first document for the procedure, producing an early-stage dossier with placeholders for outstanding items so the user sees the artefact taking shape), refresh (fires after each subsequent document arrives, replacing the relevant placeholder with the real document), and final (fires at the procedure's terminal step via an inline <span class="dsl dsl-skill"><a href="https://becivic.be/skills/bc-dossier-compilation/canonical" target="_blank" rel="noopener">Skill: bc-dossier-compilation</a></span> tag, producing the version the user prints and files). Produces a fully-bound A4 PDF containing: cover page, checklist table, "bring originals" callout, agent-authored officer notes, every gathered document rendered through one of six standardised presentation classes (id-card, full-page-cert, multi-page-doc, fee-receipt, filled-form, placeholder), and filled official forms at the end. Items where the procedure canonical's required-documents row marks the form as anything other than Printout acceptable receive a diagonal watermark in the user's conversation_language on every page (six locales: en/fr/nl/de/ar/uk). The renderer is vendored pure-Python (fpdf2 for new pages, pypdf for merging + watermark overlays); no system dependencies, no pip install at plugin-install time. The agent writes a render.py script in the user's project folder at <procedure-id>/dossier/render.py and iterates on it as documents arrive — the user can also edit the script directly. Agent-tooling sub-skill: produces an artefact rather than running a Belgian admin process; Be Civic does not stage or store the dossier (it intentionally carries identity-shaped data, which stays in the user's local folder); the renderer runs locally on the customer's machine. Locked design: ../docs/agent-ux/dossier-rebuild-design.md (W25, 2026-05-19).
  • Drafter sub-agents (bc-path-drafter, bc-skill-drafter) — spawned by bc-session-close to author amendments and new entries from session research-notes. Defined as agent files under agents/. Per-agent purpose:
    • bc-path-drafter — drafts new Be Civic path entries (single-outcome routes like portal flows, commune deeplinks, or single forms), or amendments to existing paths, from session research-notes. Spawned by bc-session-close, OR by bc-skill-drafter when content turns out path-shaped rather than skill-shaped. Reads <USER_DATA_DIR>/memory/research-notes-<path-slug>.md plus any existing entry from paths/index.json (for amendments); emits a structured payload per harness-spec §H.6. Model routing: amendment → sonnet (mechanical edit); proposal → opus (judgment-grade for new entries).
    • bc-skill-drafter — drafts new Be Civic procedure skills, or amendments to existing skills, from session research-notes. Spawned by bc-session-close when a customer's experience reveals a procedure the catalogue lacks or should change. Reads <USER_DATA_DIR>/memory/research-notes-<slug>.md plus any cited canonicals; emits a structured payload per harness-spec §H.6 (drafter-subagent output contract). Model routing: amendment → sonnet (mechanical edit); proposal → opus (judgment-grade for spec §15 compliance). Detects path-shaped content and routes to bc-path-drafter when appropriate.
  • Three-step fallback chain for data fetch and submission (per architecture.md §24.4.1; see harness-CLAUDE.md §6):
    1. MCP tool call if available
    2. HTTPS endpoint at becivic.be/api/... if MCP unreachable
    3. WebFetch of the canonical content from becivic.be if HTTP API unreachable
  • Observation submission follows the same chain: mcp__becivic__submit_concernPOST /api/concerns → buffer to disk under <USER_DATA_DIR>/.be-civic/sessions/<session-id>/observations-buffer.jsonl.

2.4 Procedure skills (MCP-delivered, NOT plugin-bundled)

The target procedure exists in the public corpus at bc-docs/skills/nationality-application/canonical.md (currently status: alpha). Its requires: frontmatter names three legalisation sub-skills, one of which applies per customer based on their origin country's treaty status:

Skill Selects when v1 status
nationality-application (target procedure) promote alphabeta for v1 ship
apostille-foreign-document-hague origin country is a Hague Apostille Convention signatory alpha
consular-legalisation-foreign-document origin country has no treaty relationship alpha
eu-2016-1191-multilingual-form origin country is an EU member state alpha
meta-no-skill-fallback zero-match routing per §24.8 (see architecture.md) promote to beta for v1 ship

These five skills are served live from mcp.becivic.be (or HTTPS fallback). They are NOT shipped in the plugin bundle — the harness fetches them at runtime via mcp__becivic__read_skill. This keeps procedure content updates flowing without plugin re-installs.

For v1:

  • All five skills must be reviewed for skill body discipline (§15.8 (see skills.md): no implementation paths, no operator references, no design rationale, no dogfooding notes), customer readability (§15.8 invariants 7 and 8: glosses for admin/legal jargon on first use; legislation references in prose form, not parenthetical citations), and the new invariant 11 on positioning copy (Be Civic is a tool the user's agent uses, not an agent itself — depends on the universal proposal landing).
  • The nationality skill must cover at minimum: eligibility (5-year continuous residence; permitted gaps; knowledge of an administrative language; social integration); the document list (proof of residence, language certificate, etc., per commune); the filing surface (commune État Civil); the typical timeline; the failure modes. Sources: Code de la nationalité belge art. 12bis (Belgilex link); Moniteur belge official text; the commune's published procedure page where available.
  • The three legalisation sub-skills ship at their current alpha status. Promotion to beta happens at v1.1+ after each has been exercised through at least one real customer session.
  • The harness chooses among the three sub-skills deterministically: once the customer's origin-country treaty status is known (one of hague-acceded, non-treaty, eu-member-civil-status), the harness loads the matching sub-skill via the <Skill> composition tag in nationality's body or via the explicit requires: edge in the graph.

2.5 No-skill-fallback meta-skill (meta-no-skill-fallback)

Per §24.8 (see architecture.md), the no-skill-fallback meta-skill is the routing floor for procedures Be Civic doesn't yet cover. It ships at v1 so referrals (or the friend if she pivots mid-session to a non-nationality task) get a graceful "Be Civic doesn't have this verified yet, here's what I'll do anyway" response.

v1 deliverable shape:

  • bc-docs/skills/meta-no-skill-fallback/canonical.md — new skill canonical. Carries the §24.8 obligations as customer-facing instructions: acknowledge openly, run Be-Civic-style, follow research-method guidance (Belgilex / commune sites / Moniteur belge primary; blog posts and social media advisory only; sources >12 months flagged), identify and reuse corpus sub-skills, offer skill-draft submission at session end. Status beta at v1 ship.
  • D1 catalogue row with applies_to: meta. The harness's get_graph always returns this node when filters don't otherwise match.
  • Harness routing logic in harness-CLAUDE.md §3 step 4 on zero-match loads the meta-skill via the standard skill-tool route, identical to loading any procedure skill.

What v1 does NOT include from §24.8: the skill-draft submission flow degrades to producing a draft markdown blob and instructing the customer to copy/paste into the manual form at becivic.be/agents/submit/draft; the drafting-skills skill review is queued for v1.1.

2.6 Profile fields used in v1

profile.json schema is the v1 catalogue per schemas/profile.schema.json. The v1 harness populates and reads only the fields nationality requires:

  • region, commune_nis5, administration_language, conversation_language (routing)
  • nationality_status (eligibility check)
  • residency_status, residency_history (5-year continuity check)
  • employment_history (continuity supporting evidence)
  • civic_status, dependents (procedure variations: cohabitant-legal, minor children)
  • document_inventory.has_id_card and other doc-presence booleans (forward dependency on the universal proposal landing has_id_card)
  • browser_driving_preference (forward dependency)
  • consent block: alpha_bundle / signed_at / version (forward dependency; alpha-operational per §3.8)
  • active_procedures (starts with ["nationality-application"] once she commits)

Other fields (education_history) are part of the schema but not elicited or read in v1.

Date precision — YYYY-MM uniformly across all date fields. Every date field in profile.json is encoded as YYYY-MM. Day-level precision MAY be held in <USER_DATA_DIR>/.be-civic/sessions/<session-id>/facts.json for the active session only (for deadline reminders) and MUST NOT enter persistent state. See privacy.md §8.7.4 "Date precision".

2.7 Concerns (HTTP submission MUST work, MCP MAY work)

The 2026-05-15 taxonomy normalization renames observationconcern and replaces the three-value event_type enum with target_type as the sole discriminator. The harness MUST submit concerns during her session when:

  • A document name or fee value in the skill turns out to differ from what she encountered → concern with target_type=volatile_value.
  • The skill's guidance is unclear, wrong, or missing context for her specific situation → concern with target_type=skill.
  • She mentions a procedure or sub-procedure Be Civic does not have a skill for → concern with target_type=skill_graph. This MUST happen only after the gate-skill classifier has classified the opener as procedure-intent and after the user has accepted the confirmation gate — meta-question and exploring shapes never produce a skill_graph concern. (Gate-classifier ordering per skills.md §15.7 obligation 12 update — forward dependency on the universal proposal.)

Submission path:

  • MCP configured: mcp__becivic__submit_concern is the deterministic path.
  • MCP absent: HTTPS POST https://becivic.be/api/concerns per-type endpoint with the v4 concern schema body, or POST /api/feedback polymorphic envelope.

All submissions are gated on profile.consent.alpha_bundle: true per §3.8.

2.8 agents.mdx boot loader on becivic.be

The revised boot loader deployed to becivic.be/agents. It must include plugin install instructions (the canonical entry point per architecture.md §3 principle 13), the marketplace-by-GitHub-URL path, and the zip-upload fallback per §1.1. Tier-detection prose is retired — under the plugin model, the agent operates inside the plugin runtime with filesystem-availability assumed; degradation to advice-only happens only when the filesystem write probe fails (preamble emits bundle_root_read_only).

2.9 Filesystem layout under the Cowork host

Under the Cowork plugin (V1 onwards), <USER_DATA_DIR> resolves to <user-picked-parent>/BeCivic/ — the user picks the parent folder via the Cowork host's directory picker (mcp__cowork__request_cowork_directory) at form-submit time per §3.3. The previous flat-~/.be-civic/ layout is retired under the Cowork plugin path.

Profile and shared state live at the BeCivic root; per-procedure state lives in a project subfolder named after the procedure. Multiple projects coexist under the same BeCivic root for the same user.

<user-picked-parent>/BeCivic/
├── CLAUDE.md                           # ONE harness CLAUDE.md at the BeCivic shared root; services every procedure
├── profile.json                        # shared across the user's procedures; see privacy.md §8.7.4
├── MEMORY.md                           # shared narrative index across procedures
├── trust_contract_seen.json            # all-null in V1; scaffolded for future use (D46 dropped)
├── privacy-attachment.md               # copy from plugin (D49); alpha-specific terms, user-readable
├── .be-civic/                          # hidden directory holding system-only state
│   ├── marker                          # plugin-recognises-folder marker; lives here, not in per-procedure subfolders
│   └── sessions/
│       └── <session-id>/               # per-session ephemeral state; deleted on session close
│           ├── facts.json
│           ├── dossier-draft.md
│           └── observations-buffer.jsonl
├── <project-name-1>/                   # one subfolder per procedure (e.g. naturalisation/)
│   │                                   # NO CLAUDE.md here — Cowork auto-load walks ancestors and picks up BeCivic/CLAUDE.md
│   ├── procedure_progress.md           # narrative + state for this procedure
│   ├── documents/                      # original documents the user uploaded (lazy — only when there's content)
│   └── memory/
│       └── research-notes-*.md         # session-scoped research notes; per-procedure narrative
├── <project-name-2>/                   # additional projects coexist; same NO-per-procedure-CLAUDE.md rule
├── submissions.jsonl                   # cumulative receipt log of all submitted items (shared)
└── analytics-outbox.jsonl              # offline queue for analytics events (shared)

Note: ONE shared-root CLAUDE.md, no per-procedure copies. A single CLAUDE.md lives at the BeCivic shared root (<user-picked-parent>/BeCivic/CLAUDE.md) and services every procedure under it. Cowork's CLAUDE.md auto-load convention walks ancestors when the user opens any procedure subfolder, so the BeCivic-root CLAUDE.md is the one that loads regardless of which procedure subfolder the user is in. Per-procedure subfolders MUST NOT carry their own CLAUDE.md — Cowork's surface does not support per-subfolder CLAUDE.md branching, and a per-procedure copy would duplicate the harness body and risk drift between procedures. The shared-root model is also consistent with how profile.json and MEMORY.md are shared across procedures.

Marker location. The marker lives at <user-picked-parent>/BeCivic/.be-civic/marker — inside the hidden .be-civic/ directory at the BeCivic shared root, not in per-procedure subfolders. The hidden subdirectory keeps system state out of the user's sidebar. The be-civic gate skill walks cwd ancestors and finds the marker at the BeCivic root regardless of which procedure subfolder the user is currently in. System-only files (marker, future internal state) live in .be-civic/ at the BeCivic root OR remain in the plugin install itself if they don't need to be in the user's folder. Older Be Civic projects created before the hidden-subdir convention used a top-level .be-civic-project marker; the be-civic gate skill checks that fallback location and offers to migrate.

Lazy subfolder creation. The harness does NOT create empty placeholder subfolders at mount time. documents/<procedure-id>/ gets created when bc-document-handler first writes a document. .be-civic/sessions/<session-id>/ gets created when the first observation lands or session state needs to persist. The project root stays clean — from the user's sidebar perspective they see only what they can read and understand (CLAUDE.md, profile.json, MEMORY.md initially; documents/, research-notes/ later as they accumulate). System state lives under .be-civic/ and stays hidden.

.gitignore note. Any harness writing to a <user-picked-parent>/BeCivic/ directory inside a git-tracked tree MUST add BeCivic/ (or the absolute equivalent) to the nearest .gitignore before writing the first file.

Latin-script paths (G20). On-disk folder names use Latin-script (ASCII) only — <project-name-N> is the kebab-case procedure id or a transliterated slug from a user-provided label. The display layer (chat acknowledgements, file-list summaries) MAY render in the user's preferred script.

"Project" terminology kept (D48 dropped). Context disambiguates Be Civic projects from Cowork's project concept. Plugin documentation MAY use "Be Civic project" where ambiguity is plausible.

Other harnesses (the future ChatGPT-app harness, etc.) may have different on-disk semantics — or no on-disk semantics at all. Each harness spec documents its own filesystem-or-not story.

3. Onboarding flow (Cowork host)

This section specifies the Cowork host instantiation of the universal onboarding flow defined in architecture.md §24.6 (per the 2026-05-18 onboarding-rebuild sibling amendment — forward dependency). The universal flow has three beats: gate-classification + confirmation, branded form delivery, and folder-mount-after-submit. This section pins each beat to Cowork primitives (mcp__visualize__show_widget, sendPrompt, mcp__cowork__request_cowork_directory) and lists the on-disk fallback HTML file paths.

The flow is owned by the bc-onboarding skill (first-contact only per D34); the gate skill (be-civic) classifies before bc-onboarding loads.

3.1 Gate classification and confirmation gate (Cowork shape)

The gate-skill classifier and confirmation gate fire on the Cowork host as plain chat prose — no widget, no branded surface. The user sees ordinary chat output from their agent. The confirmation copy adapts to skill-match confidence (high/medium/low per D18). Plugin install opted the user into the conversation; the folder ask opts them into putting files on disk (D1). Cowork doesn't add anything specific here; the universal flow runs as-is.

On user "yes" to the confirmation gate, the agent proceeds to §3.2 (form delivery). On "no," graceful exit ("No problem — I can keep helping with that without Be Civic. Let me know if you change your mind."). No mcp__cowork__request_cowork_directory call, no folder created.

3.2 Branded form delivery (Cowork shape)

After the confirmation gate is accepted, the agent surfaces the pre-form loading message (D19) as a plain chat message:

"Okay, I'm going to pull together an onboarding form for you. Just give me a moment while I analyse your case."

The agent then fires one MCP call (single round-trip per D43):

  • With a candidate procedure: mcp__becivic__read_skill(skill_id, with_form: true, app: "cowork", locale, mode: "first-contact", pre_selected, profile_snapshot). Returns {canonical, form_html} — the canonical body for the agent's context, the branded HTML for mcp__visualize__show_widget. Forward dependency on universal proposal.
  • Without a candidate procedure (low-confidence intent): mcp__becivic__get_onboarding_form(app: "cowork", locale, pre_selected). Returns {form_html} — section-1-only HTML; the agent runs skill-match against the form's answers post-submit. Forward dependency on universal proposal.

The agent calls mcp__visualize__show_widget with the returned form_html. The user sees the Be Civic-branded onboarding form rendered inside the Cowork chat surface as a widget.

Branded HTML widget specifics (Cowork-only).

  • The widget HTML uses the Version-1 styling (D25): cream pills (#f5f4ee, weight 500) at rest, gold (#fae042, weight 700) when selected. Inputs white with thin hairline border. Submit = ink-body dark CTA.
  • Bare form elements (<button>, <input>, etc.) use inline style="all: unset; ..." because Cowork's own form CSS out-competes <style>-block rules on bare form tags (D24). Non-form elements (panel divs, headings) use <style>-block rules without all: unset.
  • For locale ar, the form root carries dir="rtl" and the widget is laid out right-to-left.

MCP-unreachable fallback (D44; Cowork file paths). If the MCP call fails after two retries, the agent loads the plugin's static fallback HTML for the user's locale at:

${CLAUDE_PLUGIN_ROOT}/skills/bc-onboarding/references/onboarding.en.html
${CLAUDE_PLUGIN_ROOT}/skills/bc-onboarding/references/onboarding.fr.html
${CLAUDE_PLUGIN_ROOT}/skills/bc-onboarding/references/onboarding.nl.html
${CLAUDE_PLUGIN_ROOT}/skills/bc-onboarding/references/onboarding.de.html
${CLAUDE_PLUGIN_ROOT}/skills/bc-onboarding/references/onboarding.ar.html  # RTL
${CLAUDE_PLUGIN_ROOT}/skills/bc-onboarding/references/onboarding.uk.html

(Note: the live plugin currently ships only the single onboarding.html widget under the same path. The six locale fallback files are a forward commitment per the §C reconciliation doc.)

The agent passes the fallback HTML to mcp__visualize__show_widget directly. The fallback is section-1-only (no procedure routing); Section 2 is handled post-submit via AskUserQuestion (≤4 simple) or a Tier-2 elicitation form (D45).

If the user's conversation language is none of the six V1 locales (D35), the agent loads onboarding.en.html AND tells the user in their conversation language that the form is in English while the conversation continues in their preferred language (G24). Example copy in Spanish: "El formulario está en inglés, pero seguimos hablando en español; léelo en inglés y respóndeme en español si tienes dudas."

Degraded-mode framing (G23). Whenever the agent falls back to the plugin's static HTML, it MUST name the degradation explicitly: "I can't reach the Be Civic server right now, so I'll use the offline form on your machine. We can wait a few minutes and try the full form, or proceed with the offline one — it covers everything except the procedure-specific questions, which I'll ask you directly after submit. Which would you prefer?" The agent does not silently substitute the fallback.

3.3 Folder mount after submit (Cowork shape)

Folder mount happens after form submit, not upfront (D11). The flow is the load-bearing inversion from the prior design. The Cowork-host mechanism:

  1. The widget's JS submit handler calls sendPrompt with the form's collected values as a colon-delimited payload (see schemas.md form-payload contract — forward dependency).
  2. The agent receives the values as a chat message and caches them in working memory.
  3. The agent calls mcp__cowork__request_cowork_directory — the Cowork-host tool that pops the OS file picker. The user picks a parent folder.
  4. The agent creates BeCivic/ inside the parent folder per §2.9, then creates the first per-procedure subfolder BeCivic/<project-name>/. The project name is derived from the matched procedure id or, for low-confidence intent, asked once after the picker returns.
  5. The agent writes the BeCivic-root shared contents (one set per BeCivic root, not per procedure):
    • BeCivic/CLAUDE.mdsingle shared-root copy from ${CLAUDE_PLUGIN_ROOT}/skills/bc-onboarding/references/harness-CLAUDE.md (services every procedure; per §2.9 no per-procedure CLAUDE.md is written)
    • BeCivic/profile.json — written from the cached form values (template at ${CLAUDE_PLUGIN_ROOT}/skills/bc-onboarding/references/project-init/profile.json)
    • BeCivic/MEMORY.md — empty narrative index (template at ${CLAUDE_PLUGIN_ROOT}/skills/bc-onboarding/references/project-init/MEMORY.md)
    • BeCivic/privacy-attachment.md — copied from ${CLAUDE_PLUGIN_ROOT}/data/privacy-attachment.md (forward commitment)
    • BeCivic/trust_contract_seen.json — all-null in V1; scaffolded for future use
    • BeCivic/.be-civic/marker — folder marker, inside the hidden .be-civic/ at the BeCivic root And the per-procedure subfolder contents (only procedure-specific things — NO CLAUDE.md, NO marker, NO profile):
    • <project>/procedure_progress.md — empty starter
  6. The agent acknowledges to the user: "Saved locally at <parent>/BeCivic/<project-name>/. Only the categorical fields you ticked travel — your name, address, identifiers stay in this folder." JIT trust clause 1 (anonymity) fires here.

Why sendPrompt and not a widget-side write. The widget cannot return real filesystem paths (browser security). request_cowork_directory is the host-tool that mounts the folder; the form-submit-then-picker pattern is the only valid way to mount under the Cowork host (D11). sendPrompt is how the widget hands the form values back to the agent; the agent then drives the picker.

Post-submit probing (D21, D22). Folder mount is the start of high-confidence capture, not the end. The agent probes uncertain answers post-submit — "I'm not sure" responses, contradictions between form fields and earlier chat content, fields that are load-bearing for the procedure (residence dates, exact card type, exact employment status). This is the "lawyer onboarding a client" framing in D21; mistakes captured here compound into wrong procedure paths.

Handover to subsequent session. After folder mount and post-submit probing, the agent says:

"Setup is done. From here, open this folder in Cowork to continue — the next conversation you open from this folder will have the Be Civic harness loaded automatically, and we can pick up where we left off."

It does NOT continue into the procedure body in this conversation. The handover is the natural break (per bc-onboarding SKILL.md §1 step 8).

3.4 Canonical onboarding sequence (informative)

The end-to-end first-contact flow on Cowork, expressed as a sequence:

 user           agent         gate skill    bc-onboarding   MCP server    Cowork host    filesystem
  │               │                │              │              │              │              │
  │ opener msg    │                │              │              │              │              │
  ├──────────────▶│                │              │              │              │              │
  │               │ classify opener│              │              │              │              │
  │               ├───────────────▶│              │              │              │              │
  │               │ procedure intent              │              │              │              │
  │               │◀───────────────┤              │              │              │              │
  │               │                │              │              │              │              │
  │ confirmation gate (D10): "set up a project?"  │              │              │              │
  │◀──────────────┤                │              │              │              │              │
  │  yes          │                │              │              │              │              │
  ├──────────────▶│                │              │              │              │              │
  │               │ invoke bc-onboarding          │              │              │              │
  │               ├──────────────────────────────▶│              │              │              │
  │ "pull together a form..." (D19)               │              │              │              │
  │◀───────────────────────────────────────────────┤              │              │              │
  │               │                │              │ read_skill(skill_id, with_form, pre_selected)
  │               │                │              ├─────────────▶│              │              │
  │               │                │              │ {canonical, form_html}      │              │
  │               │                │              │◀─────────────┤              │              │
  │ show_widget(form_html) — branded form         │              │              │              │
  │◀───────────────────────────────────────────────┤              │              │              │
  │ submit (sendPrompt with values)               │              │              │              │
  ├───────────────────────────────────────────────▶│              │              │              │
  │               │                │              │ request_cowork_directory     │              │
  │               │                │              ├──────────────────────────────▶              │
  │ (OS picker)   │                │              │                              │              │
  │◀───────────────────────────────────────────────────────────────────────────────┤            │
  │ parent folder picked            │              │                              │              │
  ├───────────────────────────────────────────────────────────────────────────────▶              │
  │               │                │              │                              │ mkdir BeCivic/<project>/
  │               │                │              │                              ├─────────────▶│
  │               │                │              │                              │ write profile.json, CLAUDE.md, marker
  │               │                │              │                              ├─────────────▶│
  │ "Saved locally at <path>." (JIT clause 1)     │                              │              │
  │◀───────────────────────────────────────────────┤                              │              │
  │ (post-submit probing per D21 begins)          │                              │              │
  │ handover: "open this folder in Cowork to continue"            │              │              │
  │◀───────────────────────────────────────────────┤                              │              │

Three notable properties:

  • One MCP round-trip per the D43 single-call pattern (the form HTML and canonical body return together).
  • Folder mount happens after the form, not before; mcp__cowork__request_cowork_directory is the only valid path to a real filesystem path under Cowork (browser security prevents widgets from returning real paths).
  • The mini header (§3.9) fires at the confirmation-gate acceptance moment for mid-session triggers (G30) — that's the transition from "agent doing other work" to "agent actively using Be Civic for procedure work."

3.5 bc-onboarding under Cowork — host-tool bindings

The universal harness obligations on skills.md §15.7 (obligations 24–31 — forward dependency on universal proposal) describe gate classification, confirmation gate, form rendering, fallback handling, locale fallback, folder-after-submit, post-submit probing, and three-modes ownership without committing to specific host tool names. Under the Cowork plugin, those obligations bind to the following host primitives:

Universal obligation Cowork binding
Obligation 24: run gate-skill classifier Plain chat prose; no Cowork-specific surface
Obligation 25: fire confirmation gate before any branded surface Plain chat prose; before any mcp__visualize__show_widget call
Obligation 26: render branded onboarding form via MCP read_skill --with_form or get_onboarding_form Form rendered via mcp__visualize__show_widget(form_html). Form-payload return via the widget's JS submit handler calling sendPrompt(values). pre_selected payload drawn from session chat history, Cowork's ${CLAUDE_PLUGIN_ROOT}/state/last-session.json (if accessible), prior procedure_progress_*.md files under <user-picked-parent>/BeCivic/, and existing BeCivic/profile.json (if present).
Obligation 26 fallback: load plugin's static fallback HTML on MCP failure Fallback HTML paths: ${CLAUDE_PLUGIN_ROOT}/skills/bc-onboarding/references/onboarding.<locale>.html for each of EN, FR, NL, DE, AR (RTL), UK. Agent passes file contents to mcp__visualize__show_widget directly.
Obligation 27: name MCP-unreachable degradation explicitly Plain chat prose; surfaced before the fallback mcp__visualize__show_widget call
Obligation 28: locale fallback to EN form + speak user's language Plain chat prose surfacing locale-fallback notice; onboarding.en.html is the fallback file
Obligation 29: mount folder after form submit using host directory picker mcp__cowork__request_cowork_directory is the host-tool. Agent calls it with no arguments; user picks a parent folder; tool returns picked path. Agent creates BeCivic/<project>/ under the picked parent.
Obligation 30: probe uncertainty post-submit Plain chat prose; no Cowork-specific surface
Obligation 31: bc-onboarding owns first-contact only; harness handles returning + multi-active bc-onboarding invoked via Skill-tool route on first-contact; on returning sessions, harness fires mcp__becivic__read_skill --with_form mode="returning" and renders via mcp__visualize__show_widget directly, bypassing bc-onboarding.

Sibling harnesses (ChatGPT-app, etc.) will get their own §3.5-style binding tables in their respective harness specs.

Status: operational — varies by phase. This subsection describes how consent and the privacy attachment work during the Be Civic alpha and beta programmes. At post-alpha cutover, granular per-stream consent controls ship and the in-plugin privacy attachment is retired in favour of the live becivic.be/privacy page. The contract surface (form submit = consent) is durable; the specific statement copy and the attachment-in-plugin mechanic are alpha-operational.

Statement copy (V1, alpha)

The required statement on the onboarding form's consent panel reads verbatim:

You're part of the Be Civic alpha. By continuing, you agree that anonymous session telemetry and observations on the catalogue are part of how Be Civic learns during pre-launch. We need this to ship a good product.

An expansion (collapsible disclosure below the statement) explains:

  • What "anonymous session telemetry" includes: session-open / session-close events, scrub-rules fetch outcomes, MCP/HTTP fallback rates. No identifying content. Defined by schemas.md §6.2.6 (analytics).
  • What "observations on the catalogue" includes: per-attempt path-source validations, skill_amendment / skill_draft / concern / volatile_value observations the user explicitly approves at session close. Defined by schemas.md §6.2.1 and §6.2.2.
  • That to stop using Be Civic entirely during alpha, the user tells their agent and deletes the BeCivic folder.
  • That granular per-stream opt-out controls are part of post-alpha.

A hidden input sends consent.alpha_bundle: yes automatically on submit (user agreed by clicking Continue). Per D28, this is a statement, not a checkbox — the click on Continue is the consent gesture.

Privacy attachment (alpha-specific terms)

The plugin ships the alpha-specific terms at:

${CLAUDE_PLUGIN_ROOT}/data/privacy-attachment.md

(Forward commitment — file not yet present on disk; tracked in §5 build sequence and §C reconciliation doc.)

At folder-mount time (per §3.3 step 5), the agent copies this file to <user-picked-parent>/BeCivic/privacy-attachment.md so the user can read it via their file manager without needing the plugin path (D49). The attachment is distinct from the live becivic.be/privacy page — the page exists during alpha as a placeholder pointing at the in-plugin attachment to keep the alpha boundary visible. See §3.7 below for the lifecycle.

Canonical privacy snippet

When the agent is asked a data-handling meta question (per the gate-classifier "meta" shape — forward dependency on the universal proposal's Amendment 4.1), it draws from the canonical privacy snippet at:

${CLAUDE_PLUGIN_ROOT}/data/privacy-snippet.md

(Forward commitment.)

for consistent answers across agents and sessions (D47). Like the privacy attachment, the snippet content is alpha-specific; post-alpha cutover replaces it with the live becivic.be/privacy content rendered as plain markdown.

If the user reads the statement, decides not to participate, and closes the form without submitting, the agent says: "Be Civic is opt-in during alpha. Come back when we've shipped granular controls." No project is created, no folder is mounted, no observations are submitted. The form-submit gesture is the consent gesture; absence of submit = absence of consent.

3.7 Alpha privacy attachment lifecycle (alpha-operational)

Status: operational — varies by phase.

The alpha-specific privacy terms ship as ${CLAUDE_PLUGIN_ROOT}/data/privacy-attachment.md in the plugin bundle (per the 2026-05-17 plugin-as-bootstrap amendment, paired with §3.6 above). The plugin install copies the file to <user-picked-parent>/BeCivic/privacy-attachment.md at folder-mount time so the user can read it via their file manager (D49).

Versioning. The attachment is versioned via profile.json.consent.version (per §3.8). Users on older versions see a one-line nudge at session-start if the attachment version on disk lags the plugin-shipped version. The nudge is plain chat prose; it does not block the session.

Live /privacy page on becivic.be. The live page exists at https://becivic.be/privacy during alpha as a placeholder pointing at the in-plugin attachment (so anyone auditing the public site can find the operative terms). At post-alpha cutover, the live page becomes the operative terms — it describes the post-alpha granular consent controls (per stream: telemetry, observations, draft submissions, amendment submissions, each independently opt-out-able).

Divergence summary.

Aspect Alpha attachment (in plugin) Post-alpha /privacy (live page)
Operative during Alpha + beta programmes Post-alpha cutover onwards
Consent posture Required statement, no granular opt-out (D28) Granular per-stream controls
Distribution Plugin bundle → copied to BeCivic folder Public web page, rendered from bc-docs/docs/privacy.mdx
Versioning profile.json.consent.version; in-plugin nudge on version mismatch Page version; no per-user tracking

Retirement. At post-alpha cutover the plugin stops shipping privacy-attachment.md. Existing user folders retain their copy as a historical record (the agent doesn't delete it; deletion is the user's unilateral act per architecture.md §3 principle 11).

Status: operational — varies by phase. During alpha and beta, the profile.json.consent object carries three keys. Post-alpha cutover replaces them with granular per-stream consent keys (not yet defined; tracked in roadmap).

Alpha-operational keys. Defined in privacy.md §8.7.4 as part of the profile schema (forward dependency on the universal proposal). Operational meaning:

Key Type Operational meaning
consent.alpha_bundle bool true means the user submitted the onboarding form during alpha and saw the bundled consent statement (D28). Precondition for any observation / concern / amendment / draft submission during alpha. The agent reads this before any submission and refuses to submit if false.
consent.signed_at ISO8601 timestamp The moment of form submit. Captured by the form's JS submit handler before sendPrompt. Used to correlate consent with the operative statement version.
consent.version string The version string of the alpha-consent statement (e.g. "alpha-r1"). Corresponds to the version field on the in-plugin privacy-attachment.md. Mismatch triggers the one-line nudge per §3.7.

Wire shape. The consent block does not appear on any submission envelope (per universal proposal Amendment 7). Consent is profile-level state, captured once at onboarding, read by the agent before any submission. Keeping it off the wire schemas means: (a) consent is captured once; (b) the agent gates submission on profile state; (c) the wire stays unchanged so any Be Civic-compatible harness submits using the same shape.

Post-alpha trajectory. At cutover, consent.alpha_bundle is retired. The granular per-stream keys (likely consent.telemetry, consent.observations, consent.drafts, consent.amendments, each bool | "ask") ship alongside an UI affordance for changing them. The agent reads the relevant key before each submission type. These post-alpha keys are not yet defined — they will be specified in a future amendment when the post-alpha consent flow is designed.

Migration. At post-alpha cutover, existing profiles with consent.alpha_bundle: true are auto-migrated to the granular keys with conservative defaults (telemetry: true, observations: true, drafts: ask, amendments: ask) and the user is prompted on next session-start to confirm or change. Migration logic lives in the harness, not the server.

3.9 Returning-user mini header (Cowork shape)

Under the Cowork plugin, "a Be Civic folder exists" is detected as follows on session start (per architecture.md §24.5.1 — forward dependency):

  1. The harness reads ${CLAUDE_PLUGIN_ROOT}/state/last-mount.json (a plugin-internal file recording the user-picked parent path from the most recent successful mount). If absent or unreadable, the harness probes user-OS-conventional locations (e.g. ~/Desktop, ~/Documents) for a BeCivic/.be-civic/marker file. Probing is best-effort; absence is interpreted as first-contact.
  2. If a BeCivic/ folder is located, the harness reads BeCivic/profile.json and BeCivic/MEMORY.md. Presence of either with non-default content classifies the session as returning; absence classifies as first_contact.
  3. The mini header fires immediately on returning detection (D54) via mcp__visualize__show_widget with a single-line branded card from ${CLAUDE_PLUGIN_ROOT}/data/mini-header-strings.md (forward commitment).

Sibling harnesses without a host-side persistent plugin state file (e.g. a pure web-app harness) will need a different mechanism — likely a server-side bookmark keyed off a user identifier or an in-conversation memory marker.

4. Open questions for v1

These need answers before friend-tester delivery but are not architectural decisions:

  1. Cowork host directory picker behaviour. Does mcp__cowork__request_cowork_directory exist at the agent's tool surface in a fresh Cowork session, and does it behave per D11 — pop the OS picker, return a real filesystem path the agent can write to? Specifically: (a) the tool exists at the agent's tool surface, (b) calling it pops the OS file picker, (c) the user's pick returns a real path the agent can write to, (d) the agent can create BeCivic/<project>/ under the picked parent without further prompts. Verification task; resolve during build step 1.
  2. Does Cowork agent have write access under the picked parent folder? Expected yes (the whole point of the directory-picker feature), but verify empirically. Verification task; will be answered during build step 2 (write a probe file).
  3. Does Cowork agent see the new read_skill --with_form and get_onboarding_form parameters? The universal proposal extends read_skill with with_form, app, locale, mode, pre_selected, profile_snapshot; adds the get_onboarding_form sibling tool. v1 verifies that Cowork's tool-call layer passes these parameters through to mcp.becivic.be unchanged, and that the server's HTML response is consumable by mcp__visualize__show_widget. Verification task; resolve during build step 1.
  4. Does the agent in Cowork have permission to fetch URLs? Needed for the agents.mdx boot-loader fetch step and HTTPS / WebFetch fallback. If not, the boot-loader text must be inlined in the plugin bundle.
  5. Language of harness output for the tester. Defaults to conversation_language (free text per universal-amendment D27). Confirm preference before sending the install link.
  6. Plugin format probe — ${CLAUDE_PLUGIN_ROOT} semantics. Confirm that the Cowork plugin runtime sets ${CLAUDE_PLUGIN_ROOT} at session start; confirm that plugin-bundled SKILL.md files reference sibling files via ${CLAUDE_PLUGIN_ROOT}/skills/.../ (or whatever the plugin format mandates) and load correctly. Confirm that ${CLAUDE_PLUGIN_DATA} writable location exists or whether ${CLAUDE_PLUGIN_ROOT} IS writable. Verification task; resolve during build step 1.
  7. Shared connected folder across multiple Cowork Projects. Anthropic's docs neither permit nor prohibit two Cowork Projects pointing at the same BeCivic/ parent. The intended posture is permissive: two Projects → one parent → one customer-side state. What v1 needs to verify: (a) does Cowork warn the user when picking an already-used folder? (b) are concurrent writes from two open Cowork sessions handled cleanly, or do they race? Verification task; if (b) reveals a race condition, v1.x adds a write-lock file BeCivic/.be-civic/.lock.
  8. CLAUDE.md auto-load behaviour. Retired (2026-05-17 plugin-as-bootstrap amendment). The plugin delivery model supersedes the zip-plus-folder approach. The Cowork plugin runtime owns plugin-side CLAUDE.md loading; the single shared-root CLAUDE.md written by bc-onboarding into <user-picked-parent>/BeCivic/ is loaded by Cowork's ancestor-walking auto-load convention when the user opens any procedure subfolder under that BeCivic root. Replacement verification task (Phase 1B): confirm that the BeCivic-root CLAUDE.md written at folder-mount loads as Project Instructions on the next session opened from any procedure subfolder under the BeCivic root; confirm that Cowork does not overwrite plugin-supplied skill bundles with learned-preferences content between sessions.

5. Build sequence

  1. Probe Cowork plugin format, write access, tool shape, and directory picker. Manual: install Be Civic plugin from a hand-built local marketplace into operator's own Cowork. Verify ${CLAUDE_PLUGIN_ROOT} is set at session start. Verify mcp__cowork__request_cowork_directory exists. Probe write access under the picked parent. List the MCP tools the agent sees. Resolve open questions 1–6, 9. Document discovered behaviour in bc-operations/docs/cowork-plugin-format-notes.md.
  2. D1 migration for skill-graph columns. Add applies_to, tags, prerequisites, related, profile_requirements to the skills catalogue table. Backfill rows for the five v1 skills. ~45 min.
  3. Implement GET /api/skill-graph handler in bc-docs/api/handlers/skill-graph.ts. D1 query joining skill rows + the new metadata columns; filter args (applies_to, status, customer_locale). Cache-Control headers set. ~1 hour.
  4. Implement mcp__becivic__get_graph tool in bc-docs/mcp/tools/get-graph.ts. Thin wrapper around the api handler. ~30 min.
  5. Add bootstrap() MCP tool to mcp.becivic.be. One tool, returns three strings (boot-loader text, manifest URL, plugin install link). ~30 min.
  6. Revise harness CLAUDE.md template in skills/bc-onboarding/references/harness-CLAUDE.md for plugin-context (${CLAUDE_PLUGIN_ROOT}-aware paths; remove tier-system prose). Wire skill-graph fetch at session start. ~1 hour.
  7. Review nationality skill + 3 legalisation sub-skills for skill body discipline (§15.8) AND customer readability (jargon glosses, prose legislation refs) AND positioning copy (skills.md §15.8 invariant 11 — forward dependency). Promote nationality to beta; leave sub-skills at alpha. ~1 hour. 7a. Draft meta-no-skill-fallback meta-skill canonical per §24.8. Acknowledge-openly + Be-Civic-style + research-method guidance + sub-skill reuse + opt-in skill-draft submission. Status beta. Add corresponding D1 row with applies_to: meta. ~2 hours. 7b. Wire harness zero-match routing. In harness-CLAUDE.md, the get_graph response with zero applicable nodes routes to the meta-skill via the standard skill-tool mechanism. ~30 min.
  8. Build the six locale fallback HTML files. Author onboarding.en.html (port from current onboarding.html), then translate to FR, NL, DE, AR (RTL), UK per D44 / D35. Place at skills/bc-onboarding/references/onboarding.<locale>.html. ~3 hours.
  9. Write INSTRUCTIONS.md. Retired — plugin delivery eliminates the need for a separate Project Instructions template (2026-05-17 amendment). Step 9 is replaced by: verify that the shared-root CLAUDE.md written by bc-onboarding into <user-picked-parent>/BeCivic/ loads correctly as Project Instructions when the user opens any procedure subfolder under that BeCivic root in a new Cowork session. ~15 min.
  10. Write plugin README (operator-facing — install paths per §1.1, what ships in the plugin vs in the user folder, repo status). Already drafted at the live plugin path; revisit for accuracy after build step 1. ~15 min.
  11. Author alpha privacy artefacts. Author data/privacy-attachment.md (alpha-specific terms per D49). Author data/privacy-snippet.md (canonical privacy answer for meta-question shape per D47). Author data/mini-header-strings.md (8-10 rotating call-out variants per D13). ~2 hours.
  12. End-to-end dogfood in operator's Cowork: full session as if the operator were the tester, including a deliberate concern submission AND a get_graph call AND a read_skill --with_form call (or fallback HTML if the universal proposal hasn't landed yet). ~1 hour.
  13. Send to friend. Plugin install link, paste-prompt template for first contact.

Total: ~13–15 hours of focused work spread over 2–3 days. (Increase from prior estimates reflects the six locale fallback files in step 8, the alpha privacy artefacts in step 11, and the plugin-format probe in step 1.)

6. Success criteria for "send to friend"

The bundle is ready to send when:

  • Operator has personally completed acceptance criteria 1–10 in their own Cowork Project (full dogfood pass).
  • The plugin installs cleanly via the marketplace-by-GitHub-URL path.
  • The be-civic gate skill auto-activates on a Belgian-admin opener.
  • bc-onboarding runs the form, calls mcp__cowork__request_cowork_directory, mounts BeCivic/<project>/ correctly.
  • At least one concern submitted from operator's dogfood session is visible in MCP get_skill_observations output.
  • Plugin uninstall + rm -rf BeCivic/ produces a clean reset.
  • The plugin README is something the operator would feel comfortable showing a non-developer friend.

7. Failure modes to watch for in tester session

These are the realistic ways v1 fails. Watch for them in the tester's session and capture concerns.

  • Cowork directory-picker prompt confuses the tester. Customer doesn't know what to pick. Mitigation: harness opening framing names the prompt explicitly; the agent says "your OS will open a folder picker — pick anywhere you keep your important files, like Documents or Desktop." Suggest a default.
  • The skill misses a commune-specific document requirement. Some communes ask for documents others don't. Mitigation: nationality skill says explicitly "always verify with YOUR commune"; harness ends every guidance with that line; one skill concern is filed automatically.
  • The 5-year residence calculation differs from what the commune accepts. Civilian-leave gaps, study periods, EU vs non-EU rules. Mitigation: skill explicitly enumerates the known edge cases; harness asks about each in the eligibility section.
  • Language certificate format / source isn't accepted at the commune. A1, A2, B1 differences; CIRÉ vs ULB vs commune-organised tests. Mitigation: skill lists the accepted certificate sources per region.
  • Tester gives up because she doesn't trust the agent's outputs. Hardest to detect. Mitigation: every claim is sourced inline (Moniteur belge link, commune procedure page link); harness ends with "verify before filing"; harness offers to walk her through verification calls.
  • MCP unreachable during the form-render step. Mitigation: fallback to plugin-bundled locale HTML per §3.2, with explicit degraded-mode framing per G23.

8. Out of scope (do NOT build)

The following are explicitly deferred. If a sub-agent or contributor produces work in any of these areas during v1 development, treat it as scope creep and either move it to the post-v1 backlog or reject it.

  • Other procedures. Only nationality-application + 3 legalisation sub-skills + meta-no-skill-fallback ship. No mutuelle enrolment, no work permit, no commune address-change, no birth-registration.
  • Static skills-graph.json file. Removed from scope (revised 2026-05-11 PM). Skill graph is served live via MCP + HTTP endpoint pair.
  • Server-side profile_match filter evaluation. Advisory only at v1; client-side filtering. Server-side evaluation lands at v1.1+.
  • Analytics opt-in flow. Defer to v1.1. Concerns alone are sufficient for the v1 feedback loop.
  • Hook support (T4). No UserPromptSubmit-driven MEMORY.md injection, no PostToolUse analytics. Plugin ships hooks/ empty.
  • Vendor memory bridges. No Project Memory mirroring, no key-value workarounds. Read-only-advice degraded path uses chat-only output (no manual-paste / re-upload pattern, retired per 2026-05-17 amendment).
  • Multi-language harness output beyond skill canonicals. Harness output is in conversation_language (free text). Form HTML is in the matched V1 locale or EN fallback.
  • State-machine status checks in the harness. With one procedure pinned at beta, no runtime gating needed.
  • Document-content extraction. v1 does not extract routing fields from customer-uploaded documents. The harness asks for routing fields directly. Document-content-discard rule is STATED to the customer in the contract framing; enforcement by absence-of-extraction.
  • Multi-customer isolation logic. v1 is one customer per BeCivic/ directory.
  • Cross-machine sync. No git mirror, no cloud sync, no portability tooling.
  • D1 staging dashboard for the operator. v1 reads concerns directly via MCP get_skill_observations or by querying D1 with the existing tooling.
  • Granular per-stream consent controls. Post-alpha only (per §3.6 / §3.8). Alpha is bundled-consent-or-decline.

9. After v1 ships (post-tester)

If v1 acceptance criteria are met by the friend tester:

  • File an internal retro from her concerns + session transcript.
  • Promote nationality-application skill from beta to stable after one more cycle (or after first independent corroboration).
  • Open v1.1 scope: second procedure (likely commune address-change, given residence-history dependency overlap with nationality).
  • Re-open the deferred analytics opt-in question (§18 architecture.md) only if a consent-fatigue signal or specific request emerges.
  • Plan post-alpha consent posture (granular per-stream controls; retire consent.alpha_bundle).

If acceptance criteria are not met, file failures by acceptance-criterion number and decide per-criterion whether to revise scope or revise the architecture.

Cross-references