--- title: Be Civic — Architecture type: spec status: v0.5.1 — post-2026-05-12 tag-resolution reconciliation date: 2026-05-12 parent_spec: ./README.md sibling_specs: - protocol.md - schemas.md - privacy.md - lifecycle.md - skills.md - website.md tags: ["be-civic", "bc-internal", "architecture"] --- # Be Civic — Architecture This sub-spec covers the non-negotiable principles (§3), the architecture overview with its system diagram and storage split (§4), the repository layout (§5), the reference consumer (§13) and its agent interface split (§13.1), the anti-patterns catalogue (§16), settled decisions (§17), open questions (§18), out-of-scope items (§19), and the full consumer-side runtime specification (§24). For all JSON schemas (skill frontmatter, submission schemas, MDX tags), see `schemas.md`. For the PII pipeline and consumer-side state contract, see `privacy.md`. For the state machine and branch policy, see `lifecycle.md`. For the website rendering substrate, see `website.md`. For the trust model, citation rot, provider protocol, and MCP server, see `protocol.md`. ## 3. Non-negotiable principles 1. **Every factual claim cites a source.** Articles of the Code, Moniteur belge refs, INAMI / SPF / commune URLs. No uncited assertions in any skill body. 2. **Freshness is explicit.** Each skill carries `last_verified` and `verification_notes` in frontmatter. Staleness handling lives in agent guidelines, not in the skill itself: agents treat skills past a recency threshold as suspect and re-fetch volatile values from cited sources. 3. **Skills scaffold, they don't replace iteration.** Skills surface statutory hooks, document hierarchy, and known pitfalls. They do not enumerate every commune or interpret edge cases. When in doubt, prefer fewer branches and clearer escalation prompts. 4. **No PII in any contribution.** PII protection is structural, not promissory. Schema-level bans on identity-shaped fields, hard length caps on free-text, salted hashed per-IP correlation only (daily-rotating salt for rate limits; per-artefact salt scoped to an artefact's `alpha`/`beta` lifetime for self-validation prevention and distinct-IP counting), no request-body logging anywhere, consumer pre-flight scrub (regex from canonical rules + LLM contextual judgment), Worker hard-gate scrub on submit, NER on commit. NER detection holds the submission in a human-review queue (the same queue handling injection-flag quarantines); it is not auto-revert. PII never reaches the public corpus without human eyes when NER flags. See §8.5 (see privacy.md). 5. **Disclaim authority.** Skills are starter scaffolds. Users must verify with their commune. This statement appears at the top of every skill and in the README. 6. **Substrate-agnostic data shapes.** The skill format and submission protocol are specified independently of any specific agent runtime or backend. GitHub + Cloudflare Workers + D1 are the v1 substrate; the data shapes and protocol do not assume them. 6b. **Everything open, no closed parts** (corpus side). The user's LLM is the runtime; it needs to read the design (composition graph, scrub mechanism, state machine, validation protocol) to use the system correctly. There is no "closed core" to protect because the architecture *requires* transparency to function. All source, docs, schemas, prompts, and design narratives are public under CC-BY-4.0. (The provider-integration protocol layer described in §21 (see protocol.md) is an additive commercial layer; its provider contracts and partner-relationship details are not part of the public corpus, but the eligibility criteria and the editorial-firewall principles are.) 7. **Rejected submissions never leave private state.** Submissions go to the Worker, not to a public surface like a GitHub Issue. On rejection at the Worker (regex / schema fail / capability mismatch / self-validation) the submission is returned as 4xx with a category-only error and never persists anywhere. On NER detection at commit, the submission is held in the review queue rather than auto-published; if the maintainer discards it, the public corpus is unaffected. 8. **Platform-agnostic by capability declaration.** Be Civic tests capability declarations, not vendor behaviour. The four capability tiers per submission type (§6.7 (see schemas.md)) are vendor-neutral. Agents self-classify against those declarations, disclose limitations to the user, and recommend stepping up to a capable mode in their own ecosystem if they fall below tier (Anthropic → Claude Code; OpenAI → ChatGPT with code interpreter / Codex CLI; Google → Gemini code execution; Mistral → Le Chat with Canvas; etc.). Be Civic does not maintain per-vendor adapters; it maintains a recommendations page that consumers' agents can amend as ecosystems shift. (Per D.1 redirect, 2026-04-27.) 9. **Designed for generic users, not the maintainer.** Skills are written for a generic third-country expat using a generic capable agent. The maintainer is a test user, not the target. Skills do not embed maintainer-specific context, examples, or assumptions. Agent capability is declared explicitly per skill (see §6.1, §6.7) so consumers can determine compatibility. 10. **Honor-system contribution, made visible — and opting out is first-class.** There is no enforcement mechanism for submission. The system's data quality depends on consumer agents honestly submitting validated content. The README documents this explicitly; auto-generated activity dashboards (per-skill at point of use, plus global) make the honor system tangible. The submission contract requires only a one-off framed message at the user's first session — explaining that submissions are anonymous, automatic, and reviewable in the local submissions log, with cancellation available within the 24-hour staging window. No per-event consent prompts; the burden is on the consuming agent to scrub correctly and on the user to set the policy once. **Opt-out is first-class.** Users who decline to submit (privacy-sensitive populations, refugees, anyone who simply doesn't want to) must receive the same skill-loading and guidance experience as users who do submit. The system must be useful for users who never submit a single submission. The honor-system framing is about aggregate data quality across the user population, not about pressuring individual users. Consumer agents that degrade UX for non-submitters are non-compliant. 11. **Customer-side state only.** Be Civic operates with no server-side per-customer state of any kind; customer-side state MUST satisfy all three of the following clauses: the customer can read it directly with standard tools, the customer can delete it unilaterally as a single artifact, and the agent can both read and write it in-session (§8.7 (see privacy.md)). This is a Tier C invariant: changing it requires explicit protocol amendment. 12. **Deterministic submission paths for code-driven signals.** LLM-driven submission is permitted only where semantic judgment is required (for example, drafting an observation `gap_description`). Submission of session-lifecycle events, hook-fired analytics, and structured catalogue updates MUST follow deterministic code paths with no LLM in the submission loop (§24; see also §6.2.1 (see schemas.md) and §6.2.5 (see schemas.md) on the analytics endpoint). 13. **Plugin-as-bootstrap.** The primary consumer-side delivery mechanism is a Cowork plugin distributed via `becivic.be`. Installing the plugin is the canonical onboarding path; it delivers the harness skill, all shipped procedure skills (or — under the live-fetch architecture — the meta-skills that fetch procedures), starter state files, and schemas in a single install gesture — no zip download, no folder selection at install time, no manual paste of Project Instructions. The plugin runtime exposes the plugin install root as `${CLAUDE_PLUGIN_ROOT}` and a writable plugin-data location as `${CLAUDE_PLUGIN_DATA}`; the harness uses these for read-only plugin assets and plugin-internal state respectively. User-picked-parent `BeCivic/` folders for per-procedure state are created later by `bc-onboarding` via `mcp__cowork__request_cowork_directory` after form submit (see cowork-plugin.md §3.3). Paste-prompt sampler and free-tier read-only paths are retained as degraded fallbacks and explicitly not the recommended entry point. Cowork-specific instantiation lives in [`cowork-plugin.md`](cowork-plugin.md); sibling harnesses (ChatGPT app, etc.) will get their own harness specs. 14. **Be Civic is a tool for the user's agent, not an agent itself.** The user installs the plugin; the user's own agent loads it; Be Civic gives that agent the verified Belgian procedures. The user never speaks to "Be Civic" — they speak to their agent, which uses Be Civic. Customer-facing copy MUST reflect this framing: "your agent will use Be Civic to walk through your apostille" is correct; "Be Civic will help you with your apostille" is not. Trust posture depends on this: the user's data stays in their agent's environment, the agent is the one running the procedure, and Be Civic is the verified-content layer the agent reads. This is consistent with principle 11 (customer-side state only) and principle 13 (plugin-as-bootstrap). ## 4. Architecture overview ``` ┌─────────────────────────────────────────┐ │ Consumer-side (personal agent + user) │ │ ─ self-classifies against capability │ │ requirements (§6.7); recommends │ │ stepping up if below tier │ │ ─ reads relevant skill from becivic.be │ │ (one canonical.md per skill at its │ │ current status: alpha/beta/stable) │ │ ─ applies user context │ │ ─ guides user through admin task │ │ ─ runs pre-flight validation locally │ │ (regex scrub + cross-ref script) │ │ ─ POSTs one of five feedback types │ │ + analytics + rating (post-2026-05-15):│ │ concern | amendment | validation | │ │ draft | feedback | rating | analytics │ │ ─ logs cancel_token + cancel_url to │ │ submissions.jsonl │ │ ─ NO GitHub account needed │ └────────────────┬────────────────────────┘ │ HTTPS POST / ▼ ┌─────────────────────────────────────────┐ │ Staging service @ becivic.be │ │ (Cloudflare Worker (api/) + cron │ │ Worker (tools/staging-worker/)) │ │ ─ seven endpoints (/concerns, │ │ /amendments, /drafts, /validations, │ │ /feedback-channel, /ratings, │ │ /analytics) + /feedback envelope │ │ ─ target_type-keyed schema validation │ │ ─ capability self-declaration check │ │ ─ regex scrub (canonical rules) │ │ ─ identity-field ban (defensive) │ │ ─ self-validation prevention │ │ (validations only) │ │ ─ rate limits per IP per day │ │ ─ stage in KV with 24h TTL │ │ (concerns/amendments/drafts/ │ │ feedback/ratings); validations │ │ apply immediately │ │ ─ cohort_anchor Worker-stamp on │ │ target_type ∈ {skill, path} rows │ │ ─ return cancel_token │ │ ─ scheduled commit Worker (5–15 min) │ │ ─ catalogue/concern/validation/ │ │ rating writes go to D1; draft and │ │ amendment (target_type=skill | path) │ │ open PRs on GitHub via GitHub App │ │ credentials │ └────────────────┬────────────────────────┘ │ D1 INSERT │ + GitHub App API: open PR / │ auto-merge on CI green ▼ ┌─────────────────────────────────────────┐ │ Repository state (canonical, on main) │ │ skills//canonical.md (one file │ │ per skill; status in frontmatter: │ │ draft | alpha | beta | stable) │ │ skills//process.mmd │ │ data/communes.json │ │ data-snapshot/ (D1 backup snapshots; │ │ fallback for build-time tag │ │ resolution; see §6.10) │ │ docs/submission-contract-v.mdx │ │ index.mdx (landing; see §20.2) │ │ agents.mdx (agent entry; see §13.1) │ └────────────────┬────────────────────────┘ │ on commits: ▼ ┌─────────────────────────────────────────┐ │ D1 (catalogues + signals) │ │ volatile_values (val-NNNNN entries) │ │ references (ref-NNNNN entries) │ │ concerns (con-NNNNN entries; │ │ target_type keyed) │ │ amendments (amd-NNNNN entries; │ │ target_type keyed) │ │ drafts (drf-NNNNN entries; │ │ target_type keyed) │ │ validations (val_*; target_type │ │ keyed, 6 values) │ │ feedback_channel (fbk-NNNNN entries; │ │ operator triage) │ │ ratings (rtg-NNNNN entries; │ │ three-axis stars) │ │ analytics_events (anl-NNNNN entries; │ │ aggregate-only) │ │ ─ INSERT-with-supersede update model │ │ (current row WHERE superseded_at IS │ │ NULL); full history queryable │ │ ─ daily JSONL backup snapshot to Git │ │ under data-snapshot/ (archival only) │ └────────────────┬────────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ Commit-side checks │ │ ─ Presidio NER on submitted prose │ │ fields (notes, rationale, │ │ injection_reason) │ │ → on flag: hold in review queue │ │ (§8.5) │ │ ─ Cross-ref validator (§10.1) │ │ ─ State machine (§9): │ │ automated promotion of skills │ │ and paths (frontmatter status edits │ │ via PR) and catalogue rows (D1 │ │ UPDATE); threshold-driven, no human │ │ in loop except on draft PRs │ │ (target_type=skill | path) │ └────────────────┬────────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ Apex router (site/router-worker.js) │ │ ─ owns becivic.be/ apex routing │ │ ─ /api/* → staging Worker (above) │ │ ─ everything else → renderer (below) │ └────────────────┬────────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ Renderer Worker (§20) │ │ Cloudflare Workers Static Assets + │ │ custom Worker entry (worker.ts); │ │ serves everything that isn't /api/*: │ │ ─ becivic.be/ (marketing landing) │ │ ─ becivic.be/agents (agent entry, │ │ ~40 lines + manifest.json + per- │ │ endpoint pages; §13.1) │ │ ─ becivic.be/skills/ │ │ one canonical URL per skill; │ │ status banner in frontmatter when │ │ not stable │ │ ─ becivic.be/docs/* │ │ ─ llms.txt + llms-full.txt │ │ ─ , , MDX │ │ components resolved at build/fetch │ │ time (build-fetch from /api/*; │ │ fallback to data-snapshot/) — §20.3 │ │ ─ runtime hooks: HTMLRewriter beacon, │ │ _redirects dedup (§20.3) │ └────────────────┬────────────────────────┘ │ in parallel: ▼ ┌─────────────────────────────────────────┐ │ MCP Worker (§23) │ │ mcp.becivic.be — stateless; │ │ createMcpHandler; ~6 intent tools │ │ wrapping becivic.be/api/* │ └────────────────┬────────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ Maintainer review queue │ │ (constitutional court only) │ │ ─ injection-flag quarantines │ │ ─ NER-held submissions │ │ ─ skill_draft PR review (S31) │ │ ─ provider-eligibility decisions (§21) │ │ ─ protocol amendments │ │ ─ NOT routine corpus growth │ └─────────────────────────────────────────┘ ``` **Storage split.** Authored prose lives in Git; records live in D1. | Item | Where | Reasoning | |------|-------|-----------| | Skills (`canonical.md`, `process.mmd`) | Git | Authored prose; renderer Worker emits HTML; agents read via URL or content-negotiated `.md` | | Volatile values | D1 | Records, indexation churn | | References | D1 | Records, citation maintenance | | Observations | D1 | High volume, body is short, vote-rankable | | Validations | D1 | Pure records, very high volume; `target_type` keyed across skills/VVs/refs/observations | | Votes (on concerns) | D1 | Subset of validations, `target_type='observation'` (the wire surface name preserved per the locked OPEN-13 wire-vs-render split; the underlying D1 lookup goes to the `concerns` table) | A daily backup routine dumps every D1 table to JSONL under `data-snapshot/` in Git for archival. The renderer build does not depend on the dump in the steady-state path; it is the fallback for build/fetch-time MDX-tag resolution when `/api/*` is unreachable from the build environment (§6.10 (see schemas.md), §20.3 (see website.md)). **Unified status state machine.** Every skill, volatile value, and reference advances through a single status enum (§9 (see lifecycle.md)). The skill's `status` lives in `canonical.md` frontmatter; the catalogue row's `status` lives in D1. | Status | Meaning | Path | |--------|---------|------| | `draft` | Skeleton or in 24h staging | not yet committed (or skeleton frontmatter) | | `alpha` | Committed, awaiting initial validations | `canonical.md` (skills) or current row in D1 (catalogues) | | `beta` | Promoted from alpha; broader validation underway | same path; status advanced | | `stable` | Consensus-promoted; the canonical version | served as default at `/skills/` (skills); current row WHERE superseded_at IS NULL (catalogues) | | `quarantined` | Pulled for cause; not rendered; audit-only | maintainer-action terminal state; body replaced with quarantine notice | | `deprecated` | Superseded or no longer recommended; may remain readable with `superseded_by` | maintainer-action terminal state | Rollback works by D1 supersession (catalogue rows: a fresh INSERT marks the prior row's `superseded_at`) or by `git revert` (skill bodies). The `status` enum includes `quarantined` (pulled for cause, not rendered, audit-only) and `deprecated` (superseded, may remain readable with `superseded_by`) as maintainer-action terminal states alongside the four promotion states (`draft | alpha | beta | stable`). History remains queryable via the history endpoints (§9 (see lifecycle.md), §28 design conversation S36). **Trust shape**: a skill at `stable` status is citation-grade and consensus-validated; a skill at `alpha` or `beta` is visible to consumers but flagged with a banner; observations are signal, not edits, and are surfaced inline via the `` MDX component on each skill page (§6.10 (see schemas.md)). **Editorial neutrality, commercial sustainability.** Be Civic is operated by an independent company. The corpus is published under CC-BY-4.0 (§17). The provider-integration protocol layer (§21 (see protocol.md)) is a v1.x ambition that funds the work; eight editorial-firewall principles (§21 (see protocol.md)) keep the corpus partner-blind, prevent ranking or recommendation, and make removal-for-cause contractually mandatory and revenue-blind. Skills cite authoritative sources and describe processes; they never name specific providers in the body. **Human / agent entry split.** `becivic.be/` is human-facing landing and explanatory copy. `becivic.be/agents` is the agent entry — overview (~40 lines after S52 implementation), with protocol details in `/agents/manifest.json` and per-endpoint pages at `/agents/submit/*` (§13.1). Both are served by the same renderer Worker per §20 (see website.md). Three reinforcing channels (llms.txt nav order, MCP tool surfacing, per-skill body line) direct agents to `/agents` first. (Per G.12.) ## 5. Repository layout ``` be-civic/ # public repo (github.com/Be-Civic/be-civic); the corpus ├── README.md # what the repo is, who it's for, how skills are loaded ├── CONTRIBUTING.md # rules for human and agent contributors ├── LICENSE # CC-BY-4.0 — applies to everything (code AND content); see §17 ├── index.mdx # marketing landing rendered at becivic.be/; see §20 ├── agents.mdx # agent entry overview (becivic.be/agents); ~40 lines after S52 — see §13.1 ├── docs.json # navigation source-of-truth read by the renderer; auto-regenerated on commits (state-machine validator) ├── site/ # bc-infra-side renderer source (multi-site monorepo: core/ + sites//); deployed as Cloudflare Workers Static Assets; see §20 and §22 ├── api/ # Cloudflare Worker source for the four POST endpoints under /api/* (observations, skill-amendments, skill-drafts, validations); see §10 and §17 │ ├── index.html │ ├── style.css │ ├── fonts/ # self-hosted Manrope woff2 (700, 800), latin subset │ ├── logo/ # light.svg, dark.svg │ └── favicon.svg ├── docs/ │ ├── submission-contract-v.mdx # versioned contribution contract (5 feedback types + analytics + rating) │ ├── skill-conventions.md # file structure, frontmatter, body layering │ ├── agent-guidelines.md # how consuming agents should behave │ ├── retraction-protocol.md # incident response for skills and submissions │ ├── reference-consumer.md # capability requirements + per-ecosystem capable-mode recommendations │ ├── activity/ # auto-generated, regenerated by activity-dashboard.yml │ │ ├── global.json │ │ └── global.md │ └── website/ # internal design brief for the human-facing surface (not served by the renderer; lives in bc-operations only); see §20 ├── schemas/ │ ├── skill.schema.json # JSON Schema for skill frontmatter │ ├── observation.schema.json # JSON Schema for observation submissions │ ├── skill-amendment.schema.json # JSON Schema for amendment submissions │ ├── skill-draft.schema.json # JSON Schema for draft submissions │ ├── validation.schema.json # JSON Schema for validation submissions │ ├── communes.schema.json # JSON Schema for the commune list │ ├── types.json # canonical I/O types │ ├── categories.json # category list (open enum, deterministic guards per G.3) │ ├── source-classes.json # primary / secondary / tertiary source allowlists (§15.2) │ └── regex-rules.schema.json # JSON Schema for tools/scrub/regex-rules.json ├── data/ │ └── communes.json # REFNIS-derived commune list (pinned nomenclature date) ├── data-snapshot/ # daily JSONL backup of D1 catalogues + signals; archival + build-time tag-resolution fallback (see §6.10) │ ├── volatile-values.jsonl │ ├── references.jsonl │ ├── observations.jsonl │ ├── validations.jsonl │ └── votes.jsonl ├── skills/ │ ├── index.json # auto-generated; activity stats per skill │ └── / # one folder per skill │ ├── canonical.md # the skill body; frontmatter status: draft | alpha | beta | stable │ ├── process.mmd # mermaid graph (required when branching) │ └── templates/ # optional sample documents │ │ # Volatile values, references, observations, validations, and votes │ # (on observations) are not files in this tree — they live in D1 and │ # are referenced from skill bodies via , , MDX │ # components (§6.10). Daily JSONL backup snapshots are written to │ # data-snapshot/ above for archival and as a build-time fallback. ├── tests/ │ └── fixtures/ │ ├── observations/{valid,invalid}/*.json │ ├── skill-amendments/{valid,invalid}/*.json │ ├── skill-drafts/{valid,invalid}/*.json │ ├── validations/{valid,invalid}/*.json │ ├── pii-samples/*.txt │ └── nrn-checksum/*.json ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── skill-error-report.yml # accuracy concerns; routes to maintainer │ │ └── bug-report.yml │ └── workflows/ │ ├── ner-on-commit.yml # Presidio NER on prose fields → review queue │ ├── state-machine.yml # scans validations/*, updates frontmatter, regenerates docs.json │ ├── skills-index.yml # regenerates skills/index.json │ ├── activity-dashboard.yml # regenerates docs/activity/global.{json,md} │ ├── communes-refresh.yml # quarterly REFNIS diff │ ├── citation-linkcheck.yml # monthly skill-citation URL check │ └── deploy-worker.yml # deploys tools/staging-worker/ on push ├── tools/ │ ├── scrub/ │ │ ├── regex-rules.json # canonical detector rules (Worker AND consumer) │ │ ├── presidio-recognizers/ # Belgian-specific NER recognisers (commit-side Action only) │ │ └── checksum/ # NRN, IBAN, BCE mod-97 implementations │ ├── staging-worker/ # v1 reference impl of staging service │ │ ├── worker.ts # POST /, DELETE //{id}, scheduled commit │ │ ├── wrangler.toml # Cloudflare Worker config; KV namespace bindings │ │ ├── setup.sh # one-shot: create KV namespace, configure secrets │ │ └── README.md # deployment instructions │ └── scripts/ │ ├── validate-cross-refs.ts # deterministic graph + frontmatter validator (§10.1, G.10) │ ├── audit-categories.ts # monthly orphan + edit-distance audit (G.3) │ ├── state-machine-tick.ts # called by state-machine.yml; advances proposal frontmatter │ └── regenerate-docs-json.ts # called by state-machine.yml; rebuilds renderer navigation ├── launch/ # internal launch / brand / channel planning artefacts (mockups, brand exploration); not served by the renderer (lives in bc-operations); see §20 └── runtime/ # gitignored — local Action-side state if needed └── ner-review-queue.log # appended by ner-on-commit Action when holding; audit only ``` Note: `meta-validate-skill-graph` is **not** a skill in v1. The graph validator is the deterministic script `tools/scripts/validate-cross-refs.ts` (per G.10) — invoked from three points: consumer pre-flight (via `tool_execution`), Worker hard-gate, and PR-CI on `main`. A markdown-wrapper skill would have added drift surface for zero LLM-judgment value. **One canonical.md per skill.** There is no `proposals/` directory and no `archive/` directory. A skill's lifecycle moves through `draft → alpha → beta → stable` via a single `status` field in `canonical.md` frontmatter (§6.1 (see schemas.md), §9 (see lifecycle.md)). Rollback of a skill body is a `git revert` against `canonical.md`; rollback of a catalogue entry (volatile value, reference) is a fresh INSERT in D1 that supersedes the prior row (§9 (see lifecycle.md), S29). History is preserved in both cases — Git for skill bodies, D1's `superseded_at` column for catalogue rows — and surfaced via the history endpoints (§9 (see lifecycle.md) / S36). **Self-rendered hosting.** The repository is consumed by the renderer Worker at `bc-infra/site/renderer/` (Cloudflare Workers Static Assets binding). The renderer serves `becivic.be/agents` from `agents.mdx`, `becivic.be/` from `index.mdx`, and one canonical URL per skill from `skills//canonical.md` — the `status` frontmatter field (§6.1 (see schemas.md)) drives an in-page banner when the skill is at `draft`, `alpha`, or `beta`. Custom MDX components (``, ``, ``) are resolved at build/fetch time, primarily by build-fetching from `/api/*`, with a fallback to `data-snapshot/` (§6.10 (see schemas.md), §20.3 (see website.md)). `llms.txt`, `llms-full.txt`, and content-negotiated markdown are emitted by the renderer build pipeline. The `/mcp` endpoint moves to the dedicated MCP Worker at `mcp.becivic.be` (§23 (see protocol.md)). **Agent-vs-human surface split.** Agents enter at `becivic.be/agents` (overview ~40 lines + machine-readable manifest + per-endpoint pages, per §13.1); humans enter at `becivic.be/` (marketing landing). Both are served by the same renderer Worker on the same apex; path-routed at Cloudflare via the apex router worker in `site/router-worker.js`. The `/api/*` namespace is reserved for the existing Cloudflare Worker staging service (per §4). MCP-protocol clients hit `mcp.becivic.be` directly. Architecture summary in §20 (see website.md); full design brief in `docs/website/requirements.md`. **File encoding and formatting conventions** (apply to every JSON, JSONL, YAML, and Markdown / MDX file in this repo): - UTF-8, no BOM - LF line endings only on committed files (enforced via `.gitattributes`); local user-data files (`sessions.jsonl`, `submissions.jsonl`, `feedback-buffer-*.jsonl`) MAY use the host's native line endings, and readers MUST tolerate either - JSON files end with a trailing newline after the closing `}`; JSONL files end with `\n` after the last line - Skill frontmatter is YAML 1.2 (strict mode — `yes/no/on/off` are strings, not booleans). Single document per file; no anchors or custom tags - Committed observation / validation files are pretty-printed JSON, 2-space indent, trailing newline. The Worker is the canonical writer - NIS5 codes in YAML MUST be quoted (`"21013"`, not `21013`) so they remain strings; CI rejects unquoted numeric NIS codes ## 13. Reference consumer: harness skill package The reference consumer ships as an agentskills.io-conformant skill package: a harness skill (`becivic`) plus one or more procedure skills (`becivic-`), installed at `~/.claude/skills/` and symlinked to `~/.agents/skills/` per §15.9 (see skills.md). The canonical deployment surface is Claude Desktop's Cowork tab with the Project's connected folder set to `~/.be-civic/`. The full specification of the consumer-side runtime is in §24; this section records what the reference consumer **is** and what it **is not**. **What it is.** A two-layer skill package that connects a customer to the Be Civic corpus. The harness owns conversation lifecycle, capability detection, profile routing, observation buffering, scrub, and submission. Procedure skills carry the regulation for one administrative process each. Together they implement the session lifecycle contract from `/agents/feedback-template`, the observation-buffer protocol, the validate-then-stage submission pattern, and the three-tier returning-user adaptation (§24.5). The harness is the one implementation surface every customer interacts with and is held to the full protocol conformance obligations in §15.7 (see skills.md). Procedure skills are governed by §15.1 (see skills.md) (drafting protocol) and §15.8 (see skills.md) (body discipline). **What it is not.** A per-vendor adapter project. The D.1 redirect from 2026-04-27 stands: Be Civic does not maintain Anthropic, OpenAI, Google, Mistral, or long-tail vendor adapters. The harness is agentskills.io-conformant so that it installs across any conformant client without vendor-specific adapter code. Capability-tier detection (§24.4) makes the harness gracefully degrade on clients that lack filesystem access or MCP tools without requiring a separate adapter for each. **What ships with v1.** - `becivic` harness skill (SKILL.md, `status: stable` from day one per §15 meta-skill seeding). - `nationality-application` procedure skill (canonical.md, targeting `beta` at v1 launch; see §14 (see README.md) Phase 4 deliverable 26). - `becivic.be/agents/bootstrap.zip`: the Cowork bootstrap zip per §24.6.1. - `mcp__becivic__get_skill_graph` MCP tool and `GET /api/skill-graph` HTTP endpoint serving the skills-graph from D1 per §24.2. - `becivic.be/agents` page rewritten per §24.6 to serve as both the T0 boot loader and the install hub for T2. - `GET /api/skills//observations` HTTP endpoint per §24.7. **Per-ecosystem capable-mode recommendations** (retained from the prior §13 framing as a narrower reference; the authoritative tier table is §24.4): - Anthropic: Claude Desktop Cowork tab (T2, canonical; T3 with `mcp.becivic.be` connected); Claude Code (T4, hooks confirmed); free-tier Chat with the Project installed (T1, degraded with upgrade prompt). - Any agentskills.io-conformant client: T1 or higher, depending on filesystem and MCP availability. - Paste-prompt sampler (T0): any AI that can fetch URLs (ChatGPT, Gemini, Le Chat, free-tier Claude Chat without the Project). There is no standalone reference-consumer codebase outside the skill package. The harness IS the reference consumer. ### 13.1 Agent interface: overview, manifest, per-endpoint split (S52) The `/agents` page is split into three layers, optimised for just-in-time context injection rather than a monolithic preamble: - **`/agents`**: overview (approximately 40 lines). What Be Civic is, where to read what, links to manifest and per-endpoint pages, and the T0 boot-loader behaviour per §24.6. Loaded by every agent on first contact. - **`/agents/manifest.json`**: machine-readable schema for capability tiers, endpoint signatures (including `GET /api/skill-graph` per §24.2 and `GET /api/skills//observations` per §24.7), session-start required fields, retry policy, scrub-rules location. Loaded once per session. - **`/agents/submit/{observation,amendment,draft,validation}`**: per-endpoint deep references holding full schema, examples, and error responses for that one submission type. Loaded only when the agent intends to submit that type. This shape replaces the round-6 monolithic `agents.mdx` (approximately 430 lines). The schema and architecture are recorded here as forward declaration; the implementation lives in the round-7 plan. See design-conversation S52. ## 16. Anti-patterns - Trying to enumerate every commune. Skill body says "verify with your commune"; submission accumulation captures variance. - Hardcoding deadlines or fees in skill prose without flagging them as `volatile_values`. - Bundling legal advice. Skills surface statutory text and document workflows; they do not interpret edge cases. - LLM at the Worker or in the commit-side NER Action. The receiving end is deterministic; LLM is consumer-side only. - Single-file shared JSON for observations / amendments / drafts / validations. Per-submission files only. - Embedded submission contract in skill bodies (drift, versioning ambiguity). - Trusting `submitting_agent` strings for rate-limiting or reputation. Worker uses hashed IP only. - Persisting submitter identity (plaintext IP, GitHub login, anything) to the public corpus. Anonymous by construction. - Retrying a failed Worker submission with a fresh id. Retries must reuse the original id so dedup works. - Submitting amendments without running pre-flight validation. The Worker will reject; the consumer wastes the user's turn. - Falsely flagging injection to DoS a proposal. Repeated false flags ban the IP permanently (per G.6). - Assuming NER auto-revert (it now goes to the human review queue per G.14 — submitters learn via status endpoint). - Self-validating one's own proposal. Worker rejects on IP-hash match (per G.7). - End-of-session bundled observations. Loses abandoned-session signal. Submit events as they happen. - Mixing origin processes into the main skill body. A US birth certificate is its own sub-skill. - Adding commune-specific sub-skills for minor variance. Sub-skills only when divergence is structural. - Vendor-coupled submission contracts. Contract instructions written to exploit a specific model's behaviour break compatibility across runtimes (per Principle 8 reframe). - Designed-for-maintainer skills. Maintainer-specific examples or assumptions creep in unnoticed. - Consumers without a submissions log. Even with the one-off start message, the user must be able to review what's been submitted. - Maintainer running individual-submission review in steady state. Maintainer is constitutional-court only — quarantines, NER holds, protocol amendments. The state machine handles the rest. ## 17. Settled decisions Reorganised by area for ease of reference. ### Round 6 (2026-05-03) - **2026-05-03: Round 6 architectural collapse.** Collapse proposal/canonical/archive to one canonical.md per skill; catalogues + signals (volatile values, references, observations, validations, votes) move to D1; build-time MDX-tag resolution (``, ``, ``); state machine automated from day 1 (with maintainer review only on skill_draft PRs); status enum unified to `draft | alpha | beta | stable`. Cite design-conversation-r6 S1–S36. - **2026-05-03: CC-BY-4.0 licence.** Replaces CC0 (rejected for EU jurisdiction incompatibility and sui generis database right friction); also rejects CC-BY-SA-4.0 (recursive share-alike semantics strain agent-excerpting and the anonymous-AI-contribution model). CC-BY-4.0 is the lowest-friction attribution licence for agent excerpting (a citation footer in chat satisfies attribution); compatible with EU jurisdiction; allows commercial use including by Be Civic; preserves brand/credit accumulation. Cite design-conversation-r6 S38. - **2026-05-03: Commercial posture.** Be Civic is operated by an independent company that publishes a public corpus under CC-BY-4.0 and operates a provider-integration protocol layer (§21 (see protocol.md)) as v1.x ambition. Eight editorial-firewall principles (§21 (see protocol.md)) keep skill content partner-blind, prevent ranking or recommendation, and make removal-for-cause contractually mandatory and revenue-blind. The protocol layer's v1.x scope stops at documentation and application forms completed inside the conversation (S45); full transaction completion is further-out ambition. Cite design-conversation-r6 S37–S49 and `docs/product-vision/press-release.md` v4 (commit 50c25d3). ### Round 7 (2026-05-04) - **2026-05-04: Self-rendered Cloudflare Worker site replaces Mintlify.** Renderer at `bc-infra/site/renderer/` ships markdown→HTML build pipeline, Pagefind static search, light/dark theme, mobile drawer, TOC active-tracking, runtime analytics-beacon injection via HTMLRewriter, and `_redirects` build-time deduplication. Cutover happens at the same time as this round-7 spec rebase lands. Bare apex `becivic.be` only; no `www`. Cite design-conversation-r6 S50. - **2026-05-04: End-state-first; v1/v1.x split collapsed.** The round-6 hedge that deferred D1 storage, MDX-tag resolution, and state-machine automation to v1.x is reversed. Every architectural component (D1, automated state machine, agent-interface compression, multi-site monorepo, MCP Worker, bc-docs privatisation) ships in v1, before walks resume. Volume only makes round-6 work harder; walks compound on a fixed substrate. Cite design-conversation-r6 S51. - **2026-05-04: Agent interface redesigned around just-in-time context.** The monolithic `/agents` page (~430 lines) splits into three layers: `/agents` ~40-line overview, `/agents/manifest.json` machine-readable schema, and per-endpoint pages at `/agents/submit/*`. Optimised for what an agent loads at decision time, not what a human reads end-to-end. Implementation deferred to round-7 implementation plan; spec §13.1 records the architecture. Cite design-conversation-r6 S52. - **2026-05-04: API-first; agents don't need GitHub; bc-docs target visibility = private.** All submission flows through `becivic.be/api/*`; agents never need GitHub credentials. Customer-facing pages stop directing humans to GitHub for contributions. bc-docs target end-state is private; corpus content remains CC-BY-4.0 regardless of repo visibility. Privatisation itself is implementation-phase work (post-cutover, after dependent links are cleaned up). Cite design-conversation-r6 S53. - **2026-05-04: MCP server at `mcp.becivic.be`; `createMcpHandler`; ~6 intent-oriented tools; API-primary.** Stateless Cloudflare Worker using `createMcpHandler` (sub-100 LOC; no Durable Objects). Tool surface ≈ 6 intent tools (e.g., find-skill, read-skill, search-corpus, submit-observation, validate, get-current-status), each routing to existing API endpoints. The public API at `becivic.be/api/*` is the primary surface; MCP is a thin overlay. Replaces the round-6 expectation of Mintlify-auto-generated `/mcp`. Cite design-conversation-r6 S54. - **2026-05-04: Multi-site monorepo `core/` + `sites//`; per-site `wrangler.toml`.** `bc-infra/site/core/` holds shared rendering primitives and Worker scaffolding; `bc-infra/site/sites//` holds per-site config, theme, content scope, and `wrangler.toml`. Site `becivic` is the default and only live site at launch. Future experimental verticals (e.g. tailored "buying a house" deployments) instantiate as new entries. Scaffold ships in v1 even without experiments; restructure is much cheaper at zero-tenant scale. Cite design-conversation-r6 S55. - **2026-05-04: Cloudflare cleanup; bare apex; remove `www` and Mintlify-era residue.** Target topology is `becivic.be` apex (renderer + API via `router-worker.js`) plus `mcp.becivic.be` subdomain (MCP Worker). Mintlify domain bindings retire with the cutover. `www.becivic.be` removed entirely; bare apex is canonical. Renderer staging on workers.dev subdomain remains as a CI artefact, not user-visible. Cite design-conversation-r6 S56. - **2026-05-04: Renderer specifics — HTMLRewriter beacon, `_redirects` dedup, MDX-tag resolution path.** (a) Cloudflare Web Analytics token held as a Worker Secret (not Plaintext, not in `wrangler.toml [vars]`); beacon `