From 9370348d54852cb3707680553fd251a1b78bf435 Mon Sep 17 00:00:00 2001 From: tegwick Date: Thu, 23 Jul 2026 21:56:07 +0200 Subject: [PATCH] Bootstrap repo: State Hub registration, agent docs, TEN-WP-0001/0002 statehub register + repo-seed template scaffold (CLAUDE.md, .claude/rules/, registry/). INTENT.md and SCOPE.md rewritten from the generated stub to match net-kingdom's ratified tenant-engine-boundary-contract_v0.1.md (Purpose, Responsibility Boundary, Non-Goals). topic_slug corrected from the auto-assigned custodian default to netkingdom, matching key-cape and user-engine. TEN-WP-0001 (bootstrap) complete: files reviewed/refined, stack decided (Python 3.12 + FastAPI, matching qonto-assistant's convention), first real workplan seeded. TEN-WP-0002 drafted: service skeleton, domain model (tenant/grouping/ capability-role/plan-grant), storage layer, and the three boundary-contract API surfaces (cache-read for key-cape, live-lookup for flex-auth with an explicit fail-closed requirement, write API behind a WriteAuthorizer seam since real flex-auth integration is a declared non-goal for this pass). Co-Authored-By: Claude Sonnet 5 --- .claude/rules/agents.md | 20 ++ .claude/rules/architecture.md | 25 +++ .claude/rules/credential-routing.md | 50 +++++ .claude/rules/first-session.md | 42 ++++ .claude/rules/repo-boundary.md | 18 ++ .claude/rules/repo-identity.md | 6 + .claude/rules/session-protocol.md | 92 +++++++++ .claude/rules/stack-and-commands.md | 29 +++ .claude/rules/workplan-convention.md | 55 +++++ .custodian-brief.md | 27 +++ .gitignore | 6 + .repo-classification.yaml | 26 +++ AGENTS.md | 189 ++++++++++++++++++ CLAUDE.md | 12 ++ INTENT.md | 131 ++++++++++++ SCOPE.md | 65 ++++++ registry/README.md | 12 ++ registry/capabilities/.gitkeep | 0 registry/indexes/capabilities.yaml | 4 + workplans/TEN-WP-0001-statehub-bootstrap.md | 79 ++++++++ .../TEN-WP-0002-domain-model-and-scaffold.md | 179 +++++++++++++++++ 21 files changed, 1067 insertions(+) create mode 100644 .claude/rules/agents.md create mode 100644 .claude/rules/architecture.md create mode 100644 .claude/rules/credential-routing.md create mode 100644 .claude/rules/first-session.md create mode 100644 .claude/rules/repo-boundary.md create mode 100644 .claude/rules/repo-identity.md create mode 100644 .claude/rules/session-protocol.md create mode 100644 .claude/rules/stack-and-commands.md create mode 100644 .claude/rules/workplan-convention.md create mode 100644 .custodian-brief.md create mode 100644 .repo-classification.yaml create mode 100644 AGENTS.md create mode 100644 CLAUDE.md create mode 100644 INTENT.md create mode 100644 SCOPE.md create mode 100644 registry/README.md create mode 100644 registry/capabilities/.gitkeep create mode 100644 registry/indexes/capabilities.yaml create mode 100644 workplans/TEN-WP-0001-statehub-bootstrap.md create mode 100644 workplans/TEN-WP-0002-domain-model-and-scaffold.md diff --git a/.claude/rules/agents.md b/.claude/rules/agents.md new file mode 100644 index 0000000..0e8a5d9 --- /dev/null +++ b/.claude/rules/agents.md @@ -0,0 +1,20 @@ +## Kaizen Agents + +Specialized agent personas available on demand via the state-hub MCP. + +**Discover:** `list_kaizen_agents()` — returns all agents with name, description, category +**Load:** `get_kaizen_agent("tdd-workflow")` — returns full instructions; read and follow them + +Common agents: + +| Agent | Category | When to use | +|-------|----------|-------------| +| `tdd-workflow` | testing | Step-by-step TDD8 workflow for any feature | +| `code-refactoring` | quality | Code quality analysis and safe refactoring | +| `test-maintenance` | testing | Diagnose and fix failing tests | +| `requirements-engineering` | process | Prevent interface/mock mismatches upfront | +| `keepaTodofile` | process | Maintain TODO.md during work | +| `project-management` | process | Track status, determine next steps | +| `datamodel-optimization` | quality | Optimize dataclasses and data structures | + +All 17 agents: call `list_kaizen_agents()` for the full list. diff --git a/.claude/rules/architecture.md b/.claude/rules/architecture.md new file mode 100644 index 0000000..793ee95 --- /dev/null +++ b/.claude/rules/architecture.md @@ -0,0 +1,25 @@ +## Architecture + +Small headless service, modeled on `qonto-assistant`'s layout (same fleet +convention). Layers: + +- `domain/` — tenant, grouping, capability-role, and plan-grant models; pure, + no framework dependency. +- `store/` — persistence for tenant records and the role/plan grant audit + trail. Starts in-memory/SQLite for early workplans; production backend TBD. +- `api/` — three surfaces per the boundary contract: a cache-read API + (`key-cape` calls at token issuance), a live-lookup API (`flex-auth` calls + synchronously for high-stakes decisions — must fail closed, never open), + and a write API (grant/revoke/plan mutations, authorization-gated by + `flex-auth`, not self-authorized). +- `guardrail/` — reserved namespace, not implemented yet (spend limits, + entity/action counts). + +Full ownership boundary and API contract: +`net-kingdom/canon/standards/tenant-engine-boundary-contract_v0.1.md`. +Claim/carrying mechanism this service implements: +`net-kingdom/canon/standards/iam-profile_v0.3.md` ("Tenant Roles" section). + +## Quick Reference + +`~/state-hub/mcp_server/TOOLS.md` — MCP tool reference diff --git a/.claude/rules/credential-routing.md b/.claude/rules/credential-routing.md new file mode 100644 index 0000000..bc920ab --- /dev/null +++ b/.claude/rules/credential-routing.md @@ -0,0 +1,50 @@ +# Credential and access routing + +**Audience:** Codex, Claude Code, Grok, and custodian agents that call **llm-connect** +for inference. Run this check **before** requesting secrets, API keys, SSH access, +login tokens, or database passwords — in any repo, not only `ops-warden`. + +ops-warden **issues SSH certificates only** (`warden sign`, `cert_command`). Every +other credential need belongs to another subsystem. **Do not** message +`ops-warden` on State Hub expecting a secret value; the reply is a pointer, not a key. + +### Lookup (do this first) + +```bash +warden route find "" --json +warden route show --json +``` + +Requires the `warden` CLI from `~/ops-warden` (`uv tool install .` or `uv run warden`). + +| Agent runtime | How to orient | +| --- | --- | +| **Codex / Grok** (shell, HTTP State Hub) | `warden route` commands above; inbox `to_agent=repo-seed` is for coordination, not secret vending | +| **Claude Code** (MCP when available) | `get_domain_summary("custodian")` for workplans; **still** use `warden route` for credential ownership | +| **llm-connect** (inference service) | Never put secret retrieval in prompts; route custody to OpenBao/operator paths surfaced by `warden route` | + +### Quick routing table + +| I need… | Owner | ops-warden executes? | +| --- | --- | --- | +| SSH cert (`adm`/`agt`/`atm`) | ops-warden | **Yes** — `warden sign` | +| API key, DB password, provider token | OpenBao (`railiance-platform`) | No — route only | +| Login / OIDC / MFA | key-cape / Keycloak | No — route only | +| Authorization decision | flex-auth | No — route only | +| activity-core → issue-core emission | activity-core + issue-core | No — `warden route show activity-core-issue-sink` | +| SSH tunnel | ops-bridge (+ `cert_command` from warden) | No — route only | + +### Anti-patterns (do not do these) + +- `POST /messages/` to `ops-warden` asking for `ISSUE_CORE_API_KEY`, `OPENROUTER_API_KEY`, etc. +- Inventing `warden secret`, `warden login`, `warden bao`, `warden tunnel` — they do not exist +- Pasting secrets into Git, State Hub, workplans, logs, or chat + +### Other capabilities (reuse-surface) + +Non-credential capabilities are usually discovered through **reuse-surface** federation +(`reuse-surface` registry / `capability.*` indexes). Credential routing is inlined in +every repo's agent instructions because it is high-frequency, high-risk, and easy to +get wrong. + +**Canon:** `~/ops-warden/wiki/CredentialRouting.md` · catalog `~/ops-warden/registry/routing/catalog.yaml` \ No newline at end of file diff --git a/.claude/rules/first-session.md b/.claude/rules/first-session.md new file mode 100644 index 0000000..a891fa0 --- /dev/null +++ b/.claude/rules/first-session.md @@ -0,0 +1,42 @@ +## First Session Protocol + +Triggered when `get_domain_summary("infotech")` shows **no workplans**. +The project is registered but work has not yet been structured. + +**Step 1 — Read, don't write** +- `~/the-custodian/canon/projects/infotech/project_charter_v0.1.md` — purpose, scope +- `~/the-custodian/canon/projects/infotech/roadmap_v0.1.md` — planned phases +- Scan repo root: README, directory structure, existing code or docs + +**Step 2 — Survey in-progress work** +Look for TODOs, open branches, half-finished files. Note done vs. started but incomplete. + +**Step 3 — Propose workplans to Bernd** +Propose 1–3 workplans — each a coherent strand, weeks to months, anchored to a +roadmap phase. **Wait for approval before creating.** + +**Step 4 — Write the workplan file; fix-consistency registers it (ADR-001)** +``` +workplans/REPO-WP-NNNN-.md ← write this, commit it +``` +Then register by running the consistency check — do **not** call +`create_workplan`/`create_task` yourself; manual registration duplicates what +C-06 creates from the file: +```bash +statehub fix-consistency --repo repo-seed +``` +C-06 creates the hub workplan + tasks and writes `state_hub_workstream_id` +(legacy frontmatter name — holds the workplan UUID) and `state_hub_task_id` +back into the file. + +**Step 5 — Record the setup** +``` +add_progress_event( + summary="First session: structured infotech into N workplans, M tasks", + event_type="milestone", + topic_id="cee7bedf-2b48-46ef-8601-006474f2ad7a", + detail={"workplans": [...], "tasks_created": M} +) +``` + + diff --git a/.claude/rules/repo-boundary.md b/.claude/rules/repo-boundary.md new file mode 100644 index 0000000..9fc27b6 --- /dev/null +++ b/.claude/rules/repo-boundary.md @@ -0,0 +1,18 @@ +## Repo boundary + +This repo owns **tenant-as-an-entity facts** only: tenant existence, +onboarding grouping, capability roles, plan/subscription assignment, and +(reserved) guardrail/quota policy. It does not own: + +- Identity, authentication, MFA, token issuance → `key-cape` / Keycloak +- Tenant identifier format and role vocabulary → NetKingdom canon + (`iam-profile_v0.3.md`, ADR-0013, ADR-0014) — this repo consumes it, + never redefines it +- Users, profiles, memberships → `user-engine` (shared key: `tenant_id` only) +- Authorization decisions → `flex-auth` (this repo is a data source it + queries, never the decision maker) +- Pricing-model / plan term definitions → `adaptive-pricing` +- Payment processing → not yet identified as owned by any repo +- Runtime secret custody → OpenBao / `secrets-engine` + +Full contract: `net-kingdom/canon/standards/tenant-engine-boundary-contract_v0.1.md`. diff --git a/.claude/rules/repo-identity.md b/.claude/rules/repo-identity.md new file mode 100644 index 0000000..9e2a4b1 --- /dev/null +++ b/.claude/rules/repo-identity.md @@ -0,0 +1,6 @@ +**Purpose:** Canonical owner of tenant-as-an-entity facts for NetKingdom: existence, onboarding grouping, capability roles, and plan/subscription assignment. + +**Domain:** infotech +**Repo slug:** tenant-engine +**Workplan prefix:** `TEN-WP-` +**Topic:** netkingdom diff --git a/.claude/rules/session-protocol.md b/.claude/rules/session-protocol.md new file mode 100644 index 0000000..e65d9d4 --- /dev/null +++ b/.claude/rules/session-protocol.md @@ -0,0 +1,92 @@ +## Session Protocol + +Dev Hub (State Hub API): http://127.0.0.1:8000 +MCP server name in `~/.claude.json`: `dev-hub` + +**Step 1 — Orient** + +Read the offline-safe brief first — it works without a live hub connection: +```bash +cat .custodian-brief.md +``` +Then call the MCP tool for richer cross-domain context when MCP tools are exposed: +``` +get_domain_summary("infotech") +``` +If MCP tools are unavailable in the current agent session, use the REST API: +```bash +curl -s "http://127.0.0.1:8000/state/summary" | python3 -m json.tool +``` +If the hub is offline: `cd ~/state-hub && make api` + +**Step 2 — Check inbox** +With MCP tools: +``` +get_messages(to_agent="repo-seed", unread_only=True) +``` +Mark read with `mark_message_read(message_id)`. Reply or act on coordination +requests before proceeding. + +Without MCP tools: +```bash +curl -s "http://127.0.0.1:8000/messages/?to_agent=repo-seed&unread_only=true" \ + | python3 -m json.tool +curl -s -X PATCH "http://127.0.0.1:8000/messages//read" \ + -H "Content-Type: application/json" -d '{}' +``` + +**Step 3 — Scan workplans** +```bash +ls workplans/ +``` +For each file with `status: ready`, `active`, or `blocked`, note pending +`wait`/`todo`/`progress` tasks. + +**Step 4 — Present brief** + +1. **Active workplans** for `infotech` — title, task counts, blocking decisions +2. **Pending tasks** from `workplans/` + any `[repo:repo-seed]` hub tasks +3. **Goal guidance** — if `goal_guidance` in summary: + - `needs_workplan`: surface as top action — *"Repo goal '{title}' has no workplan yet"* + - `alignment_warnings`: flag if active work is not aligned with current goal +4. **Suggested next action** — highest-priority open item +5. **SBOM status** — flag if `last_sbom_at` is unset for this repo + +If no workplans: follow First Session Protocol (`first-session.md`). + +**During work:** `record_decision()` · `add_progress_event()` · `resolve_decision()` + +> State Hub is a *read model*. **Never register workplans or tasks by hand** +> (`create_workplan`, `create_task`) — write the workplan file in `workplans/` +> and run `fix-consistency`; C-06 registers the workplan and tasks and writes +> IDs back into the file. Manual registration creates duplicates when +> fix-consistency runs. Work structure belongs in repo files (ADR-001). +> +> Legacy: `create_workstream` and `/workstreams/` remain as metered aliases — +> see `workplan-convention.md` (compatibility footnote). + +**Session close:** +With MCP tools: +``` +add_progress_event(summary="...", topic_id="cee7bedf-2b48-46ef-8601-006474f2ad7a", workplan_id="") +``` +Without MCP tools: +```bash +curl -s -X POST http://127.0.0.1:8000/progress/ \ + -H "Content-Type: application/json" \ + -d '{"topic_id":"cee7bedf-2b48-46ef-8601-006474f2ad7a","workplan_id":"","event_type":"note","summary":"what changed","author":"codex"}' +``` +If workplan files were modified, ensure the local copy is up to date first, +then sync from the repo checkout: +```bash +git pull --ff-only +statehub fix-consistency +``` +For repos where implementation runs on a remote machine (e.g. CoulombCore), +use the pull-before-fix mode from any shell with the State Hub CLI: +```bash +statehub fix-consistency --repo repo-seed --remote +``` +**C-15** (DB task ahead of file) is normal in multi-machine workflows — writeback +will sync the file to match DB. **C-16** (repo behind remote) blocks all writes +until you pull — intentional to prevent clobbering remote progress. diff --git a/.claude/rules/stack-and-commands.md b/.claude/rules/stack-and-commands.md new file mode 100644 index 0000000..ae47c37 --- /dev/null +++ b/.claude/rules/stack-and-commands.md @@ -0,0 +1,29 @@ +## Stack + +- **Language:** Python 3.12 (matches `qonto-assistant`'s fleet convention for + small headless services) +- **Framework:** FastAPI, `pyproject.toml` + `Makefile` layout +- **Key deps:** State Hub ADR-001 workplans, `net-kingdom` canon (IAM Profile + v0.3, ADR-0013, ADR-0014, tenant-engine boundary contract) + +## Dev Commands + +```bash +# Orient (offline-safe) +cat .custodian-brief.md +cat INTENT.md +cat SCOPE.md +ls workplans/ + +# Once scaffolded (TEN-WP-0002) +make install-dev +make test +make lint +make run + +# After workplan or registry edits — from this repo checkout +statehub fix-consistency + +# Sanity-check markdown / registry edits +git diff --check +``` diff --git a/.claude/rules/workplan-convention.md b/.claude/rules/workplan-convention.md new file mode 100644 index 0000000..4df2bc3 --- /dev/null +++ b/.claude/rules/workplan-convention.md @@ -0,0 +1,55 @@ +## Workplan Convention (ADR-001) + +File location: `workplans/REPO-WP-NNNN-.md` +ID prefix: `REPO-WP-` + +Work items originate as files in this repo **before** being registered in the hub. + +Canonical workplan frontmatter statuses are: +`proposed`, `ready`, `active`, `blocked`, `backlog`, `finished`, `archived`. +Use `proposed` for a newly drafted plan, `ready` after review against current +repo state, and `finished` when implementation is complete. `stalled` and +`needs_review` are derived health labels, not stored statuses. + +Closed workplans may be moved to `workplans/archived/` with a completion-date +prefix: `YYMMDD-REPO-WP-NNNN-.md`. The frontmatter id remains +unchanged; the prefix is only for quick visual reference. + +Small opportunistic tasks discovered during another session use **Ad Hoc Tasks**: +`workplans/ADHOC-YYYY-MM-DD.md`, workplan slug `adhoc-YYYY-MM-DD`, and task ids +`ADHOC-YYYY-MM-DD-T01`, `T02`, etc. Use adhocs only for low-risk work completed +directly. Promote anything requiring analysis, design, approval, dependencies, or +multiple planned phases into a normal workplan. + +Ecosystem todos from other agents arrive as `[repo:repo-seed]` hub tasks — +visible at session start. Pick one up by creating the workplan file, committing, +and running `statehub fix-consistency` — C-06 registers the workplan in the hub. +Never register by hand with `create_workplan` (legacy MCP alias: `create_workstream`). + +Task blocks use this shape: + +```task +id: REPO-WP-NNNN-T01 +status: wait | todo | progress | done | cancel +priority: high | medium | low +state_hub_task_id: "" # written by fix-consistency — do not edit +``` + +Status progression is `todo` → `progress` → `done`; use `wait` for waiting or +blocked work and `cancel` for stopped work. + +Workplan frontmatter carries `state_hub_workstream_id` — a legacy field name +kept for compatibility; it holds the hub workplan UUID and is written by +fix-consistency. Do not edit or rename it. + +### Legacy terminology (compatibility footnote) + +**Workplan** is the fleet term — see +`the-custodian/canon/standards/workplan-terminology-fleet_v0.1.md`. +**Workplan** is legacy only: some API routes (`/workstreams/`), params +(`workstream_id`), MCP aliases (`create_workstream`), and the frontmatter field +above remain until `STATE-WP-0069` retires them via legacy-meter. Treat those +identifiers as workplan IDs. Prefer `GET /workplans/` and `workplan_id` in new +examples and scripts. + + diff --git a/.custodian-brief.md b/.custodian-brief.md new file mode 100644 index 0000000..ff8b8d9 --- /dev/null +++ b/.custodian-brief.md @@ -0,0 +1,27 @@ + +# Custodian Brief - tenant-engine + +**Project:** tenant-engine +**Domain:** infotech +**State Hub:** http://127.0.0.1:8000 +**Topic ID:** `cee7bedf-2b48-46ef-8601-006474f2ad7a` + +## Open Workplans + +### Bootstrap State Hub integration + +Workplan file: `workplans/TEN-WP-0001-statehub-bootstrap.md` + +Open tasks: +- T01 - Review generated integration files +- T02 - Verify local developer workflow +- T03 - Seed first real workplan + +## Session Start + +1. Read `INTENT.md`, `SCOPE.md`, and `AGENTS.md`. +2. Check inbox: `GET /messages/?to_agent=tenant-engine&unread_only=true`. +3. Scan `workplans/`. +4. Update task statuses in workplan files as work progresses. + +Last generated: 2026-07-23 diff --git a/.gitignore b/.gitignore index ab3e8ce..73caa0b 100644 --- a/.gitignore +++ b/.gitignore @@ -162,3 +162,9 @@ cython_debug/ # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ + +# state-hub: track .claude/rules +# Claude Code local state (track shared rules; ignore machine-specific files) +.claude/* +!.claude/rules/ +!.claude/rules/*.md diff --git a/.repo-classification.yaml b/.repo-classification.yaml new file mode 100644 index 0000000..b0eb1ea --- /dev/null +++ b/.repo-classification.yaml @@ -0,0 +1,26 @@ +repo_classification: + standard: Repo Classification Standard + version: '1.0' + classified_at: '2026-07-23' + classified_by: agent + category: product + domain: infotech + secondary_domains: + - financials + capability_tags: + - identity + - access-control + - tenancy + - platform + - operations + business_stake: + - technology + - operations + - legal + - product + business_mechanics: + - control + - operation + notes: Canonical owner of tenant-as-an-entity facts for NetKingdom -- tenant + existence, onboarding grouping, capability roles, and plan/subscription + assignment. See net-kingdom canon/standards/tenant-engine-boundary-contract_v0.1.md. diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..a979563 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,189 @@ +# tenant-engine — Agent Instructions + +## Repo Identity + +**Purpose:** Canonical owner of tenant-as-an-entity facts for NetKingdom: existence, onboarding grouping, capability roles, and plan/subscription assignment. + +**Domain:** infotech +**Repo slug:** tenant-engine +**Topic ID:** `cee7bedf-2b48-46ef-8601-006474f2ad7a` +**Workplan prefix:** `TEN-WP-` + +--- + +## State Hub Integration + +The Custodian State Hub tracks work across all domains. Interact via HTTP REST — +there is no MCP server for Codex agents. + +| Context | URL | +|---------|-----| +| Local workstation | `http://127.0.0.1:8000` | +| Remote via tunnel | `http://127.0.0.1:18000` | +| Optional local edge relay | http://127.0.0.1:18080 | + +When an operator has enabled the edge relay, set API_BASE to the relay URL. +Queueable writes return an explicit queued receipt if the central hub is +unreachable. Treat that as pending local evidence, then ask the operator to run +statehub outbox status/replay after connectivity returns. + +### Orient at session start + +```bash +# Offline brief — works without hub connection +cat .custodian-brief.md + +# Active workplans for this domain +curl -s "http://127.0.0.1:8000/workplans/?topic_id=cee7bedf-2b48-46ef-8601-006474f2ad7a&status=active" \ + | python3 -m json.tool + +# Check inbox +curl -s "http://127.0.0.1:8000/messages/?to_agent=tenant-engine&unread_only=true" \ + | python3 -m json.tool +``` + +Mark a message read: +```bash +curl -s -X PATCH "http://127.0.0.1:8000/messages//read" \ + -H "Content-Type: application/json" -d '{}' +``` + +### Log progress (required at session close) + +```bash +curl -s -X POST http://127.0.0.1:8000/progress/ \ + -H "Content-Type: application/json" \ + -d '{ + "summary": "what was done", + "event_type": "note", + "author": "codex", + "workplan_id": "", + "task_id": "" + }' +``` + +Omit `workplan_id` / `task_id` when not applicable. + +### Update task status + +```bash +curl -s -X PATCH "http://127.0.0.1:8000/tasks/" \ + -H "Content-Type: application/json" \ + -d '{"status": "progress"}' +# values: wait | todo | progress | done | cancel +``` + +### Flag a task for human review + +```bash +curl -s -X PATCH "http://127.0.0.1:8000/tasks/" \ + -H "Content-Type: application/json" \ + -d '{"needs_human": true, "intervention_note": "reason"}' +``` + +--- + +## Session Protocol + +**Start:** +1. `cat .custodian-brief.md` — domain goal and open workplans (offline-safe) +2. Check inbox: `GET /messages/?to_agent=tenant-engine&unread_only=true`; mark read +3. Scan workplans: `ls workplans/` — note `status: ready`, `active`, or `blocked` files and open tasks +4. Check human-needed tasks: `GET /tasks/?needs_human=true` + +**During work:** +- Update task statuses in workplan files as tasks progress +- Record significant decisions via `POST /decisions/` + +**Close:** +1. Update workplan file task statuses to reflect progress +2. If finishing a workplan: hand off **residuals** as live work records first + (intake with `origin: residual` + `origin_ref: `, or a next workplan / + decision / engagement). Do not park leftovers only in prose or `SCOPE.md`. + Canon: `the-custodian/canon/standards/work-record-types_v0.1.md` § Residuals. +3. Log: `POST /progress/` with a summary of what changed (name handoff ids) +4. After workplan file changes, run: + ```bash + statehub fix-consistency + ``` + Coding agents should run this directly; ask the operator only if the CLI or + State Hub API is unavailable. This syncs task status from files into the hub DB. + +--- + +{CREDENTIAL_ROUTING} + + + + +--- + +## Workplan Convention (ADR-001) + +Work items originate as files in this repo — not in the hub. The hub is a +read/cache/index layer that rebuilds from files. + +**File location:** `workplans/TEN-WP-NNNN-.md` + +**Archived location:** finished workplans may move to +`workplans/archived/YYMMDD-TEN-WP-NNNN-.md`. The `YYMMDD` prefix is +the completion/archive date; the frontmatter `id` does not change. + +**Ad Hoc Tasks:** small opportunistic fixes discovered during a session use +`workplans/ADHOC-YYYY-MM-DD.md` with task ids `ADHOC-YYYY-MM-DD-T01`, etc. Use +this only for low-risk work completed directly; create a normal workplan for +anything needing analysis, design, approval, dependencies, or multiple phases. + +**Frontmatter:** + +```yaml +--- +id: TEN-WP-NNNN +type: workplan +title: "..." +domain: infotech +repo: tenant-engine +status: proposed | ready | active | blocked | backlog | finished | archived +owner: codex +topic_slug: ... +created: "YYYY-MM-DD" +updated: "YYYY-MM-DD" +state_hub_workstream_id: "" # fix-consistency — do not edit (legacy field name; workplan UUID) +--- +``` + +Use `proposed` for a new draft, `ready` after review against current repo +state, and `finished` after implementation. `stalled` and `needs_review` are +derived health labels, not frontmatter statuses. + +**Terminology:** workplan is the fleet term; `workstream` appears only in legacy +API/MCP/frontmatter bridges until `STATE-WP-0069` retires them — see +`the-custodian/canon/standards/workplan-terminology-fleet_v0.1.md`. + +**Task block format** (one per `##` section): + +``` +## Task Title + +` ` `task +id: TEN-WP-NNNN-T01 +status: wait | todo | progress | done | cancel +priority: high | medium | low +state_hub_task_id: "" # written by fix-consistency — do not edit +` ` ` + +Task description text. +``` + +Status progression: `todo` → `progress` → `done`; use `wait` for waiting/blocked work and `cancel` for stopped work. + +**Residuals when finishing:** actionable leftovers become live work records +before `status: finished` — usually an intake (`origin: residual`, +`origin_ref: TEN-WP-NNNN`) or a spawned workplan. Residual is a *role*, +not a kind. Fleet list lives on State Hub, not in `SCOPE.md`. + +To create a new workplan: +1. Write the file following the format above +2. Run `statehub fix-consistency` locally; ask the operator only if the CLI or + State Hub API is unavailable. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..a01c656 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,12 @@ +# Repo Seed — Claude Code Instructions + +@SCOPE.md +@.claude/rules/repo-identity.md +@.claude/rules/session-protocol.md +@.claude/rules/first-session.md +@.claude/rules/workplan-convention.md +@.claude/rules/stack-and-commands.md +@.claude/rules/architecture.md +@.claude/rules/repo-boundary.md +@.claude/rules/credential-routing.md +@.claude/rules/agents.md diff --git a/INTENT.md b/INTENT.md new file mode 100644 index 0000000..18f3ab6 --- /dev/null +++ b/INTENT.md @@ -0,0 +1,131 @@ +--- +repo: tenant-engine +updated: "2026-07-23" +--- + +# INTENT + +## Purpose + +`tenant-engine` exists to be the single, canonical source of truth for what a +**tenant** is on the NetKingdom platform — its existence, its onboarding +grouping, its capability roles, and its plan/subscription assignment — +separate from who its *users* are (`user-engine`), who *authenticates* it +(`key-cape`/Keycloak), who *authorizes* actions against it (`flex-auth`), and +what it *pays* for a given plan (`adaptive-pricing`). + +Full ownership boundary, formally: `net-kingdom/canon/standards/tenant-engine-boundary-contract_v0.1.md`. + +## Why This Exists + +No service in the fleet currently owns tenant-as-an-entity facts as a +queryable resource. `user-engine`'s own boundary contract explicitly scopes +it to *consuming* tenant identifiers and *storing records scoped by* a +tenant — not owning the tenant record itself. The IAM Profile defines the +*shape* of the `tenant` claim, not a live directory of which tenants exist, +what they're allowed to do, or what plan they're on. + +That gap became concrete during Binky Hedgehog GmbH's onboarding as the +platform's first tenant outside `tenant:coulomb` +(`key-cape/workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md`): a +tenant's **capability roles** (`PLTF`/`IAM`/`VEN`/`CUS`, non-exclusive — a +tenant can hold several at once, and its roles can change independently of +how it was onboarded) needed a real owner, a real carrying mechanism, and a +real governance model (role grants are usually a consequence of a paid +plan). See `net-kingdom/docs/adr/ADR-0013-tenant-onboarding-grouping-taxonomy.md` +and `ADR-0014-tenant-capability-roles-and-tenant-engine-ownership.md`. + +## Primary Utility + +`tenant-engine` provides: + +- **Tenant records**: existence, identifier, and onboarding grouping + (`trial`/`friendly`/`single`/`small`/`medium`/`large`/`enterprise`/ + `consumer`/`family`/`community`/`association`/`agentic`, per ADR-0013). +- **Capability role grants**: `PLTF`/`IAM`/`VEN`/`CUS`, with an audited + grant/revoke trail linking most grants to a plan-assignment change. +- **Plan/subscription assignment**: which `adaptive-pricing` plan a tenant + currently holds, referenced by id — never a duplicated copy of plan terms. +- **A cache-read API** `key-cape` calls at token-issuance time to source the + optional `tenant_roles` token claim (IAM Profile v0.3). +- **A live-lookup API** `flex-auth` calls synchronously for privileged, + high-stakes decisions — the case where a cached claim isn't trustworthy + enough (a stale `VEN` grant surviving a plan cancellation is not an + acceptable risk for money-movement or credential-vending actions). +- Reserved, not yet implemented: **guardrail/quota policy** (spend limits, + entity/action counts) — `trial`-grouped tenants default to a **zero** + spend budget; design is future work. + +## Intended Users + +- `key-cape` / Keycloak, at token issuance time (cache-read API). +- `flex-auth`, at authorization-decision time (live-lookup API, and the + target of write-authorization for grant/revoke/plan mutations). +- Operators and tenant administrators, indirectly, through whatever admin + surface eventually consumes `tenant-engine`'s APIs (not yet built). +- `adaptive-pricing`, as the source `tenant-engine` references for plan + *definitions* (not the other way around). + +## Strategic Role + +`tenant-engine` sits **beside** `user-engine`, not inside it — a deliberate +bounded-context split, not an oversight. `user-engine`'s aggregate root is +the *person*; `tenant-engine`'s is the *tenant*. Consumers that only need a +fast, security-critical tenant-role lookup (`flex-auth` on every privileged +decision, `key-cape` on every token issuance) should never need to depend on +`user-engine`'s much larger surface (registration flows, factor models, +family dataspace onboarding) to get it. This matches the fleet's existing +convention of many small, single-purpose `-engine`/`-core` services rather +than one large one. + +## Responsibility Boundary + +`tenant-engine` owns tenant-as-an-entity facts. It never re-derives or +duplicates facts another system already owns. + +### tenant-engine Owns + +- Tenant existence, identifier, and onboarding grouping. +- Capability role grants and their audit trail. +- Plan/subscription assignment (by reference, not by copying plan terms). +- Reserved: guardrail/quota policy, once designed. + +### Other Systems Own + +| Concern | Owner | +| --- | --- | +| OIDC discovery, login, token issuance | `key-cape`, Keycloak | +| Tenant identifier format and role vocabulary | NetKingdom (IAM Profile, ADR-0013, ADR-0014) | +| Users, profiles, memberships | `user-engine` | +| Authorization decisions | `flex-auth` | +| Pricing-model / plan definitions | `adaptive-pricing` | +| Runtime secrets | OpenBao / `secrets-engine` | +| Payment processing | Not yet identified — `tenant-engine` records plan assignment, not payment execution | + +## Non-Goals + +- Not an identity provider. Never issues, verifies, or refreshes tokens. +- Not a policy decision point. Never makes an authorization decision itself + — it answers queries `flex-auth` issues. +- Not a billing processor. Records plan assignment, not payment execution. +- Not a pricing-model engine. References `adaptive-pricing` plan ids. +- Not the user/membership domain. Never stores user accounts or profiles. + +## Governing Principle + +This repository should stay focused on the purpose above. Work that changes +its authority, ownership boundaries, or operational promises should be +captured in a workplan before implementation, and should stay consistent +with `net-kingdom/canon/standards/tenant-engine-boundary-contract_v0.1.md` — +if the two drift, the canon contract wins and this file should be corrected. + +## Related + +- `net-kingdom/canon/standards/tenant-engine-boundary-contract_v0.1.md` — the + formal ownership contract +- `net-kingdom/canon/standards/iam-profile_v0.3.md` — the `tenant_roles` + claim and carrying mechanism this service implements +- `net-kingdom/docs/adr/ADR-0013-tenant-onboarding-grouping-taxonomy.md` +- `net-kingdom/docs/adr/ADR-0014-tenant-capability-roles-and-tenant-engine-ownership.md` +- `key-cape/workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md` — the + first tenant this service will record diff --git a/SCOPE.md b/SCOPE.md new file mode 100644 index 0000000..15a7d1c --- /dev/null +++ b/SCOPE.md @@ -0,0 +1,65 @@ +# SCOPE + +> Lightweight boundary for agents and contributors. Full contract: +> `net-kingdom/canon/standards/tenant-engine-boundary-contract_v0.1.md`. + +--- + +## One-liner + +Canonical owner of tenant-as-an-entity facts for NetKingdom: existence, +onboarding grouping, capability roles, and plan/subscription assignment. + +--- + +## Core Idea + +`tenant-engine` answers "what is this tenant, what can it do, and what plan +is it on" for two consumers: `key-cape` (cached, at token issuance) and +`flex-auth` (live, for high-stakes decisions). It never stores users, +issues tokens, or makes authorization decisions. + +--- + +## In Scope + +- Tenant records: identifier, onboarding grouping (ADR-0013). +- Capability role grants/revocations: `PLTF`/`IAM`/`VEN`/`CUS` (ADR-0014), + audited, mostly plan-linked. +- Plan/subscription assignment, referenced by `adaptive-pricing` plan id. +- Cache-read API for `key-cape` (token-issuance-time `tenant_roles` claim + source). +- Live-lookup API for `flex-auth` (high-stakes, `aal2`-class decisions). +- Write API for grant/revoke/plan mutations, authorization-gated by + `flex-auth`. +- Reserved, not yet implemented: guardrail/quota policy (spend limits, + entity/action counts). + +## Out of Scope + +- Identity, authentication, MFA, token issuance — `key-cape`/Keycloak. +- Authorization decisions — `flex-auth` is the PDP; `tenant-engine` is a + data source it queries, never the other way around. +- Users, profiles, memberships — `user-engine`. `tenant_id` is the only key + shared between the two services. +- Pricing-model / plan term definitions — `adaptive-pricing`. +- Payment processing — not yet identified as owned by any repo. +- Runtime secret custody — OpenBao / `secrets-engine`. +- Irreversible operational decisions without human approval. + +## Current State + +- Status: bootstrapping. No implementation yet — see + `workplans/TEN-WP-0001-statehub-bootstrap.md` and the first real + implementation workplan it seeds. +- Ownership contract (`tenant-engine-boundary-contract_v0.1.md`) and the two + governing ADRs (0013, 0014) are ratified in `net-kingdom` and predate this + repo's code, by design. + +## Getting Oriented + +- Start with: `INTENT.md` +- Ownership contract: `net-kingdom/canon/standards/tenant-engine-boundary-contract_v0.1.md` +- Claim/carrying mechanism: `net-kingdom/canon/standards/iam-profile_v0.3.md` +- Agent instructions: `AGENTS.md`, `CLAUDE.md` +- Workplans: `workplans/` diff --git a/registry/README.md b/registry/README.md new file mode 100644 index 0000000..569abe9 --- /dev/null +++ b/registry/README.md @@ -0,0 +1,12 @@ +# Capability Registry + +Markdown-first capability index for federation and reuse planning. + +## Authoring + +1. Copy a capability entry template (see reuse-surface `templates/capability-entry.template.md`). +2. Add the row to `indexes/capabilities.yaml`. +3. Run `reuse-surface validate` from a checkout with the CLI installed. +4. Merge to `main` and verify publish with `reuse-surface establish --publish-check`. + +Federation contract: reuse-surface `docs/RegistryFederation.md`. diff --git a/registry/capabilities/.gitkeep b/registry/capabilities/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/registry/indexes/capabilities.yaml b/registry/indexes/capabilities.yaml new file mode 100644 index 0000000..7648bed --- /dev/null +++ b/registry/indexes/capabilities.yaml @@ -0,0 +1,4 @@ +version: 1 +updated: '2026-07-23' +domain: infotech +capabilities: [] diff --git a/workplans/TEN-WP-0001-statehub-bootstrap.md b/workplans/TEN-WP-0001-statehub-bootstrap.md new file mode 100644 index 0000000..82ffe72 --- /dev/null +++ b/workplans/TEN-WP-0001-statehub-bootstrap.md @@ -0,0 +1,79 @@ +--- +id: TEN-WP-0001 +type: workplan +title: "Bootstrap State Hub integration" +domain: infotech +repo: tenant-engine +status: finished +owner: codex +topic_slug: netkingdom +created: "2026-07-23" +updated: "2026-07-23" +--- + +# Bootstrap State Hub integration + +Canonical owner of tenant-as-an-entity facts for NetKingdom: existence, onboarding grouping, capability roles, and plan/subscription assignment. + +## Review Generated Integration Files + +```task +id: TEN-WP-0001-T01 +status: done +priority: high +``` + +Review `INTENT.md`, `SCOPE.md`, `AGENTS.md`, and `.custodian-brief.md`. +Replace generated placeholders with repo-specific facts where needed. + +**Done 2026-07-23:** `INTENT.md` and `SCOPE.md` rewritten from the generated +stub into the full Purpose/Responsibility-Boundary/Non-Goals shape matching +the ratified `net-kingdom/canon/standards/tenant-engine-boundary-contract_v0.1.md` +(Ownership Model + Source-of-Truth Matrix condensed into this repo's own +docs, not duplicated wholesale). `topic_slug` corrected from the +auto-assigned `custodian` default to `netkingdom`, matching `key-cape` and +`user-engine`'s convention. `.claude/rules/{repo-identity,architecture,repo-boundary,stack-and-commands}.md` +placeholders filled in (copied from `repo-seed`, the fleet's bootstrap +template). `AGENTS.md`/`.custodian-brief.md` left as generated — both carry +an explicit "state-hub template sync" marker meaning they're managed by +`fix-consistency`, not hand-edited. + +## Verify Local Developer Workflow + +```task +id: TEN-WP-0001-T02 +status: done +priority: high +``` + +Identify the repo's install, test, lint, build, and run commands. Add or refine +those commands in the agent instructions so future coding sessions can verify +changes confidently. + +**Done 2026-07-23:** Stack decided — Python 3.12 + FastAPI, matching +`qonto-assistant`'s fleet convention for small headless services (this repo +plays a structurally similar role: a narrow, security-adjacent API a couple +of other services call synchronously). `make install-dev`/`test`/`lint`/`run` +commands documented in `.claude/rules/stack-and-commands.md` ahead of the +actual `Makefile`/`pyproject.toml`, which land in `TEN-WP-0002`. + +## Seed First Real Workplan + +```task +id: TEN-WP-0001-T03 +status: done +priority: medium +``` + +Create the first implementation workplan for the repository's most important +next change. After workplan file updates, run the sync locally from this repo +checkout: + +```bash +statehub fix-consistency +``` + +**Done 2026-07-23:** `TEN-WP-0002-domain-model-and-scaffold.md` created — +service skeleton, domain model (tenant/grouping/role/plan-grant), and the +cache-read + live-lookup APIs per the boundary contract. Registered via +`statehub fix-consistency`. diff --git a/workplans/TEN-WP-0002-domain-model-and-scaffold.md b/workplans/TEN-WP-0002-domain-model-and-scaffold.md new file mode 100644 index 0000000..3c85e90 --- /dev/null +++ b/workplans/TEN-WP-0002-domain-model-and-scaffold.md @@ -0,0 +1,179 @@ +--- +id: TEN-WP-0002 +type: workplan +title: "Service skeleton, domain model, and the three boundary-contract APIs" +domain: infotech +repo: tenant-engine +status: ready +owner: codex +topic_slug: netkingdom +created: "2026-07-23" +updated: "2026-07-23" +--- + +# Service skeleton, domain model, and the three boundary-contract APIs + +First real implementation workplan. Builds the tenant-engine skeleton +against `net-kingdom/canon/standards/tenant-engine-boundary-contract_v0.1.md`: +tenant/grouping/capability-role/plan-grant domain model, an audited grant +trail, and the three API surfaces the contract defines — cache-read +(`key-cape`), live-lookup (`flex-auth`), and write (grant/revoke/plan +mutation). Guardrail/quota policy stays a reserved, unimplemented namespace +per ADR-0014 — not in scope here. + +**Depends on:** `net-kingdom` canon — `iam-profile_v0.3.md`, +`tenant-engine-boundary-contract_v0.1.md`, `ADR-0013`, `ADR-0014` (all +ratified). **Non-goals:** payment processing, pricing-model definitions +(`adaptive-pricing`'s job), real `flex-auth` integration (the write/live +APIs get a policy-hook interface, not a working `flex-auth` client — that's +a follow-up once `flex-auth` has a reachable endpoint), guardrail/quota +enforcement. + +## Task: Service skeleton + +```task +id: TEN-WP-0002-T01 +status: todo +priority: high +``` + +Python 3.12 + FastAPI, matching `qonto-assistant`'s layout convention: +`pyproject.toml`, `Makefile` (`install-dev`, `test`, `lint`, `run`), package +layout separating `domain/` (pure), `store/` (persistence), `api/` (FastAPI +routers), from day one — not refactored in later. + +Done when: `make test` runs an empty/smoke suite; `make run` starts a bare +FastAPI app with a `/health` endpoint. + +## Task: Domain model — tenant, grouping, capability role, plan grant + +```task +id: TEN-WP-0002-T02 +status: todo +priority: high +``` + +Pure domain types, no framework dependency: + +- `Tenant`: id, identifier (`tenant::` or reserved + `tenant:platform`/`tenant:coulomb`), grouping (ADR-0013 enum, nullable for + the two reserved identifiers). +- `CapabilityRole`: enum `PLTF`/`IAM`/`VEN`/`CUS`. +- `RoleGrant`: tenant_id, role, grant_reason (`plan_assignment` / + `manual_grant` / `platform_default`), plan_id (nullable), granted_by, + granted_at, revoked_at (nullable), correlation_id — the audited record + shape from the boundary contract's Tenant Role & Plan Grant Contract. +- `PlanAssignment`: tenant_id, plan_id (references an `adaptive-pricing` + plan id — stored as an opaque string, never resolved or duplicated + locally), assigned_at. + +Validation rules encoded as domain invariants, not just API-layer checks: + +- grouping must be one of ADR-0013's twelve values, or the tenant identifier + must be exactly `tenant:platform`/`tenant:coulomb` (grouping-less); +- a `trial`-grouped tenant may hold any role with `grant_reason: + platform_default` and no `plan_id` (ADR-0014) — this must be + representable, not blocked by a plan-required constraint; +- non-`trial` roles other than `platform_default` require a `plan_id` when + `grant_reason: plan_assignment`; +- revoking a grant sets `revoked_at`, never deletes the record (audit trail). + +Done when: unit tests cover valid/invalid grouping values, the trial/ +no-plan-required exception, and grant/revoke as append-only operations. + +## Task: Storage layer + +```task +id: TEN-WP-0002-T03 +status: todo +priority: high +``` + +In-memory store behind a `TenantStore` protocol/interface (mirrors +`qonto_client.QontoClientProtocol`'s pattern in `qonto-assistant`), so a +real backend can be swapped in later without touching `domain/` or `api/`. +Every mutation emits a domain event (tenant created, role granted, role +revoked, plan assigned) per the boundary contract's Audit Correlation +Contract — the event bus itself can be a simple in-process list for now. + +Done when: unit tests cover create/read/grant/revoke/assign-plan through the +store interface, plus event emission for each mutation. + +## Task: Cache-read API (for key-cape) + +```task +id: TEN-WP-0002-T04 +status: todo +priority: high +``` + +`GET /tenants/{tenant_id}/roles` — returns current, non-revoked capability +roles for a tenant. This is the endpoint `key-cape` calls at token-issuance +time to source the cached `tenant_roles` claim (IAM Profile v0.3). No +authorization gate of its own beyond service-to-service auth (out of scope +here — see Non-Goals); this endpoint's whole purpose is to be cheap and +fast, per the boundary contract's performance model. + +Done when: integration test hits the endpoint against the in-memory store +and returns the expected role set for a seeded tenant. + +## Task: Live-lookup API (for flex-auth) — fail closed + +```task +id: TEN-WP-0002-T05 +status: todo +priority: high +``` + +`GET /tenants/{tenant_id}/roles/live` — same data as the cache-read +endpoint, but explicitly documented and tested as the path `flex-auth` must +call before authorizing `aal2`-class actions. The distinction from T04 is +operational intent (freshness guarantee, called synchronously on a +privileged-decision path), not payload shape. + +Per the boundary contract's performance model: **fail closed, never open**. +If the store is unavailable, this endpoint must return an error response +that a policy caller would treat as "deny", not a default-allow or an empty +role list indistinguishable from "no roles granted". + +Done when: a test simulates store unavailability and asserts the endpoint +signals failure distinctly from "zero roles", not silently as 200 + `[]`. + +## Task: Write API — grant, revoke, assign-plan + +```task +id: TEN-WP-0002-T06 +status: todo +priority: medium +``` + +`POST /tenants` (create), `POST /tenants/{id}/roles/grant`, +`POST /tenants/{id}/roles/revoke`, `POST /tenants/{id}/plan` — all mutating +endpoints from the boundary contract's Write API. Per the contract, +`tenant-engine` does not self-authorize these writes; `flex-auth` is meant +to gate them. Since a reachable `flex-auth` integration is explicitly a +non-goal here, implement a `WriteAuthorizer` protocol/interface point (one +class, default-deny stub) so every mutation already flows through a single +seam — swapping in a real `flex-auth` client later touches one file, not +every endpoint. + +Done when: unit tests confirm every write endpoint calls the +`WriteAuthorizer` seam and is denied by the default-deny stub; a +test-only authorizer override proves the seam actually gates the mutation +when swapped. + +## Task: Closure review + +```task +id: TEN-WP-0002-T07 +status: todo +priority: low +``` + +Confirm T01–T06 done; run `make test`/`make lint`; verify the three API +surfaces match the boundary contract's Ownership Model and Source-of-Truth +Matrix with no drift. Note follow-ups: real `flex-auth` `WriteAuthorizer` +integration, `key-cape` wiring to actually call the cache-read endpoint at +issuance, guardrail/quota policy design (ADR-0014's reserved item), and +Binky Hedgehog GmbH as the first real tenant record once `key-cape`'s +`KEY-WP-0004` reaches that point. Run `statehub fix-consistency`.