From 08c87dead13d33a600acd60faa2a218cc8203e83 Mon Sep 17 00:00:00 2001 From: tegwick Date: Mon, 22 Jun 2026 21:44:32 +0200 Subject: [PATCH] Add State Hub bootstrap workplan and agent integration files Seed workplans/ with bootstrap workplan to satisfy ADR-001 C-01. Includes regenerated dev-hub session-protocol and agent instruction files. --- .claude/rules/agents.md | 20 + .claude/rules/architecture.md | 8 + .claude/rules/credential-routing.md | 50 ++ .claude/rules/first-session.md | 38 ++ .claude/rules/repo-boundary.md | 8 + .claude/rules/repo-identity.md | 5 + .claude/rules/session-protocol.md | 85 +++ .claude/rules/stack-and-commands.md | 19 + .claude/rules/workplan-convention.md | 40 ++ AGENTS.md | 488 ++++++++---------- CLAUDE.md | 70 +-- SCOPE.md | 168 +++--- tpsc.yaml | 9 + .../BINECT-WP-0001-statehub-bootstrap.md | 58 +++ 14 files changed, 660 insertions(+), 406 deletions(-) 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 tpsc.yaml create mode 100644 workplans/BINECT-WP-0001-statehub-bootstrap.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..7c2a645 --- /dev/null +++ b/.claude/rules/architecture.md @@ -0,0 +1,8 @@ +## Architecture + + + +## 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..b156c67 --- /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=binect-js` is for coordination, not secret vending | +| **Claude Code** (MCP when available) | `get_domain_summary("custodian")` for workstreams; **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..6a62d9e --- /dev/null +++ b/.claude/rules/first-session.md @@ -0,0 +1,38 @@ +## First Session Protocol + +Triggered when `get_domain_summary("communication")` shows **no workstreams**. +The project is registered but work has not yet been structured. + +**Step 1 — Read, don't write** +- `~/the-custodian/canon/projects/communication/project_charter_v0.1.md` — purpose, scope +- `~/the-custodian/canon/projects/communication/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 workstreams to Bernd** +Propose 1–3 workstreams — each a coherent strand, weeks to months, anchored to a +roadmap phase. **Wait for approval before creating.** + +**Step 4 — Create workplan file first, then DB record (ADR-001)** +``` +workplans/BINECT-WP-NNNN-.md ← write this first +``` +Then register in the hub: +``` +create_workstream(topic_id="36c7421b-c537-4723-bf75-42a3ebc6a1dc", title="...", owner="...", description="...") +create_task(workstream_id="", title="...", priority="high|medium|low") +``` + +**Step 5 — Record the setup** +``` +add_progress_event( + summary="First session: structured communication into N workstreams, M tasks", + event_type="milestone", + topic_id="36c7421b-c537-4723-bf75-42a3ebc6a1dc", + detail={"workstreams": [...], "tasks_created": M} +) +``` + + diff --git a/.claude/rules/repo-boundary.md b/.claude/rules/repo-boundary.md new file mode 100644 index 0000000..87ccaaf --- /dev/null +++ b/.claude/rules/repo-boundary.md @@ -0,0 +1,8 @@ +## Repo boundary + +This repo owns **Binect-JS** only. It does not own: + + diff --git a/.claude/rules/repo-identity.md b/.claude/rules/repo-identity.md new file mode 100644 index 0000000..663b15e --- /dev/null +++ b/.claude/rules/repo-identity.md @@ -0,0 +1,5 @@ +**Purpose:** JavaScript/TypeScript wrapper (@binect/js) for the Binect REST API to send PDF documents as physical mail via Deutsche Post, plus a browser-based Explorer. Thin, transparent, zero-runtime-dependency SDK. Governance in INTENT.md / SCOPE.md. + +**Domain:** communication +**Repo slug:** binect-js +**Topic ID:** 36c7421b-c537-4723-bf75-42a3ebc6a1dc diff --git a/.claude/rules/session-protocol.md b/.claude/rules/session-protocol.md new file mode 100644 index 0000000..42dc5f2 --- /dev/null +++ b/.claude/rules/session-protocol.md @@ -0,0 +1,85 @@ +## 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("communication") +``` +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="binect-js", 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=binect-js&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 workstreams** for `communication` — title, task counts, blocking decisions +2. **Pending tasks** from `workplans/` + any `[repo:binect-js]` 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 workstreams: follow First Session Protocol (`first-session.md`). + +**During work:** `record_decision()` · `add_progress_event()` · `resolve_decision()` + +> State Hub is a *read model*. Bootstrap tools (`create_workstream`, `create_task`) +> are First Session Protocol only. Work structure belongs in repo files (ADR-001). + +**Session close:** +With MCP tools: +``` +add_progress_event(summary="...", topic_id="36c7421b-c537-4723-bf75-42a3ebc6a1dc", workstream_id="") +``` +Without MCP tools: +```bash +curl -s -X POST http://127.0.0.1:8000/progress/ \ + -H "Content-Type: application/json" \ + -d '{"topic_id":"36c7421b-c537-4723-bf75-42a3ebc6a1dc","workstream_id":"","event_type":"note","summary":"what changed","author":"codex"}' +``` +If workplan files were modified, ensure the local copy is up to date first: +```bash +git -C pull --ff-only +cd ~/state-hub && make fix-consistency REPO=binect-js +``` +For repos where implementation runs on a remote machine (e.g. CoulombCore), +use the combined target which pulls before fixing: +```bash +cd ~/state-hub && make fix-consistency-remote REPO=binect-js +``` +**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..dc53ac6 --- /dev/null +++ b/.claude/rules/stack-and-commands.md @@ -0,0 +1,19 @@ +## Stack + + +- **Language:** +- **Key deps:** + +## Dev Commands + +```bash +# TODO: Fill in the standard commands for this repo + +# Install dependencies + +# Run tests + +# Lint / type check + +# Build / package (if applicable) +``` diff --git a/.claude/rules/workplan-convention.md b/.claude/rules/workplan-convention.md new file mode 100644 index 0000000..3d36546 --- /dev/null +++ b/.claude/rules/workplan-convention.md @@ -0,0 +1,40 @@ +## Workplan Convention (ADR-001) + +File location: `workplans/BINECT-WP-NNNN-.md` +ID prefix: `BINECT-WP-` + +Work items originate as files in this repo **before** being registered in the hub. + +Canonical workplan/workstream 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-BINECT-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`, workstream 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:binect-js]` hub tasks — +visible at session start. Pick one up by creating the workplan file, then registering +the workstream. + +Task blocks use this shape: + +```task +id: BINECT-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. + + diff --git a/AGENTS.md b/AGENTS.md index 02986d5..52b0b43 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,295 +1,219 @@ -# AGENTS.md - Binect-JS Library Usage Guide +# Binect-JS — Agent Instructions -This file helps coding agents (Claude, Cursor, Copilot, etc.) efficiently integrate and use the Binect-JS library for sending physical mail via PDF. +## Repo Identity -## What This Library Does +**Purpose:** JavaScript/TypeScript wrapper (@binect/js) for the Binect REST API to send PDF documents as physical mail via Deutsche Post, plus a browser-based Explorer. Thin, transparent, zero-runtime-dependency SDK. Governance in INTENT.md / SCOPE.md. -Binect-JS is a TypeScript/JavaScript SDK for the [Binect API](https://app.binect.de) that enables sending PDF documents as physical letters via Deutsche Post. Upload a PDF, the service extracts the recipient address, prints it, and mails it. +**Domain:** communication +**Repo slug:** binect-js +**Topic ID:** `36c7421b-c537-4723-bf75-42a3ebc6a1dc` +**Workplan prefix:** `BINECT-WP-` -## Installation +--- -The library is not yet published to npm. Reference it locally: +## State Hub Integration -```json -// package.json -{ - "dependencies": { - "@binect/js": "file:../path/to/binect-js" - } -} -``` +The Custodian State Hub tracks work across all domains. Interact via HTTP REST — +there is no MCP server for Codex agents. -Or link it: -```bash -cd /path/to/binect-js && npm link -cd /your/project && npm link @binect/js -``` +| Context | URL | +|---------|-----| +| Local workstation | `http://127.0.0.1:8000` | +| Remote via tunnel | `http://127.0.0.1:18000` | -## Quick Start - -```typescript -import { BinectClient, DocumentStatus, isShippable } from '@binect/js'; -import { readFileSync } from 'fs'; - -const client = new BinectClient({ - username: 'your@email.com', - password: 'your-password' -}); - -// Upload a PDF -const pdfContent = readFileSync('letter.pdf').toString('base64'); -const doc = await client.documents.upload({ - content: pdfContent, - filename: 'letter.pdf', - color: false, - simplex: false, // false = duplex (double-sided) - envelope: 'DINLANG', - franking: 'STANDARD_FRANKING' -}); - -console.log(`Document ${doc.id} status: ${doc.status.code}`); -``` - -## Core API Methods - -### Documents (`client.documents`) - -```typescript -// Upload PDF (base64 encoded) -const doc = await client.documents.upload({ - content: base64String, - filename: 'letter.pdf', - color: false, - simplex: false, - envelope: 'DINLANG', // or 'C4' - franking: 'STANDARD_FRANKING' -}); - -// Get document by ID -const doc = await client.documents.get(documentId); - -// List shippable documents -const list = await client.documents.list({ limit: 10, offset: 0 }); - -// Delete document -await client.documents.delete(documentId); - -// Get PDF preview -const response = await client.documents.getPdf(documentId); -const pdfBlob = await response.blob(); -``` - -### Sendings (`client.sendings`) - -```typescript -// Send a document (triggers physical mailing) -const sending = await client.sendings.send(documentId); - -// Cancel a sending (only works if status is PRODUCTION_QUEUE or PRINTING) -const result = await client.sendings.cancel(documentId); - -// Send multiple documents at once -const sendings = await client.sendings.announce([docId1, docId2]); - -// Cancel multiple -const results = await client.sendings.cancelMultiple([docId1, docId2]); -``` - -### Accounts (`client.accounts`) - -```typescript -// Get account balance -const account = await client.accounts.get(); -console.log(`Balance: ${account.credit} ${account.unit}`); // e.g., "401 EUROCENT" - -// Get personal data -const personal = await client.accounts.getPersonalData(); -``` - -### Attachments (`client.attachments`) - -```typescript -// Upload reusable attachment -const attachment = await client.attachments.upload({ - content: base64PdfContent, - name: 'terms-and-conditions.pdf' -}); - -// Add attachment to document -await client.documents.addAttachment(documentId, attachment.attachmentId); -``` - -## Document Status Codes - -```typescript -import { DocumentStatus } from '@binect/js'; - -DocumentStatus.IN_PREPARATION // 1 - Being validated -DocumentStatus.SHIPPABLE // 2 - Ready to send -DocumentStatus.PRODUCTION_QUEUE // 3 - Queued for printing -DocumentStatus.PRINTING // 4 - Currently printing -DocumentStatus.SENT // 5 - Mailed -DocumentStatus.CANCELED // 6 - Canceled -DocumentStatus.ERRONEOUS // 7 - Has errors -``` - -## Helper Functions - -```typescript -import { - isShippable, - isErroneous, - isCancelable, - isTerminal, - hasErrors, - getErrors, - pollUntil, - waitForShippable, - bufferToBase64, - fileToBase64 -} from '@binect/js'; - -// Status checks -if (isShippable(doc)) { /* ready to send */ } -if (isErroneous(doc)) { /* check errors */ } -if (isCancelable(doc)) { /* can still cancel */ } -if (isTerminal(doc)) { /* final state: sent/canceled/error */ } - -// Validation -if (hasErrors(doc)) { - const errors = getErrors(doc); - errors.forEach(e => console.log(e.message)); -} - -// Base64 encoding -const base64 = bufferToBase64(fs.readFileSync('letter.pdf')); // Node.js -const base64 = await fileToBase64(fileInput.files[0]); // Browser -``` - -## Polling for Status Changes - -```typescript -import { pollUntil, isShippable, isErroneous } from '@binect/js'; - -// Poll until document is ready or has errors -const doc = await pollUntil( - () => client.documents.get(documentId), - (doc) => isShippable(doc) || isErroneous(doc), - { intervalMs: 2000, maxAttempts: 30 } -); - -// Or use the convenience helper -import { waitForShippable } from '@binect/js'; -const doc = await waitForShippable( - () => client.documents.get(documentId) -); -``` - -## Error Handling - -```typescript -import { BinectApiError, BinectAuthError } from '@binect/js'; - -try { - await client.documents.upload({ ... }); -} catch (error) { - if (error instanceof BinectAuthError) { - // 401 - Invalid credentials - console.error('Authentication failed'); - } else if (error instanceof BinectApiError) { - // Other API errors (400, 403, 404, 500, etc.) - console.error(`API Error: ${error.message}`); - console.error(`Status: ${error.status}`); - console.error(`Endpoint: ${error.endpoint}`); - // Full details - console.error(error.toDetailedString()); - } -} -``` - -## Complete Send-and-Cancel Example - -```typescript -import { BinectClient, DocumentStatus, pollUntil } from '@binect/js'; -import { readFileSync } from 'fs'; - -const client = new BinectClient({ - username: process.env.BINECT_USERNAME!, - password: process.env.BINECT_PASSWORD! -}); - -// 1. Upload -const pdfContent = readFileSync('letter.pdf').toString('base64'); -const doc = await client.documents.upload({ - content: pdfContent, - filename: 'letter.pdf', - envelope: 'DINLANG', - franking: 'STANDARD_FRANKING' -}); -const docId = String(doc.id); - -// 2. Send -await client.sendings.send(docId); - -// 3. Wait for production queue -const sentDoc = await pollUntil( - () => client.documents.get(docId), - (d) => d.status.code !== DocumentStatus.IN_PREPARATION, - { intervalMs: 1000, maxAttempts: 10 } -); - -// 4. Cancel if still possible -if (sentDoc.status.code === DocumentStatus.PRODUCTION_QUEUE || - sentDoc.status.code === DocumentStatus.PRINTING) { - await client.sendings.cancel(docId); -} - -// 5. Cleanup -await client.documents.delete(docId); -``` - -## Type Imports - -```typescript -// All types are exported -import type { - Document, - DocumentUploadOptions, - Sending, - AccountInfo, - ValidationMessage, - PriceInfo, - ListResponse, - BinectClientConfig -} from '@binect/js'; -``` - -## Key Constraints - -1. **PDF must have recipient address** in DIN 5008 format (address window position) -2. **Max file size**: 12 MB -3. **Authentication**: HTTP Basic Auth (credentials not stored/cached) -4. **No retries**: Network failures throw immediately (no automatic retry) -5. **Cancel window**: Can only cancel while status is PRODUCTION_QUEUE (3) or PRINTING (4) - -## Environment Variables Pattern - -```typescript -// Recommended pattern for credentials -const client = new BinectClient({ - username: process.env.BINECT_USERNAME!, - password: process.env.BINECT_PASSWORD! -}); -``` - -## Testing +### Orient at session start ```bash -# Run unit tests -npm test +# Offline brief — works without hub connection +cat .custodian-brief.md -# Run e2e tests (requires credentials) -BINECT_USERNAME="user@example.com" BINECT_PASSWORD="password" npm run test:e2e +# Active workstreams for this domain +curl -s "http://127.0.0.1:8000/workstreams/?topic_id=36c7421b-c537-4723-bf75-42a3ebc6a1dc&status=active" \ + | python3 -m json.tool + +# Check inbox +curl -s "http://127.0.0.1:8000/messages/?to_agent=binect-js&unread_only=true" \ + | python3 -m json.tool ``` -Note: Use double quotes for passwords containing `!` (bash history expansion). +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", + "workstream_id": "", + "task_id": "" + }' +``` + +Omit `workstream_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 workstreams (offline-safe) +2. Check inbox: `GET /messages/?to_agent=binect-js&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. Log: `POST /progress/` with a summary of what changed +3. Note for the custodian operator: after workplan file changes, run from + `~/state-hub`: + ```bash + make fix-consistency REPO=binect-js + ``` + This syncs task status from files into the hub DB. + +--- + +## 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=binect-js` is for coordination, not secret vending | +| **Claude Code** (MCP when available) | `get_domain_summary("custodian")` for workstreams; **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` + + + + +--- + +## 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/BINECT-WP-NNNN-.md` + +**Archived location:** finished workplans may move to +`workplans/archived/YYMMDD-BINECT-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: BINECT-WP-NNNN +type: workplan +title: "..." +domain: communication +repo: binect-js +status: proposed | ready | active | blocked | backlog | finished | archived +owner: codex +topic_slug: ... +created: "YYYY-MM-DD" +updated: "YYYY-MM-DD" +state_hub_workstream_id: "" # written by fix-consistency — do not edit +--- +``` + +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. + +**Task block format** (one per `##` section): + +``` +## Task Title + +` ` `task +id: BINECT-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. + +To create a new workplan: +1. Write the file following the format above +2. Notify the custodian operator to run `make fix-consistency REPO=binect-js` + (or send a message to the hub agent via `POST /messages/`) diff --git a/CLAUDE.md b/CLAUDE.md index 2363277..4bf00a9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,58 +1,18 @@ -# CLAUDE.md +# Binect-JS — Claude Code Instructions -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. +@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 -## Build Commands +## Kaizen Agents -```bash -npm install # Install dependencies -npm run build # Build TypeScript to dist/ -npm test # Run tests with vitest -npm run typecheck # Type check without emitting -``` - -## Project Overview - -Binect-JS is a JavaScript/TypeScript wrapper for the Binect REST API (https://app.binect.de/index.jsp?id=api) that enables sending PDF documents as physical mail. The project consists of two artifacts: - -1. **Binect-JS SDK** (`@binect/js`) - A thin API wrapper in `src/` -2. **Binect Explorer** - A browser-based interactive tool in `explorer/` - -## Architecture - -The SDK is organized around domain-aligned sub-clients that mirror the API vocabulary: -- `client.documents` - PDF upload, status inspection, parameter modification -- `client.attachments` - Attachment handling -- `client.sendings` - Mail dispatch triggers, cancellation -- `client.accounts` - Account management -- `client.invoices` - Invoice access - -### SDK Layer Separation - -**Core API Layer** (authoritative): 1:1 semantic mapping to REST endpoints. Methods like `documents.upload`, `documents.get`, `sendings.send`, `sendings.cancel`. - -**Convenience Layer** (optional, non-authoritative): Additive helpers in `src/helpers.ts` like status predicates (`isShippable`), error extraction, polling helpers. These must never be the only way to perform an action. - -## Design Constraints - -- **No runtime dependencies**: Uses native `fetch` API only -- **No semantic reinterpretation**: Wrapper must not alter business meaning or outcomes -- **Transparency over abstraction**: Developers must reason about actual API calls -- **No default retries**: Network behavior must be explicit and opt-in -- **Authentication**: HTTP Basic Auth, credentials are ephemeral (not stored/cached) - -## Key Files - -- `src/client.ts` - Main BinectClient class -- `src/clients/*.ts` - Domain sub-clients -- `src/types.ts` - TypeScript type definitions -- `src/errors.ts` - BinectApiError and BinectAuthError -- `src/http.ts` - Low-level HTTP client -- `src/helpers.ts` - Convenience helpers (predicates, polling) -- `explorer/index.html` - Standalone Explorer UI - -## API Integration Notes - -- Uploads use base64-encoded PDFs (max 12 MB) -- All non-success responses surface as structured `BinectApiError` -- Document status codes: 1=Preparing, 2=Shippable, 3=Queue, 4=Printing, 5=Sent, 6=Canceled, 7=Error +Specialized kaizen agent personas are available on demand via the state-hub MCP — +discover with `list_kaizen_agents()` and load with `get_kaizen_agent("")`, +then read and follow their instructions. Full guidance in `.claude/rules/agents.md`. diff --git a/SCOPE.md b/SCOPE.md index 7664bb1..adeb9ca 100644 --- a/SCOPE.md +++ b/SCOPE.md @@ -1,94 +1,124 @@ -# SCOPE.md +# SCOPE -> This document defines **what Binect-JS does and does not cover**, concretely. Where [`INTENT.md`](INTENT.md) records *why* the project exists and the principles it must uphold, this file draws the **operational boundary**: which capabilities are in scope, which are explicitly excluded, and where the edges are. A feature request is answered first by checking it against this document. +> This file helps you quickly understand what this repository is about, +> when it is relevant, and when it is not. It draws the **operational +> boundary**; [`INTENT.md`](INTENT.md) records *why* the project exists and +> the principles it must uphold. A feature request is answered first by +> checking it against this document. -## 1. Scope Statement +--- -Binect-JS covers **JavaScript/TypeScript access to the Binect REST API for sending PDF documents as physical mail**, plus a browser-based tool for exploring that API. It covers the API surface 1:1 and a small, optional convenience layer on top. It covers nothing on the server, nothing about document creation, and nothing about business process orchestration. +## One-liner -## 2. In Scope +Thin, transparent, zero-runtime-dependency JS/TS wrapper (`@binect/js`) for the Binect REST API to send PDF documents as physical mail via Deutsche Post, plus a browser-based Explorer. -### 2.1 SDK — Core API Coverage (`src/`) +--- -The SDK provides 1:1 semantic mapping to the Binect REST endpoints, organized into domain sub-clients: +## Core Idea -| Sub-client | In-scope capabilities | -|------------|----------------------| -| `client.documents` | upload (base64 PDF), get, list (shippable), listErrors, delete, getPdf / getPng preview, get/set attributes, applyTransformation (scaling/offset), addCoverPage | -| `client.sendings` | announce, list, send, cancel, getStatus (batch) | -| `client.attachments` | upload, list, get, delete, attachToDocuments | -| `client.accounts` | get (balance/credit), get/update personal data, get/update default print options, getJournal | -| `client.invoices` | list, get, getPdf | +The Binect API is comprehensive but low-level and REST-centric: developers must hand-roll auth, base64 encoding, document-lifecycle handling, and error interpretation. Binect-JS lowers that barrier with a 1:1 SDK over the API and an interactive Explorer for learning and testing — **without changing, hiding, or reinterpreting what the Binect service actually does**. See [`INTENT.md`](INTENT.md) for the full intent and inviolable principles. -### 2.2 SDK — Convenience Layer (`src/helpers.ts`, optional, additive) +--- -In scope but **never the only way** to perform an action: -- Status predicates: `isShippable`, `isErroneous`, `isSent`, `isTerminal`, `isCancelable`, `hasErrors`, `hasWarnings` -- Extractors / descriptions: `getErrors`, `getStatusDescription` -- Opt-in polling: `waitForShippable` -- Explicit pagination: `fetchAllPages` -- Encoding: `fileToBase64` +## In Scope -### 2.3 SDK — Cross-cutting +- **SDK core (`src/`)** — 1:1 semantic mapping to Binect REST endpoints via domain sub-clients: `documents` (upload, get, list, listErrors, delete, getPdf/getPng, get/setAttributes, applyTransformation, addCoverPage), `sendings` (announce, list, send, cancel, getStatus), `attachments` (upload, list, get, delete, attachToDocuments), `accounts` (get, get/update personal data, get/update options, getJournal), `invoices` (list, get, getPdf). +- **Convenience layer (`src/helpers.ts`, optional, additive)** — status predicates (`isShippable`, `isErroneous`, …), extractors (`getErrors`, `getStatusDescription`), opt-in polling (`waitForShippable`), explicit pagination (`fetchAllPages`), `fileToBase64`. Never the only way to perform an action. +- **Cross-cutting** — HTTP Basic Auth (ephemeral, per-client), native `fetch` transport (browser + Node ≥ 18), structured errors (`BinectApiError`, `BinectAuthError`), full TypeScript types/enums. +- **Explorer (`explorer/index.html`)** — backend-free browser tool: credential input, safe call execution, structured + raw response views, preview-first workflows, reusable use-case profiles. +- **Supporting material** — tests (`tests/`, incl. opt-in credential-gated e2e), ADRs (`architecture/`, `docs/adr/`), API research (`research/`), example documents (`examples/`). -- HTTP Basic Authentication (ephemeral, per-client credentials) -- Native `fetch` transport (`src/http.ts`), browser + Node.js ≥ 18 -- Structured errors: `BinectApiError`, `BinectAuthError` -- Full TypeScript types and enums (`src/types.ts`) for requests, responses, status/envelope/franking values +--- -### 2.4 Explorer (`explorer/index.html`) +## Out of Scope -- Browser-based, no backend, runs from a local file or static host -- Credential input and safe execution of API calls -- Structured and raw presentation of responses -- Preview-first workflows before dispatch -- Reusable, named "use case profiles" +- Modifying or redesigning the Binect backend API — this is an adaptation layer, not the service. +- Any server-side / backend component — the product runs without server infrastructure. +- PDF generation, editing, or layout tooling — developers bring prepared PDFs. +- Scheduling, batching, or business-process orchestration — workflow logic belongs in the consuming app. +- Methods that aggregate or reinterpret API behavior (e.g. `listAll()`), client-side filtering the API doesn't expose, default retries/timeouts/hidden network behavior — all violate 1:1 mapping and transparency (see [ADR](docs/adr/001-no-listall-method.md)). +- Credential storage, caching, telemetry, or runtime dependencies. -### 2.5 Supporting Material +--- -- Tests (`tests/`), including opt-in e2e tests gated on real credentials -- Architecture Decision Records (`architecture/`, `docs/adr/`) -- Research notes on the API (`research/`) -- Example documents (`examples/`) -- Contributor instructions (`CLAUDE.md`, `AGENTS.md`) +## Relevant When -## 3. Out of Scope +- You are building a JS/TS application that needs to send PDF documents as physical mail through Binect. +- You want to learn, explore, or test the Binect API interactively without writing integration code first (use the Explorer). +- You need a transparent, auditable wrapper where every method maps to a known REST call. +- You are consuming Binect from a sibling project (e.g. [`binect-chrome`](../binect-chrome)) and want the SDK as a shared dependency. -These are excluded by design. A request that requires any of them is a request for a *different* product. +--- -| Excluded | Reason | -|----------|--------| -| Modifying or redesigning the Binect backend API | Binect-JS is an adaptation layer, not the service. | -| Server-side components / hosted backend | The product must operate without server infrastructure ([INTENT §4](INTENT.md)). | -| PDF generation, editing, or layout tooling | Developers bring PDFs already prepared for sending. | -| Scheduling, batching, or business-process orchestration | Workflow logic belongs in the consuming application. | -| Methods that aggregate or reinterpret API behavior (e.g. `listAll()`) | Violates 1:1 mapping and transparency (see [ADR](docs/adr/001-no-listall-method.md)). | -| Client-side filtering semantics the API doesn't expose | Would invent behavior with no API backing. | -| Default retries, timeouts, or hidden network behavior | All network behavior must be explicit and opt-in. | -| Credential storage, caching, or telemetry | Credentials are ephemeral; no hidden data transmission. | -| Runtime dependencies | Native `fetch` only. | -| Commitment to a specific UI framework | Explorer stays framework-agnostic. | +## Not Relevant When -## 4. Boundary Cases & Known Edges +- You need to *create*, render, or transform the PDF itself — that is the source application's job. +- You want a heavily abstracted, "do-it-all" mail orchestration framework with retries, scheduling, and batching baked in. +- You are integrating a non-Binect mail/postal provider. +- You need server-side queueing or persistence — this library is stateless and backend-free by design. -- **Document statuses without list endpoints.** The API only lists shippable (status 2) and erroneous (status 7) documents. Statuses 1, 3, 4, 5, 6 (IN_PREPARATION, PRODUCTION_QUEUE, PRINTING, SENT, CANCELED) have no list endpoint — so the SDK cannot and will not offer "list all documents." Retrieval of those statuses is only possible per-document via `get`. -- **Combining shippable + erroneous lists.** Supported, but only via explicit composition (`fetchAllPages` over each endpoint in parallel), never via a single hidden method. -- **CORS / direct browser access.** Assumed permitted by Binect. If Binect restricts browser-origin requests, that is an upstream constraint, not something the Explorer works around with a proxy (a proxy would be a server-side component — out of scope). -- **Large uploads.** PDFs are base64-encoded with a 12 MB ceiling per the API; the SDK surfaces the API's response rather than chunking or pre-validating size beyond what the API enforces. +--- -## 5. Scope Change Process +## Current State -In scope is defined by what the Binect API exposes plus the thin convenience layer. To change scope: +- Status: active +- Implementation: substantial — full SDK surface, convenience helpers, Explorer, and test suite in place +- Stability: evolving — `@binect/js` is at `0.1.0`, not yet published to a registry (consumed via `file:` / `npm link`) +- Usage: internal — used as a local dependency by sibling Binect tooling -1. Confirm the change does not violate any principle in [`INTENT.md §4`](INTENT.md). -2. If it adds API coverage that Binect actually exposes 1:1 → in scope, add it to the relevant sub-client. -3. If it aggregates, reinterprets, or invents behavior → out of scope; document the decision as an ADR (as was done for `listAll()`). -4. Update this file and `INTENT.md` together when the boundary genuinely moves. +--- -## 6. Related Documents +## How It Fits -- [`INTENT.md`](INTENT.md) — why the project exists and its inviolable principles -- `ProductRequirementsDocument.md` — full PRD (§4 Scope, §4.2 Non-Scope) -- `architecture/ADR-00{1,2,3}-*.md` — SDK, no-dependencies, Explorer architecture -- `docs/adr/001-no-listall-method.md` — a scope boundary enforced against a feature request -- `README.md` — usage and full API reference +- Upstream dependencies: the Binect REST API (third-party service; see `tpsc.yaml`). No runtime npm dependencies. +- Downstream consumers: [`binect-chrome`](../binect-chrome) (Chrome extension), and any JS/TS project sending physical mail via Binect. +- Often used with: prepared PDF sources; the Explorer for onboarding and demos. + +--- + +## Terminology + +- Preferred terms: **document** (an uploaded PDF), **sending** (a dispatch action), **shippable** (status 2, ready to send), **erroneous** (status 7). +- Also known as: "letter" / "mail" / "Brief" for a sent document. +- Potentially confusing terms: *uploaded ≠ sent* — uploading makes a document shippable; dispatch is a separate explicit `send` step. "list" returns only shippable documents, not all documents (no API endpoint lists all statuses). + +--- + +## Related / Overlapping + +- `binect-chrome` — Chrome extension that detects PDFs and sends them to Binect; consumes this SDK rather than reimplementing API logic. +- `email-connect` — adjacent communication-domain delivery channel (email vs. physical mail); no code overlap. + +--- + +## Getting Oriented + +- Start with: [`INTENT.md`](INTENT.md), then [`README.md`](README.md). +- Key files / directories: `src/client.ts` (main client), `src/clients/*` (sub-clients), `src/types.ts`, `src/errors.ts`, `src/http.ts`, `src/helpers.ts`, `explorer/index.html`. +- Entry points: `BinectClient` (from `src/index.ts`); the Explorer (`explorer/index.html`). + +--- + +## Provided Capabilities + +```capability +type: api +title: Binect physical-mail SDK (@binect/js) +description: Transparent JS/TS wrapper over the Binect REST API to upload PDFs, manage document lifecycle, and dispatch physical mail via Deutsche Post. Zero runtime dependencies; browser and Node ≥ 18. +keywords: [binect, physical-mail, pdf, deutsche-post, sdk, typescript, fetch] +``` + +```capability +type: documentation +title: Binect API Explorer +description: Backend-free browser tool for learning and testing the Binect API interactively, with preview-first workflows and reusable use-case profiles. +keywords: [explorer, binect, api-testing, onboarding, browser] +``` + +--- + +## Notes + +- Uploads are base64-encoded PDFs with a 12 MB ceiling enforced by the API. +- Document status codes: 1=IN_PREPARATION, 2=SHIPPABLE, 3=PRODUCTION_QUEUE, 4=PRINTING, 5=SENT, 6=CANCELED, 7=ERRONEOUS. +- Scope-change process: confirm against [`INTENT.md` §4](INTENT.md); add genuine 1:1 API coverage to the relevant sub-client; route aggregating/reinterpreting requests to an ADR instead (as was done for `listAll()`). diff --git a/tpsc.yaml b/tpsc.yaml new file mode 100644 index 0000000..d81cb11 --- /dev/null +++ b/tpsc.yaml @@ -0,0 +1,9 @@ +# tpsc.yaml — Third-Party Services Catalog declarations for binect-js +# Each entry references a service slug from the central catalog at: +# the-custodian/canon/tpsc/.yaml +# Ingest: cd state-hub && make ingest-tpsc REPO=binect-js + +services: + - slug: binect-api + purpose: Binect REST API — upload PDFs, manage document lifecycle, and dispatch physical mail via Deutsche Post (the sole external service this SDK wraps). + auth: basic_auth diff --git a/workplans/BINECT-WP-0001-statehub-bootstrap.md b/workplans/BINECT-WP-0001-statehub-bootstrap.md new file mode 100644 index 0000000..71820de --- /dev/null +++ b/workplans/BINECT-WP-0001-statehub-bootstrap.md @@ -0,0 +1,58 @@ +--- +id: BINECT-WP-0001 +type: workplan +title: "Bootstrap State Hub integration" +domain: communication +repo: binect-js +status: ready +owner: codex +topic_slug: communication +created: "2026-06-22" +updated: "2026-06-22" +state_hub_workstream_id: "66b7a60a-8279-4c33-ac6c-b7c8f42cb18b" +--- + +# Bootstrap State Hub integration + +JavaScript/TypeScript wrapper for the Binect REST API. + +## Review Generated Integration Files + +```task +id: BINECT-WP-0001-T01 +status: todo +priority: high +state_hub_task_id: "502743ee-7774-4a06-8287-b8a68527e76f" +``` + +Review `INTENT.md`, `SCOPE.md`, `AGENTS.md`, and `.custodian-brief.md`. +Replace generated placeholders with repo-specific facts where needed. + +## Verify Local Developer Workflow + +```task +id: BINECT-WP-0001-T02 +status: todo +priority: high +state_hub_task_id: "a98ca0fe-3c7c-4886-a530-62a68ca32c7e" +``` + +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. + +## Seed First Real Workplan + +```task +id: BINECT-WP-0001-T03 +status: todo +priority: medium +state_hub_task_id: "9a099aee-b374-4af4-af3b-52a2f9b1b649" +``` + +Create the first implementation workplan for the repository's most important +next change. After workplan file updates, run from `~/state-hub`: + +```bash +make fix-consistency REPO=binect-js +```